mcpField
T
typegpu

typegpu

github.com/software-mansion-labs开发2971.6k 次安装2 周前更新
代码仓库

关于 typegpu

typegpu 是面向 TypeGPU 0.12 的 GPU 编程技能,帮助开发者使用类型化模式、TypeScript 着色器、缓冲区和管线构建 GPU 程序。

主要功能

  1. 01GPU 与 CPU 共用类型模式
  2. 02将 TypeScript 着色器转为 WGSL
  3. 03类型化缓冲区与资源管理
  4. 04计算和渲染管线指导

适用场景

  1. 01编写 TypeGPU 着色器
  2. 02管理 CPU 与 GPU 数据布局
  3. 03构建计算或渲染管线

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/software-mansion-labs/skills --skill typegpu

安装后在 Claude Code 中输入/typegpu确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
advanced.md
encoders.md
matrices.md
noise.md
pipelines.md
react.md
sdf.md
setup.md
shaders.md
std.md
textures.md
timing.md
types.md

详细介绍

TypeGPU

单个架构(d.*)可同时定义 GPU 类型、CPU 缓冲区布局和 TypeScript 类型,无需手动对齐、类型映射或类型转换。构建插件 unplugin-typegpu 会转换带有 'use gpu' 标记的 TypeScript,以便在运行时转译为 WGSL,从而支持跨越 CPU/GPU 边界的类型推断和多态。

本技能适用于 TypeGPU 0.12。如果用户的项目使用较旧版本,请先确认 API 是否可用,再依赖此处的示例或推荐模式。


何时阅读参考文件

编写几乎任何着色器或 GPU 函数前都应阅读——以下两个文件涵盖了最容易出错的规则:

  • references/types.md — 抽象类型解析、何时必须使用 d.f32()、何时可省略、向量构造函数重载、tgpu.fn 签名中的采样器/纹理架构、CPU 端的 TgpuBuffer/TgpuTexture TypeScript 类型。跳过此文件会导致类型错误。
  • references/shaders.md — 循环(std.range、tgpu.unroll)、三元运算符/逻辑运算符语义、tgpu.comptime、外层作用域捕获规则、三个着色器阶段的完整内置函数参考、console.log。处理任何非简单着色器逻辑时都应阅读。
  • references/std.md — 完整的 std 函数列表(数学、比较/布尔向量、矩阵构造器、纹理、原子操作、打包、子组、环境探测)。手动编写任何数学/实用函数前请查阅。

任务明确涉及以下内容时阅读:

  • references/pipelines.md — 顶点缓冲区/布局、attribs 接线、MRT、全屏三角形、深度/模板、混合模式、fragDepth 输出、加载 3D 模型(@loaders.gl)、解析 API
  • references/matrices.md — wgpu-matrix 集成、列主序布局、摄像机 uniform、common.writeSoA、快速 CPU 写入路径。任何 3D 工作都应阅读(视图/投影矩阵、动画变换、模型加载)
  • references/textures.md — 纹理创建、视图、采样器、存储纹理、mipmap、多重采样
  • references/noise.md — @typegpu/noise(随机数、分布、Perlin 2D/3D)
  • references/sdf.md — @typegpu/sdf(2D/3D 基本图元、运算符、光线步进、抗锯齿遮罩)
  • references/encoders.md — 类型化命令编码器、多管线渲染/计算通道、渲染捆绑、批量提交、原始 WebGPU 编码器互操作(API 不稳定,行为稳定)
  • references/timing.md — 通过时间戳查询进行 GPU 计时:withPerformanceCallback 与共享查询集、available 守卫,以及各通道计时为何会重叠
  • references/react.md — @typegpu/react 钩子(useRoot、useFrame、useUniform 等)、React Native worklet 渲染循环
  • references/setup.md — TypeGPU CLI、安装、unplugin-typegpu 构建插件、tsover 运算符重载、故障排查
  • references/advanced.md — 缓冲区重新解释、间接绘制/分派、ArrayBuffer IO、缩小、静默警告、root.unwrap

设置

