3개의 실수값을 전달하고 이 값에 2를 곱한 결과를 GPU를 통해 병렬로 처리 실행해 그 결과를 얻는 코드입니다. 먼저 GPU에 대한 객체를 얻는 것은 비동기로 처리해야 하므로 다음과 같은 코드가 필요합니다.
async function main() { // 여기에 이후 모든 코드가 작성됩니다. } await main();
자, 이제 GPU에 대한 객체를 얻습니다.
const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice();
실행할 쉐이더 코드를 작성합니다. 쉐이더 코드는 WGSL로 작성됩니다.
const module = device.createShaderModule({ label: 'doubling compute module', code: /* wgsl */ ` @group(0) @binding(0) var<storage, read_write> data: array<f32>; @compute @workgroup_size(1) fn computeSomething( @builtin(global_invocation_id) id: vec3u ) { let i = id.x; data[i] = data[i] * 2.0; } `, });
위의 쉐이더 코드를 실행할 파이프라인 객체를 생성합니다.
const pipeline = device.createComputePipeline({ label: 'x2 compute pipeline', layout: 'auto', compute: { module, entryPoint: 'computeSomething', // 실행할 쉐이더 함수 }, });
실행할 쉐이더 함수를 보면 1개의 인자를 받는데, 그 인자를 정의합니다.
const input = new Float32Array([1, 3, 5]);
위의 인자는 CPU 메모리에 있으므로 이를 GPU 메모리에 만들어서 복사해 줘야 합니다.
const workBuffer = device.createBuffer({ label: 'work buffer', size: input.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, }); device.queue.writeBuffer(workBuffer, 0, input);
GPU를 통해 계산된 최종 결과를 읽기 위한 사본 버퍼를 생성합니다.
const resultBuffer = device.createBuffer({ label: 'result buffer', size: input.byteLength, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
계산을 위해 사용될 인자에 대한 버퍼가 무엇인지를 명확히 지정합니다.
const bindGroup = device.createBindGroup({ label: 'bindGroup for work buffer', layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: { buffer: workBuffer } }, ], });
GPU에서 실행할 명령을 인코딩합니다.
const encoder = device.createCommandEncoder({ label: 'x2 encoder' }); const pass = encoder.beginComputePass({ label: 'x2 compute pass' }); pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.dispatchWorkgroups(input.length); pass.end();
계산 결과를 읽어 내기 위해 맵핑용 버퍼에 복사하는 명령을 인코딩합니다.
encoder.copyBufferToBuffer(workBuffer, 0, resultBuffer, 0, resultBuffer.size);
실해 명령의 실행은 다음 코드를 통해 이루어집니다.
const commandBuffer = encoder.finish(); device.queue.submit([commandBuffer]);
실행 결과를 콘솔에 출력하기 위한 코드입니다.
await resultBuffer.mapAsync(GPUMapMode.READ); const result = new Float32Array(resultBuffer.getMappedRange()); console.log('input', input); console.log('result', result); resultBuffer.unmap();
실행 결과를 보면 다음과 같습니다.
실행 결과를 보면 입력값에 2배만큼 곱해졌는데, 이는 GPU를 통해 병렬로 계산된 것입니다.