規劃有點改變,接下來幾篇會改成講"Compute Shader on Website"。
今天要把兩件常被搞混的東西講清楚:
Storage Buffer(SBO) 是「給 GPU 讀寫的大倉庫」;Compute Shader 是「不畫圖、只算數」的 GPU 程式。
我們先用簡單的比喻建立直覺,再動手做一個範例:把陣列丟進 GPU,用 compute shader 算完再把結果拿回來。
Buffer 是什麼?
一條在 GPU 的連續記憶體。你可以把它想成「一長條資料夾」:
VERTEX
/INDEX
:專門給繪圖管線的頂點/索引資料。UNIFORM
:小小一張設定表(只讀、容量有限,常見限制 ~64KB)。STORAGE
(SBO):大容量、可以 讀/寫 的資料夾,放任意結構化資料最方便。為什麼要 SBO?
當資料很大(上 MB)、或你要在 GPU 寫回結果(像模擬、排序、粒子、貼圖運算),就需要 Storage Buffer。Uniform 無法寫、容量也不夠。
Compute Shader 是什麼?
一段在 GPU 上平行運算的程式,不經過光柵化、材質這些「畫圖」流程。
你告訴 GPU:「開 多少個工人(workgroups),每個工人處理哪一段資料」,計算結果通常寫回 SBO 或 Storage Texture。
特性 | Uniform Buffer | Storage Buffer(SBO) |
---|---|---|
讀寫 | 只讀 | 可讀可寫(或宣告為只讀以利最佳化) |
容量 | 小(常見 ~64KB/綁定) | 大(MB 級),受硬體 limits 約束 |
用途 | 常數參數、矩陣 | 大陣列/結構體、結果回寫、粒子、排序 |
WGSL 宣告 | @group/binding var<uniform> |
var<storage, read> / var<storage, read_write> |
新手口訣:要寫回、或資料很大 → Storage Buffer;只是一些小參數 → Uniform。
WebGPU/WGSL 在 Storage Buffer 使用類似 std430 的對齊規則:
f32/u32/i32
對齊 4 bytes。vec2<f32>
對齊 8;vec3/vec4<f32>
對齊 16(vec3
也看齊 16)。struct
的對齊是成員裡 最大對齊;大小會 補齊到該對齊倍數。陣列的步幅(arrayStride
)通常會是元素對齊的倍數(要小心 vec3
)。
最簡做法:
vec4
、或加註 @align(16)
,就不會被 vec3
的 16 對齊坑到。vec4
取代 vec3
。@workgroup_size(64)
,表示「每隊 64 人」。dispatchWorkgroups(G)
就會啟動 G 隊 × 64 人。global_invocation_id
(像員工編號),常用它來當陣列索引。if (i < ...)
)。延伸:下一篇會講 workgroup 之間怎麼協作(
workgroup
記憶體、workgroupBarrier()
)、原子操作、以及把 compute 算的資料拿去 render pass 畫圖。
很多人以為 Storage Buffer 只給 compute;其實你在 Vertex/Fragment Shader 也能讀(甚至寫)。
最常見用途:大量實例(Instancing)的變換矩陣。例如依 @builtin(instance_index)
在 VS 讀第 i
個矩陣:
WGSL(片段)
struct Mat { m: mat4x4<f32>; };
struct Mats { data: array<Mat>; };
@group(0) @binding(0) var<storage, read> instMats: Mats;
@vertex
fn vs_main(@location(0) pos: vec3f,
@builtin(instance_index) iid: u32) -> @builtin(position) vec4f {
let M = instMats.data[iid].m;
return M * vec4f(pos, 1.0);
}
JS 端把一整串矩陣塞進 Storage Buffer,就能一次畫出上千個分身,比把資料塞進很多小 Uniform 更彈性。
depthStencil
就必須在 render pass 提供 depthStencilAttachment
;不需要就兩邊都拿掉。bufC
copy 到 MAP_READ
的 staging buffer;或 await mapAsync
之前就讀。global_invocation_id.x
超過陣列長度。記得 if (i < N)
。@group/@binding
要和 JS 的 getBindGroupLayout(0)
/ entries 對得上。vec3
;改用 vec4
或加 @align(16)
。Storage Buffer = GPU 的大倉庫(可讀可寫,放大量結構化資料);
Compute Shader = 叫很多小工平行算(不畫圖也能用 GPU)。
把兩者串起來,你就能做各種高效處理:物理模擬、影像濾鏡、排序、幾何產生…
今天先用向量加法暖身;下一篇我們會更深入 Compute Shader 的工作群組、同步、共享記憶體與效能小撇步。