import { tgpu, d, std, common } from 'typegpu';

const root = await tgpu.init();                 // request a GPU device
const root = tgpu.initFromDevice({ device });   // or wrap an existing GPUDevice

const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });

在应用启动时创建一个根。不同根中的资源无法交互。清理:root.destroy() 会销毁通过该根创建的所有资源;如果该根来自 tgpu.init(而非 initFromDevice),还会销毁设备本身。


数据架构(d.*)

架构定义内存布局并推断 TypeScript 类型;同一架构用于缓冲区、着色器签名和绑定组条目。

标量
d.f32    d.i32    d.u32    d.f16   // f16 needs the 'shader-f16' device feature (references/setup.md)
// d.bool is NOT host-shareable - use d.u32 in buffers
向量和矩阵
d.vec2f  d.vec3f  d.vec4f     // f32
d.vec2i  d.vec3i  d.vec4i     // i32
d.vec2u  d.vec3u  d.vec4u     // u32
d.vec2h  d.vec3h  d.vec4h     // f16
d.vec2b  d.vec3b  d.vec4b     // bool - shader-side only (not host-shareable)

d.mat2x2f   d.mat3x3f   d.mat4x4f

实例类型:d.vec3f() -> d.v3f,d.mat4x4f() -> d.m4x4f。

向量构造函数支持丰富的重载——它们可以由标量、较小向量和 swizzle 的任意组合构成,只要分量总数正确(d.vec4f(rgb, 1)、d.vec3f(v.xy, newZ))。优先使用这些构造函数,而不是手动拆分分量;完整的重载列表见 references/types.md。

复合类型
const Particle = d.struct({
  position: d.vec2f,
  velocity: d.vec2f,
  color:    d.vec4f,
});

const ParticleArray = d.arrayOf(Particle, 1000); // fixed-size

运行时大小的模式。 d.arrayOf(Element) 不带数量时,会返回一个 函数 (n: number) => WgslArray<Element>。这种双重性质是关键:将函数本身(未指定大小)传递给绑定组布局,将其与数量一起调用(指定大小)以创建缓冲区。

// Plain array - arrayOf without count is already a factory:
const layout = tgpu.bindGroupLayout({
  data: { storage: d.arrayOf(d.f32), access: 'mutable' },  // unsized for layout
});
const buf = root.createBuffer(d.arrayOf(d.f32, 1024)).$usage('storage'); // sized for buffer

// Struct with a runtime-sized last field - wrap in a factory function:
const RuntimeStruct = (n: number) =>
  d.struct({
    counter: d.atomic(d.u32),
    items:   d.arrayOf(d.f32, n),  // last field gets the runtime size
  });

const layout2 = tgpu.bindGroupLayout({
  runtimeData: { storage: RuntimeStruct, access: 'mutable' }, // unsized (the function)
});
const buf2 = root.createBuffer(RuntimeStruct(1024)).$usage('storage'); // sized (called)

不能将未指定大小的模式直接传递给 createBuffer——大小必须在 CPU 上确定。


GPU 函数

TypeGPU 会将标记为 'use gpu' 的 TypeScript 编译为 WGSL。

普通回调(多态)

无需显式签名;最适合辅助数学函数和灵活的实用工具。

const rotate = (v: d.v2f, angle: number) => {
  'use gpu';
  const c = std.cos(angle);
  const s = std.sin(angle);
  return d.vec2f(c * v.x - s * v.y, s * v.x + c * v.y);
};

number 参数和 d.v2f | d.v3f 这样的联合类型是多态的——TypeGPU 会针对每个唯一调用点类型组合生成一个 WGSL 重载。从外部作用域捕获的值会内联为 WGSL 字面量;对于任何运行时会变化的内容,请使用缓冲区/uniform。

tgpu.fn(显式类型)

固定的 WGSL 签名。适用于库代码,或需要固定 WGSL 接口的场景。

const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)((v, angle) => {
  'use gpu';
  // ...
});
着色器入口点
// Compute
const myCompute = tgpu.computeFn({
  workgroupSize: [64],
  in: { gid: d.builtin.globalInvocationId },
})((input) => { 'use gpu'; /* input.gid: d.v3u */ });

