Files
2nd/10_Wiki/Topics/Coding/Frontend_WebGPU_Patterns.md
T
2026-05-09 21:08:02 +09:00

7.5 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
frontend-webgpu-patterns WebGPU — Browser GPU 컴퓨팅 / 그래픽 Coding draft B conceptual 2026-05-09 2026-05-09
frontend
webgpu
gpu
vibe-coding
language applicable_to
TS / WGSL
Frontend
WebGPU
WGSL
compute shader
render pipeline
GPU buffer
browser GPU

WebGPU

WebGL 후속. Modern GPU API + compute shader. 그래픽 + GPU compute (ML inference, simulation). 2024+ Chrome / Edge / Safari / Firefox 지원.

📖 핵심 개념

  • Adapter / Device: GPU handle.
  • Shader: WGSL (WebGPU Shading Language).
  • Pipeline: render or compute.
  • Buffer: GPU 메모리.

💻 코드 패턴

Setup

if (!navigator.gpu) throw new Error('WebGPU not supported');

const adapter = await navigator.gpu.requestAdapter();
const device = await adapter!.requestDevice();

캔버스 setup

const canvas = document.querySelector('canvas')!;
const ctx = canvas.getContext('webgpu')!;
const format = navigator.gpu.getPreferredCanvasFormat();

ctx.configure({ device, format, alphaMode: 'premultiplied' });

간단 render — 색칠된 삼각형

const shader = device.createShaderModule({
  code: `
    @vertex
    fn vs(@builtin(vertex_index) i: u32) -> @builtin(position) vec4f {
      var p = array<vec2f, 3>(
        vec2f(0.0, 0.5),
        vec2f(-0.5, -0.5),
        vec2f(0.5, -0.5)
      );
      return vec4f(p[i], 0.0, 1.0);
    }

    @fragment
    fn fs() -> @location(0) vec4f {
      return vec4f(1.0, 0.5, 0.0, 1.0);
    }
  `,
});

const pipeline = device.createRenderPipeline({
  layout: 'auto',
  vertex: { module: shader, entryPoint: 'vs' },
  fragment: { module: shader, entryPoint: 'fs', targets: [{ format }] },
});

function render() {
  const encoder = device.createCommandEncoder();
  const pass = encoder.beginRenderPass({
    colorAttachments: [{
      view: ctx.getCurrentTexture().createView(),
      clearValue: [0, 0, 0, 1],
      loadOp: 'clear',
      storeOp: 'store',
    }],
  });
  pass.setPipeline(pipeline);
  pass.draw(3);
  pass.end();
  device.queue.submit([encoder.finish()]);
}

render();

Compute shader (matrix multiply)

const shader = device.createShaderModule({
  code: `
    @group(0) @binding(0) var<storage, read> a: array<f32>;
    @group(0) @binding(1) var<storage, read> b: array<f32>;
    @group(0) @binding(2) var<storage, read_write> result: array<f32>;
    
    @compute @workgroup_size(64)
    fn main(@builtin(global_invocation_id) id: vec3u) {
      let i = id.x;
      result[i] = a[i] * b[i] + 1.0;
    }
  `,
});

const pipeline = device.createComputePipeline({
  layout: 'auto',
  compute: { module: shader, entryPoint: 'main' },
});

const N = 1024 * 1024;
const aArr = new Float32Array(N).fill(2);
const bArr = new Float32Array(N).fill(3);

