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를 통해 병렬로 계산된 것입니다.