// Vertex
const myVertex = tgpu.vertexFn({
  in:  { position: d.vec3f, uv: d.vec2f },
  out: { position: d.builtin.position, fragUv: d.vec2f },
})((input) => {
  'use gpu';
  return { position: d.vec4f(input.position, 1), fragUv: input.uv };
});

// Fragment
const myFragment = tgpu.fragmentFn({
  in: { fragUv: d.vec2f },
  out: d.vec4f,
})((input) => { 'use gpu'; return d.vec4f(input.fragUv, 0, 1); });

顶点 in 可以包含内置项:d.builtin.vertexIndex、d.builtin.instanceIndex。

完整着色器语法、分支裁剪、std 库、类型推断和惯用模式(向量运算、结构体构造函数、寄存器压力):请参阅 references/shaders.md。进行任何非平凡的着色器开发前都应阅读该文档——值与引用的处理方式在其中说明,也是最常见的 ResolutionError 来源。


缓冲区

创建
// Schema only:
const buf = root.createBuffer(d.arrayOf(Particle, 1000)).$usage('storage');

// With typed initial value (only when non-zero — all buffers are zero-initialized by default):
const uBuf = root.createBuffer(Config, { time: 1, scale: 2.0 }).$usage('uniform');

// With an initializer callback - buffer is still mapped (cheapest CPU path):
const buf = root.createBuffer(Schema, (mappedBuffer) => {
  mappedBuffer.write([10, 20], { startOffset: firstChunk.offset });
  mappedBuffer.write([30, 40], { startOffset: secondChunk.offset });
});

// Wrap an existing GPUBuffer (you own its lifecycle and flags):
const buf = root.createBuffer(d.u32, existingGPUBuffer);
buf.write(12);
用途标志
字面量着色器访问方式
'uniform'var<uniform>
'storage'var<storage, read>(或使用 access: 'mutable' 时为 read_write)
'vertex'顶点输入,与 tgpu.vertexLayout 配合使用
'index'索引缓冲区(仅限 d.u16 或 d.u32 数组)
'indirect'间接调度/绘制

所有缓冲区都会自动添加 COPY_SRC | COPY_DST。$addFlags(GPUBufferUsage.X) 可添加 $usage 未涵盖的标志。

写入

.write(value) 会处理对齐。有四种输入形式(从慢到快):

形式示例(vec3f)说明
类型化实例d.vec3f(1, 2, 3)会分配一个包装对象——适合初始化/原型开发
普通 JS 数组/元组[1, 2, 3]不会分配内存,会自动添加填充
TypedArraynew Float32Array([1, 2, 3])按原样复制字节——必须包含 WGSL 填充
ArrayBufferrawBytes吞吐量最高,按原样复制字节

在初始化时缓存普通数组或 Float32Array 并重复使用。关于填充规则(vec3f = 16 字节、mat3x3f 的列填充)和完整的快速路径指南,请参阅 references/matrices.md。

切片写入——使用 d.memoryLayoutOf 获取字节偏移量,以更新子区域:

const layout = d.memoryLayoutOf(schema, (a) => a[3]);
buffer.write([4, 5, 6], { startOffset: layout.offset });

.patch(data)——更新指定的结构体字段或数组索引,而不影响其余内容:

planetBuffer.patch({
  mass: 123.1,
  colors: { 2: [1, 0, 0], 4: d.vec3f(0, 0, 1) },
});

common.writeSoA(buffer, { field: Float32Array, ... })——将分离的、按字段打包的数组散布到 GPU 的 AoS 布局中,并正确处理填充。这是粒子系统、模拟和模型加载的惯用方式,适用于 CPU 数据已按字段分离的情况。示例请参阅 references/matrices.md,模型加载模式请参阅 references/pipelines.md。

GPU 端复制: destBuffer.copyFrom(srcBuffer)(模式必须匹配)。清零: buffer.clear()。清理: buffer.destroy()。copyFrom 和 clear 都接受可选的命令编码器——请参阅 references/encoders.md。