const aBuf = device.createBuffer({ size: aArr.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
const bBuf = device.createBuffer({ size: bArr.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST });
const resultBuf = device.createBuffer({ size: N * 4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC });

device.queue.writeBuffer(aBuf, 0, aArr);
device.queue.writeBuffer(bBuf, 0, bArr);

const bindGroup = device.createBindGroup({
  layout: pipeline.getBindGroupLayout(0),
  entries: [
    { binding: 0, resource: { buffer: aBuf } },
    { binding: 1, resource: { buffer: bBuf } },
    { binding: 2, resource: { buffer: resultBuf } },
  ],
});

const encoder = device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(Math.ceil(N / 64));
pass.end();

// CPU 로 read back
const readBuf = device.createBuffer({ size: N * 4, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
encoder.copyBufferToBuffer(resultBuf, 0, readBuf, 0, N * 4);
device.queue.submit([encoder.finish()]);

await readBuf.mapAsync(GPUMapMode.READ);
const result = new Float32Array(readBuf.getMappedRange().slice(0));
readBuf.unmap();
console.log(result[0]);  // 7

Texture / image

const img = await createImageBitmap(await fetch('/image.jpg').then(r => r.blob()));

const tex = device.createTexture({
  size: [img.width, img.height],
  format: 'rgba8unorm',
  usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,
});

device.queue.copyExternalImageToTexture({ source: img }, { texture: tex }, [img.width, img.height]);

ML — Transformers.js (WebGPU backend)

import { pipeline, env } from '@xenova/transformers';

env.backends.onnx.preferredBackend = 'webgpu';

const pipe = await pipeline('text-generation', 'Xenova/Llama-3.2-1B-Instruct');
const out = await pipe('Hello', { max_new_tokens: 50 });

→ Browser 안 LLM 빠름 (WebGPU 가 WASM 보다 5-10x).

Compute — image filter

@group(0) @binding(0) var inputTex: texture_2d<f32>;
@group(0) @binding(1) var outputTex: texture_storage_2d<rgba8unorm, write>;

@compute @workgroup_size(8, 8)
fn blur(@builtin(global_invocation_id) id: vec3u) {
    let coord = vec2i(id.xy);
    var color = vec4f(0.0);
    for (var dx = -2; dx <= 2; dx++) {
      for (var dy = -2; dy <= 2; dy++) {
        color += textureLoad(inputTex, coord + vec2i(dx, dy), 0);
      }
    }
    color /= 25.0;
    textureStore(outputTex, coord, color);
}

3D rendering

// Three.js + WebGPU renderer
import { WebGPURenderer } from 'three/examples/jsm/renderers/webgpu/WebGPURenderer.js';

const renderer = new WebGPURenderer({ canvas });
await renderer.init();
// 일반 Three.js scene

Babylon.js

import { Engine, WebGPUEngine } from '@babylonjs/core';

const engine = new WebGPUEngine(canvas);
await engine.initAsync();

Fallback (WebGL / WASM)

async function getRenderer() {
  if (navigator.gpu) {
    try {
      const adapter = await navigator.gpu.requestAdapter();
      if (adapter) return 'webgpu';
    } catch {}
  }
  if ('WebGL2RenderingContext' in window) return 'webgl2';
  return 'cpu';
}

Performance tips

  • Workgroup size = multiple of 32 (보통 64).
  • Buffer alignment 16 byte.
  • 큰 buffer 보다 small batch.
  • Texture > raw buffer for spatial.
  • writeBuffer 보다 staging buffer + queue.

디버그

Chrome: chrome://gpu, Performance tab, GPU profiler.
Spector.js — WebGL/WebGPU frame capture.

Limitations (2026)

+ Major browsers 지원 (Chrome 113+, Safari 17.4+, Firefox 141+).
- Mobile: 일부 device 만.
- Compute shader = 안전 (서버 모델 download 가능).
- Atomic operations 일부 한정.

🤔 의사결정 기준

상황 추천
3D 새 프로젝트 WebGPU (Three.js / Babylon)
기존 WebGL 점진 마이그레이션
ML inference Transformers.js + WebGPU
GPU compute WebGPU compute shader
옛 browser 지원 WebGL fallback
Game Unity WebGPU / 자체

안티패턴

  • No fallback: 옛 browser fail.
  • 모든 frame writeBuffer 큰 data: bandwidth. staging.
  • CPU read back 매 frame: 느림. GPU keep.
  • 너무 작은 dispatch: launch overhead.
  • Memory leak (destroy 안 함): GPU resources.
  • Float64 가정: WebGPU = float32.
  • Adapter / device 매번 request: cache.

🤖 LLM 활용 힌트

  • Compute shader = ML / image / simulation 강력.
  • Three.js / Babylon WebGPU renderer 가 가장 단순 시작.
  • Transformers.js + WebGPU = browser LLM.

🔗 관련 문서