[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
---
|
||||
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]]
|
||||
Reference in New Issue
Block a user