读取
const data = await buffer.read(); // returns a typed JS value matching the schema
简写“固定”资源

无需手动创建绑定组——在任何着色器中引用时,缓冲区都会自动绑定:

const particlesMutable = root.createMutable(d.arrayOf(Particle, 1000));  // var<storage, read_write>
const configUniform    = root.createUniform(Config);                     // var<uniform>
const bufReadonly      = root.createReadonly(d.arrayOf(d.f32, N));       // var<storage, read>

在着色器中通过 particles.$、config.$ 访问。默认优先使用固定资源;当需要逐帧切换资源、管理 @group 索引或在多个管线间共享布局时,再切换为手动绑定组。

手动创建的缓冲区可以通过 buffer.as('uniform' | 'readonly' | 'mutable') 转换为相同类型的绑定(需要匹配的 $usage)——当你持有 TgpuBuffer,但需要在着色器中通过 .$ 访问时,请使用此方法。


绑定组布局(手动绑定)

const layout = tgpu.bindGroupLayout({
  config:    { uniform: ConfigSchema },
  particles: { storage: d.arrayOf(Particle), access: 'mutable' },
  mySampler: { sampler: 'filtering' },   // 'filtering' | 'non-filtering' | 'comparison'
  myTexture: { texture: d.texture2d(d.f32) },
});

// Inside shaders: layout.$.config, layout.$.particles, ...

const bindGroup = root.createBindGroup(layout, {
  config:    configBuffer,
  particles: particleBuffer,
  mySampler: tgpuSampler,
  myTexture: textureOrView,
});

pipeline.with(bindGroup).dispatchWorkgroups(N);

来自 createUniform/createMutable/createReadonly 的缓冲区绑定可直接用作条目(无需解包为缓冲区)。

显式指定 @group 索引(仅在与硬编码组索引的原始 WGSL 集成时需要):layout.$idx(0)。


计算管线

// Standard - you control workgroup sizing
const pipeline = root.createComputePipeline({ compute: myComputeFn });
pipeline.with(bindGroup).dispatchWorkgroups(Math.ceil(N / 64));

// Guarded - TypeGPU handles workgroup sizing and bounds checking automatically.
// The callback's parameter count sets the dimensionality (0D to 3D):
const p0 = root.createGuardedComputePipeline(() => { 'use gpu'; /* runs once */ });
const p1 = root.createGuardedComputePipeline((x: number) => { 'use gpu'; });
const p2 = root.createGuardedComputePipeline((x: number, y: number) => { 'use gpu'; });
const p3 = root.createGuardedComputePipeline((x: number, y: number, z: number) => { 'use gpu'; });

// dispatchThreads matches the callback's arity - pass thread counts, not workgroup counts.
// TypeGPU picks workgroup sizes internally and injects a bounds guard so threads
// outside the requested range are no-ops.
p2.with(bindGroup).dispatchThreads(width, height);

// WGSL builtins like globalInvocationId are NOT available - use the callback parameters instead.

WebGPU 坐标约定

WebGPU 与 DirectX/Metal 一致,而非 OpenGL/WebGL——照搬教程会导致一些不易察觉的错误:

  • NDC z:[0, 1],而非 [-1, 1]。复制粘贴的 gluPerspective 会裁剪近裁剪面。使用 wgpu-matrix 的 mat4.perspective(已针对 [0, 1]),或使用 mat4.perspectiveReverseZ 以获得更高的深度精度。
  • 帧缓冲区的 (0, 0) 位于左上角,+y 向下——与 OpenGL 相反。片段着色器中的 d.builtin.position.xy 是以此原点为基准的像素坐标。
  • 纹理 UV 的 (0, 0) 位于左上角。不要预先翻转 v——createImageBitmap 已与此约定一致。
  • 矩阵按列主序存储:d.mat4x4f(c0, c1, c2, c3) 接收的是列。在着色器内部使用 mat.columns[c][r];普通的 mat[i] 会被拒绝。组合顺序:projection * view * model * position。请参阅 references/matrices.md。

