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

275 lines
7.5 KiB
Markdown

---
id: frontend-webgpu-patterns
title: WebGPU — Browser GPU 컴퓨팅 / 그래픽
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [frontend, webgpu, gpu, vibe-coding]
tech_stack: { language: "TS / WGSL", applicable_to: ["Frontend"] }
applied_in: []
aliases: [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
```ts
if (!navigator.gpu) throw new Error('WebGPU not supported');
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter!.requestDevice();
```
### 캔버스 setup
```ts
const canvas = document.querySelector('canvas')!;
const ctx = canvas.getContext('webgpu')!;
const format = navigator.gpu.getPreferredCanvasFormat();
ctx.configure({ device, format, alphaMode: 'premultiplied' });
```
### 간단 render — 색칠된 삼각형
```ts
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)
```ts
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
```ts
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)
```ts
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
```wgsl
@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
```ts
// 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
```ts
import { Engine, WebGPUEngine } from '@babylonjs/core';
const engine = new WebGPUEngine(canvas);
await engine.initAsync();
```
### Fallback (WebGL / WASM)
```ts
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.
## 🔗 관련 문서
- [[Frontend_Three_R3F]]
- [[Frontend_WASM_Integration]]
- [[AI_Local_LLM_Inference]]