渲染管线

const pipeline = root.createRenderPipeline({
  vertex:   myVertex,
  fragment: myFragment,
  targets:  { format: presentationFormat }, // single target - shorthand
  primitive?:    GPUPrimitiveState,
  depthStencil?: GPUDepthStencilState,
  multisample?:  GPUMultisampleState,
});

pipeline
  .with(bindGroup)
  .withColorAttachment({
    view: context,
    // loadOp/storeOp/clearValue have defaults
  })
  .withDepthStencilAttachment({ /* ... */ })
  .withIndexBuffer(indexBuffer)  // enables .drawIndexed()
  .draw(vertexCount, instanceCount /* optional */);

对于简单场景,也可以使用无壳的内联顶点/片段 lambda。

多重渲染目标(MRT)

对片段 out、管线 targets 和 withColorAttachment 使用具名记录——TypeScript 会强制检查键是否匹配。键会原样成为 WGSL 结构体字段名;不要添加 $ 前缀。内置变量(fragDepth)应放在 out 中,但不会出现在 targets 或 withColorAttachment 中。

完整 MRT 示例、逐目标的 blend/writeMask 配置,以及 fragDepth 容易踩坑之处:请参阅 references/pipelines.md。

缓存绑定组和视图

每次调用 root.createBindGroup(...) 和 texture.createView(...) 都会分配新的 GPU 对象。原型开发时没问题;对于需要认真对待的项目,应在设置阶段(靠近其包装的资源)创建一次,将句柄存入 const,然后重复使用。逐帧分配本身不一定慢,但会增加 GC 压力并造成卡顿。当视图或绑定组确实需要逐帧变化时,请缓存要循环使用的一小组对象。

关于顶点缓冲区布局、attribs 展开技巧和 common.fullScreenTriangle 助手:请参阅 references/pipelines.md。

批处理工作

每次调用 draw()/dispatchWorkgroups() 都会记录并提交一个独立的单管线渲染通道。若要在一个通道中运行多个管线(共享附件),或将多个通道批处理到一次提交中,请使用带类型的命令编码器——root['~unstable'].createCommandEncoder() → beginRenderPass/beginComputePass → pipeline.with(pass).draw(...) → pass.end() → encoder.submit()。关于渲染包和原始 WebGPU 编码器互操作:请参阅 references/encoders.md。

管线会在首次使用时延迟初始化;pipeline.initSync() / await pipeline.initAsync() 可将这部分开销提前到加载界面(请参阅 references/pipelines.md)。


GPU 作用域变量

tgpu.workgroupVar(schema)——在一个工作组的所有线程间共享(仅计算着色器可用)。tgpu.privateVar(schema)——线程私有变量。tgpu.const(schema, value)——嵌入为 WGSL 字面量的编译期常量。均通过 .$ 访问。完整示例见 references/shaders.md。


Slots

tgpu.slot<T>() 是一个带类型的占位符;可在根作用域(创建管线之前)或函数作用域中使用 .with(slot, value) 填充——管线不接受在 .with() 中传入 slot。任何类型都适用:GPU 值、函数、回调。Slots 是构建可配置、可复用着色器的惯用方式。

const distFnSlot = tgpu.slot<(pos: d.v3f) => number>();

const rayMarcher = tgpu.computeFn({
  workgroupSize: [64],
  in: { gid: d.builtin.globalInvocationId },
})(({ gid }) => {
  'use gpu';
  const dist = distFnSlot.$(d.vec3f(gid)); // call the injected function
});

root
  .with(distFnSlot, (pos) => {
    'use gpu';
    return std.length(pos - d.vec3f(0, 0, -5)) - 1.0; // sphere SDF
  })
  .createComputePipeline({ compute: rayMarcher });

带默认值的标量/向量 slot:

const colorSlot = tgpu.slot(d.vec4f(1, 0, 0, 1));
root.with(colorSlot, d.vec4f(0, 1, 0, 1)).createRenderPipeline({ ... });

访问器

tgpu.accessor(schema, initial?) 会感知 schema——值可以是缓冲区绑定、常量、字面量,或返回上述任意一种的 'use gpu' 函数。着色器无需关心值的来源。如果使用起来顺畅,应优先选择访问器而非 slots。

const colorAccess = tgpu.accessor(d.vec3f);

// Fill with a uniform buffer:
root.with(colorAccess, colorUniform).createComputePipeline(...)

// Fill with a literal (inlined):
root.with(colorAccess, d.vec3f(1, 0, 0)).createComputePipeline(...)

// Fill with a GPU function:
root.with(colorAccess, () => { 'use gpu'; return computeColor(); }).createComputePipeline(...)

写入访问:tgpu.mutableAccessor(schema, initial?)。


类型工具

d.InferInput<typeof Schema> — .write() 接受的 CPU 端类型。d.InferGPU<typeof Schema> — 'use gpu' 函数内部的类型。d.AnyData(也可从 'typegpu/data' 导入)— 泛型最宽泛的 schema 约束。完整的缓冲区/纹理 TypeScript 类型(TgpuBuffer、TgpuUniform、TgpuTexture、使用标志):references/types.md。


常见问题

  1. 数值字面量:1.0 可能会被剥离为 abstractInt。请使用 d.f32(1)。参见 types.md。
  2. 外部作用域捕获的是常量:无法在运行时更改。请使用 createUniform/createMutable。参见 shaders.md。
  3. TypedArray/ArrayBuffer 对齐:字节会原样复制。vec3f 元素占 16 字节(12 + 4 字节填充)。普通数组会处理填充;TypedArray 必须包含填充。
  4. 整数除法:对基本类型执行 a / b 会得到 f32。若要使用整数语义,请使用 d.i32()/d.u32()。参见 types.md。
  5. 未初始化的变量:let x; 无效——务必初始化,以便推断类型:let x = d.f32(0)。
  6. 三元运算符:运行时三元运算符会编译为 WGSL select——两个分支始终都会执行,因此分支必须无副作用且返回标量/向量值(不能是结构体/数组/矩阵;这些情况请使用 if/else)。编译时已知的条件会完全裁剪掉未选中的分支。参见 shaders.md。
  7. 片段输出始终为 4 个分量(d.vec4f;整数格式使用 d.vec4i/d.vec4u),即使格式的通道数更少也是如此。即便管线设置了 targets: { format: 'r8unorm' } 或 'rg16float',仍要求 out: d.vec4f 并返回 d.vec4f(...)。WebGPU 会丢弃未使用的通道。

配套包

  • @typegpu/noise — 真正的伪随机数生成器(randf)、分布(均匀、正态、半球……)以及 Perlin 噪声(perlin2d/perlin3d),并支持可选的预计算梯度缓存(速度提升约 10 倍)。建议优先使用它,而非手写哈希。参见 references/noise.md。

  • @typegpu/sdf — 2D/3D 有符号距离基本图形(sdDisk、sdBox2d、sdRoundedBox2d、sdBezier、sdSphere、sdBox3d、sdCapsule、sdPlane……)和运算符(opUnion、opSmoothUnion、opSmoothDifference、opExtrudeX/Y/Z)。全部都是类型固定的 tgpu.fn,可直接从 'use gpu' 中调用。适用于光线步进、UI 遮罩、抗锯齿矢量绘图。参见 references/sdf.md。

  • @typegpu/react — 在 React 和 React Native 中使用 TypeGPU 的 hooks(useRoot、useFrame、useUniform……),包括通过 react-native-worklets 实现的 UI 线程渲染循环。参见 references/react.md。

  • TypeGPU CLI — npx typegpu@latest 用于搭建新项目;--enhance 可将 TypeGPU 改造集成到现有项目中。参见 references/setup.md。

  • wgpu-matrix — TypeGPU 的标准数学库。TypeGPU 向量/矩阵可作为 dst 传入 wgpu-matrix 调用,以避免分配内存。完整集成模式请参见 references/matrices.md。

评论

支持 Markdown