typegpu

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeGPU

TypeGPU

A single schema (
d.*
) defines a GPU type, CPU buffer layout, and TypeScript type at once - no manual alignment, type mapping, or casting. The build plugin
unplugin-typegpu
transforms
'use gpu'
-marked TypeScript for runtime WGSL transpilation, enabling type inference and polymorphism across the CPU/GPU boundary.
This skill targets TypeGPU
0.11.2
. If the user's project is on an older release, verify API availability before relying on examples or recommended patterns here.

一套架构(
d.*
)可同时定义 GPU 类型、CPU 缓冲区布局和 TypeScript 类型——无需手动对齐、类型映射或强制转换。构建插件
unplugin-typegpu
会转换标记有
'use gpu'
的 TypeScript 代码,实现运行时 WGSL 转译,支持 CPU/GPU 边界间的类型推断与多态性。
本技能针对 TypeGPU
0.11.2
版本。如果用户项目使用旧版本,在参考示例或推荐模式前,请先验证 API 是否可用。

When to read reference files

何时查阅参考文档

Read before writing virtually any shader or GPU function — these two cover the rules that trip people up most:
  • references/types.md
    — abstract type resolution, exactly when
    d.f32()
    is required vs redundant, sampler/texture schemas for
    tgpu.fn
    signatures, CPU-side
    TgpuBuffer
    /
    TgpuTexture
    TypeScript types. If you skip this, you'll hit type errors.
  • references/shaders.md
    — full
    std
    library listing, loops (
    std.range
    ,
    tgpu.unroll
    ),
    tgpu.comptime
    , outer-scope capture rules, complete builtin reference for all three shader stages,
    console.log
    . Read this for any non-trivial shader logic.
Read when the task specifically involves:
  • references/pipelines.md
    — vertex buffers/layouts,
    attribs
    wiring, MRT, fullscreen triangle, depth/stencil, blend modes,
    fragDepth
    output, loading 3D models (
    @loaders.gl
    ), resolve API
  • references/matrices.md
    wgpu-matrix
    integration, column-major layout, camera uniforms,
    common.writeSoA
    , fast-path CPU writes. Read for any 3D work (view/projection matrices, animated transforms, model loading)
  • references/textures.md
    — texture creation, views, samplers, storage textures, mipmaps, multisampling
  • references/noise.md
    @typegpu/noise
    (random, distributions, Perlin 2D/3D)
  • references/sdf.md
    @typegpu/sdf
    (2D/3D primitives, operators, ray marching, AA masking)
  • references/setup.md
    — install,
    unplugin-typegpu
    build plugin,
    tsover
    operator overloading
  • references/advanced.md
    — buffer reinterpretation, indirect drawing/dispatch, custom encoders

在编写几乎所有着色器或 GPU 函数前阅读——这两份文档涵盖了最容易出错的规则:
  • references/types.md
    —— 抽象类型解析、
    d.f32()
    的必要与冗余场景、
    tgpu.fn
    签名的采样器/纹理架构、CPU 端
    TgpuBuffer
    /
    TgpuTexture
    TypeScript 类型。如果跳过此文档,你会遇到类型错误。
  • references/shaders.md
    —— 完整的
    std
    库列表、循环(
    std.range
    tgpu.unroll
    )、
    tgpu.comptime
    、外部作用域捕获规则、所有三个着色器阶段的完整内置函数参考、
    console.log
    编写任何非 trivial 的着色器逻辑时请阅读此文档。
当任务具体涉及以下内容时阅读:
  • references/pipelines.md
    —— 顶点缓冲区/布局、
    attribs
    连接、MRT(多渲染目标)、全屏三角形、深度/模板、混合模式、
    fragDepth
    输出、加载 3D 模型(
    @loaders.gl
    )、解析 API
  • references/matrices.md
    ——
    wgpu-matrix
    集成、列主序布局、相机 uniforms、
    common.writeSoA
    、CPU 快速写入路径。任何 3D 工作(视图/投影矩阵、动画变换、模型加载)都需阅读此文档
  • references/textures.md
    —— 纹理创建、视图、采样器、存储纹理、Mipmap、多重采样
  • references/noise.md
    ——
    @typegpu/noise
    (随机数、分布、2D/3D Perlin 噪声)
  • references/sdf.md
    ——
    @typegpu/sdf
    (2D/3D 基本体、运算符、光线步进、抗锯齿遮罩)
  • references/setup.md
    —— 安装、
    unplugin-typegpu
    构建插件、
    tsover
    运算符重载
  • references/advanced.md
    —— 缓冲区重解释、间接绘制/调度、自定义编码器

Setup

初始化

ts
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' });
Create one root at app startup. Resources from different roots cannot interact.

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

const root = await tgpu.init();             // 请求 GPU 设备
const root = tgpu.initFromDevice(device);   // 或包装现有 GPUDevice

const context = root.configureContext({ canvas, alphaMode: 'premultiplied' });
在应用启动时创建一个 root。不同 root 的资源无法交互。

Data schemas (
d.*
)

数据架构(
d.*

A schema defines memory layout and infers TypeScript types; the same schema is used for buffers, shader signatures, and bind group entries.
架构定义内存布局并推断 TypeScript 类型;同一架构可用于缓冲区、着色器签名和绑定组条目。

Scalars

标量

ts
d.f32    d.i32    d.u32    d.f16
// d.bool is NOT host-shareable - use d.u32 in buffers
ts
d.f32    d.i32    d.u32    d.f16
// d.bool 不可与主机共享——在缓冲区中使用 d.u32

Vectors and matrices

向量与矩阵

ts
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.mat2x2f   d.mat3x3f   d.mat4x4f
Instance types:
d.vec3f()
->
d.v3f
,
d.mat4x4f()
->
d.m4x4f
.
Vector constructors are richly overloaded - use them. They compose from any mix of scalars and smaller vectors that adds up to the right component count:
ts
d.vec3f()              // zero-init: (0, 0, 0)
d.vec3f(1)             // broadcast:  (1, 1, 1)
d.vec3f(1, 2, 3)       // individual components
d.vec3f(someVec2, 1)   // vec2 + scalar
d.vec3f(1, someVec2)   // scalar + vec2

d.vec4f()              // zero-init: (0, 0, 0, 0)
d.vec4f(0.5)           // broadcast:  (0.5, 0.5, 0.5, 0.5)
d.vec4f(rgb, 1)        // vec3 + scalar (common: color + alpha)
d.vec4f(v2a, v2b)      // two vec2s
d.vec4f(1, uv, 0)      // scalar + vec2 + scalar
Swizzles (
.xy
,
.zw
,
.rgb
,
.ba
, etc.) return vector instances that work as constructor arguments:
d.vec4f(pos.xy, vel.zw)
.
Prefer these overloads over manual component decomposition. Instead of
d.vec3f(v.x, v.y, newZ)
, write
d.vec3f(v.xy, newZ)
.
ts
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.mat2x2f   d.mat3x3f   d.mat4x4f
实例类型:
d.vec3f()
->
d.v3f
d.mat4x4f()
->
d.m4x4f
向量构造函数支持丰富的重载——请充分利用。 它们可由任意标量和更小向量组合而成,只要组件总数符合要求:
ts
d.vec3f()              // 零初始化:(0, 0, 0)
d.vec3f(1)             // 广播:(1, 1, 1)
d.vec3f(1, 2, 3)       // 单独指定组件
d.vec3f(someVec2, 1)   // vec2 + 标量
d.vec3f(1, someVec2)   // 标量 + vec2

d.vec4f()              // 零初始化:(0, 0, 0, 0)
d.vec4f(0.5)           // 广播:(0.5, 0.5, 0.5, 0.5)
d.vec4f(rgb, 1)        // vec3 + 标量(常见:颜色 + 透明度)
d.vec4f(v2a, v2b)      // 两个 vec2
d.vec4f(1, uv, 0)      // 标量 + vec2 + 标量
Swizzle(
.xy
.zw
.rgb
.ba
等)返回的向量实例可作为构造函数参数:
d.vec4f(pos.xy, vel.zw)
优先使用这些重载而非手动分解组件。 不要写
d.vec3f(v.x, v.y, newZ)
,而是写
d.vec3f(v.xy, newZ)

Compound types

复合类型

ts
const Particle = d.struct({
  position: d.vec2f,
  velocity: d.vec2f,
  color:    d.vec4f,
});

const ParticleArray = d.arrayOf(Particle, 1000); // fixed-size
Runtime-sized schemas.
d.arrayOf(Element)
without a count returns a function
(n: number) => WgslArray<Element>
. This dual nature is the key: pass the function itself (unsized) to bind group layouts, call it with a count (sized) for buffer creation.
ts
// 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)
You cannot pass an unsized schema directly to
createBuffer
- size must be known on the CPU.

ts
const Particle = d.struct({
  position: d.vec2f,
  velocity: d.vec2f,
  color:    d.vec4f,
});

const ParticleArray = d.arrayOf(Particle, 1000); // 固定大小
运行时大小的架构。 不带数量的
d.arrayOf(Element)
返回一个函数
(n: number) => WgslArray<Element>
。这种双重特性是关键:将函数本身(未指定大小)传递给绑定组布局,调用时传入数量(指定大小)以创建缓冲区。
ts
// 普通数组——不带数量的 arrayOf 本身就是工厂函数:
const layout = tgpu.bindGroupLayout({
  data: { storage: d.arrayOf(d.f32), access: 'mutable' },  // 布局使用未指定大小的版本
});
const buf = root.createBuffer(d.arrayOf(d.f32, 1024)).$usage('storage'); // 缓冲区使用指定大小的版本

// 最后一个字段为运行时大小的结构体——包装为工厂函数:
const RuntimeStruct = (n: number) =>
  d.struct({
    counter: d.atomic(d.u32),
    items:   d.arrayOf(d.f32, n),  // 最后一个字段使用运行时大小
  });

const layout2 = tgpu.bindGroupLayout({
  runtimeData: { storage: RuntimeStruct, access: 'mutable' }, // 未指定大小(传入函数)
});
const buf2 = root.createBuffer(RuntimeStruct(1024)).$usage('storage'); // 指定大小(调用函数)
不能直接将未指定大小的架构传递给
createBuffer
——CPU 必须知道大小。

GPU functions

GPU 函数

TypeGPU compiles TypeScript marked with
'use gpu'
into WGSL.
TypeGPU 会将标记有
'use gpu'
的 TypeScript 代码编译为 WGSL。

Plain callback (polymorphic)

普通回调(多态)

No explicit signature; best for helper math and flexible utilities.
ts
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
parameters and unions like
d.v2f | d.v3f
are polymorphic - TypeGPU generates one WGSL overload per unique call-site type combination. Values captured from outer scope are inlined as WGSL literals; use buffers/uniforms for anything that changes at runtime.
无显式签名;最适合辅助数学计算和灵活的工具函数。
ts
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
(explicit types)

tgpu.fn
(显式类型)

Pinned WGSL signature. Use for library code or when you need a fixed WGSL interface.
ts
const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)((v, angle) => {
  'use gpu';
  // ...
});
固定的 WGSL 签名。适用于库代码或需要固定 WGSL 接口的场景。
ts
const rotate = tgpu.fn([d.vec2f, d.f32], d.vec2f)((v, angle) => {
  'use gpu';
  // ...
});

Shader entrypoints

着色器入口点

ts
// 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); });
Vertex
in
may include builtins:
d.builtin.vertexIndex
,
d.builtin.instanceIndex
.
Full shader syntax, branch pruning, the
std
library, and type inference: see
references/shaders.md
.

Values vs references — the most common source of
ResolutionError
. See
references/shaders.md
.
Idiomatic patterns (vector ops, struct constructors, register pressure): see
references/shaders.md
.

ts
// 计算着色器
const myCompute = tgpu.computeFn({
  workgroupSize: [64],
  in: { gid: d.builtin.globalInvocationId },
})((input) => { 'use gpu'; /* input.gid: d.v3u */ });

// 顶点着色器
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 };
});

// 片段着色器
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

值 vs 引用——这是
ResolutionError
最常见的原因。请查阅
references/shaders.md
惯用模式(向量操作、结构体构造函数、寄存器压力):请查阅
references/shaders.md

Buffers

缓冲区

Creating

创建

ts
// 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);
ts
// 仅指定架构:
const buf = root.createBuffer(d.arrayOf(Particle, 1000)).$usage('storage');

// 带类型初始值(仅非零值时使用——所有缓冲区默认零初始化):
const uBuf = root.createBuffer(Config, { time: 1, scale: 2.0 }).$usage('uniform');

// 带初始化回调——缓冲区仍处于映射状态(CPU 最快路径):
const buf = root.createBuffer(Schema, (mappedBuffer) => {
  mappedBuffer.write([10, 20], { startOffset: firstChunk.offset });
  mappedBuffer.write([30, 40], { startOffset: secondChunk.offset });
});

// 包装现有 GPUBuffer(你需负责其生命周期和标志):
const buf = root.createBuffer(d.u32, existingGPUBuffer);
buf.write(12);

Usage flags

使用标志

LiteralShader access
'uniform'
var<uniform>
'storage'
var<storage, read>
(or
read_write
with
access: 'mutable'
)
'vertex'
vertex input, paired with
tgpu.vertexLayout
'index'
index buffer (
d.u16
or
d.u32
schema only)
'indirect'
indirect dispatch/draw
All buffers get
COPY_SRC | COPY_DST
automatically.
$addFlags(GPUBufferUsage.X)
adds any flag not covered by
$usage
.
字面量着色器访问权限
'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
未覆盖的任何标志。

Writing

写入

.write(value)
handles alignment. Four input forms (slowest → fastest):
FormExample (
vec3f
)
Notes
Typed instance
d.vec3f(1, 2, 3)
Allocates a wrapper — fine for setup/prototypes
Plain JS array / tuple
[1, 2, 3]
No allocation, padding added automatically
TypedArray
new Float32Array([1, 2, 3])
Bytes copied verbatim — must include WGSL padding
ArrayBuffer
rawBytes
Maximum throughput, bytes copied verbatim
Cache plain arrays or
Float32Array
at setup and reuse. For the padding rules (
vec3f
= 16 bytes,
mat3x3f
per-column padding) and full fast-path guidance, see
references/matrices.md
.
Slice write - update a sub-region using
d.memoryLayoutOf
to get byte offsets:
ts
const layout = d.memoryLayoutOf(schema, (a) => a[3]);
buffer.write([4, 5, 6], { startOffset: layout.offset });
.patch(data)
- update specific struct fields or array indices without touching the rest:
ts
planetBuffer.patch({
  mass: 123.1,
  colors: { 2: [1, 0, 0], 4: d.vec3f(0, 0, 1) },
});
common.writeSoA(buffer, { field: Float32Array, ... })
- scatter separate packed per-field arrays into the GPU's AoS layout with correct padding. The idiomatic path for particle systems, simulations, and model loading where CPU data is already field-separated. See
references/matrices.md
for examples and
references/pipelines.md
for the model-loading pattern.
GPU-side copy:
destBuffer.copyFrom(srcBuffer)
(schemas must match).
.write(value)
会处理对齐问题。四种输入形式(从最慢到最快):
形式示例(
vec3f
说明
类型化实例
d.vec3f(1, 2, 3)
会分配包装器——适合设置/原型开发
普通 JS 数组/元组
[1, 2, 3]
无分配,自动添加填充
TypedArray
new Float32Array([1, 2, 3])
字节直接复制——必须包含 WGSL 填充
ArrayBuffer
rawBytes
最大吞吐量,字节直接复制
在设置阶段缓存普通数组或
Float32Array
并复用。关于填充规则(
vec3f
= 16 字节,
mat3x3f
按列填充)和完整的快速路径指南,请查阅
references/matrices.md
切片写入——使用
d.memoryLayoutOf
获取字节偏移量来更新子区域:
ts
const layout = d.memoryLayoutOf(schema, (a) => a[3]);
buffer.write([4, 5, 6], { startOffset: layout.offset });
.patch(data)
——更新特定结构体字段或数组索引,不影响其他部分:
ts
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)
(架构必须匹配)。

Reading

读取

ts
const data = await buffer.read(); // returns a typed JS value matching the schema
ts
const data = await buffer.read(); // 返回与架构匹配的类型化 JS 值

Shorthand "fixed" resources

简写“固定”资源

Skip manual bind groups - the buffer is always bound when referenced in any shader:
ts
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>
Access inside shaders via
particles.$
,
config.$
. Prefer fixed resources by default; switch to manual bind groups when you need to swap resources per frame, manage
@group
indices, or share layouts across pipelines.

跳过手动绑定组——缓冲区在任何着色器中被引用时始终处于绑定状态:
ts
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
索引或跨管线共享布局时,切换到手动绑定组。

Bind group layouts (manual binding)

绑定组布局(手动绑定)

ts
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);
Explicit
@group
index (only needed when integrating with raw WGSL that hardcodes group indices):
layout.$idx(0)
.

ts
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) },
});

// 在着色器中:layout.$.config, layout.$.particles, ...

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

pipeline.with(bindGroup).dispatchWorkgroups(N);
显式
@group
索引(仅当与硬编码组索引的原生 WGSL 集成时需要):
layout.$idx(0)

Compute pipelines

计算管线

ts
// 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.

ts
// 标准模式——你控制工作组大小
const pipeline = root.createComputePipeline({ compute: myComputeFn });
pipeline.with(bindGroup).dispatchWorkgroups(Math.ceil(N / 64));

// 自动防护模式——TypeGPU 自动处理工作组大小和边界检查。
// 回调的参数数量决定维度(0D 到 3D):
const p0 = root.createGuardedComputePipeline(() => { 'use gpu'; /* 运行一次 */ });
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 匹配回调的参数数量——传递线程数,而非工作组数。
// TypeGPU 内部选择工作组大小并注入边界防护,超出请求范围的线程会执行空操作。
p2.with(bindGroup).dispatchThreads(width, height);

// 无法使用 WGSL 内置变量如 globalInvocationId——请改用回调参数。

WebGPU coordinate conventions

WebGPU 坐标约定

WebGPU matches DirectX/Metal, not OpenGL/WebGL — porting tutorials verbatim causes subtle bugs:
  • NDC z:
    [0, 1]
    , not
    [-1, 1]
    . A copy-pasted
    gluPerspective
    clips the near plane. Use
    wgpu-matrix
    's
    mat4.perspective
    (already targets
    [0, 1]
    ), or
    mat4.perspectiveReverseZ
    for better depth precision.
  • Framebuffer
    (0, 0)
    is top-left
    ,
    +y
    down — opposite of OpenGL.
    d.builtin.position.xy
    in a fragment shader is pixel-space with this origin.
  • Texture UV
    (0, 0)
    is top-left
    . Do not pre-flip
    v
    createImageBitmap
    already matches this.
  • Matrices are column-major:
    d.mat4x4f(c0, c1, c2, c3)
    takes columns. Inside shaders use
    mat.columns[c][r]
    ; plain
    mat[i]
    is rejected. Composition:
    projection * view * model * position
    . See
    references/matrices.md
    .

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

Render pipelines

渲染管线

ts
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 */);
Shell-less inline vertex/fragment lambdas are also valid for simple cases.
ts
const pipeline = root.createRenderPipeline({
  vertex:   myVertex,
  fragment: myFragment,
  targets:  { format: presentationFormat }, // 单个目标——简写形式
  primitive?:    GPUPrimitiveState,
  depthStencil?: GPUDepthStencilState,
  multisample?:  GPUMultisampleState,
});

pipeline
  .with(bindGroup)
  .withColorAttachment({
    view: context,
    // loadOp/storeOp/clearValue 有默认值
  })
  .withDepthStencilAttachment({ /* ... */ })
  .withIndexBuffer(indexBuffer)  // 启用 .drawIndexed()
  .draw(vertexCount, instanceCount /* 可选 */);
对于简单场景,也可使用无外壳的内联顶点/片段 lambda。

Multiple render targets (MRT)

多渲染目标(MRT)

Use a named record for fragment
out
, pipeline
targets
, and
withColorAttachment
— TypeScript enforces matching keys. Keys become WGSL struct field names verbatim; no
$
-prefixes. Builtins (
fragDepth
) go in
out
but do not appear in
targets
or
withColorAttachment
.
Full MRT example, per-target blend/writeMask config, and the
fragDepth
footgun: see
references/pipelines.md
.
为片段着色器的
out
、管线的
targets
withColorAttachment
使用命名记录——TypeScript 会强制匹配键名。键名会直接成为 WGSL 结构体字段名;无需添加
$
前缀。内置变量(
fragDepth
)需放在
out
中,但不会出现在
targets
withColorAttachment
中。
完整的 MRT 示例、每个目标的混合/写入掩码配置以及
fragDepth
的陷阱:请查阅
references/pipelines.md

Cache bind groups and views

缓存绑定组和视图

root.createBindGroup(...)
and
texture.createView(...)
allocate fresh GPU objects each call. Fine for prototypes; for anything you care about, create them once at setup (near the resource they wrap), store handles in
const
s, and reuse. Per-frame allocation isn't slow per se, but it raises GC pressure and introduces stutters. When a view or bind group legitimately varies each frame, cache the small set you cycle through.
For vertex buffer layouts, the attribs spread trick, and the
common.fullScreenTriangle
helper:
references/pipelines.md
.

root.createBindGroup(...)
texture.createView(...)
每次调用都会分配新的 GPU 对象。原型开发中没问题;但对于性能敏感的场景,请在设置阶段(靠近其包装的资源)创建一次,将句柄存储在
const
中并复用。每帧分配本身并不慢,但会增加 GC 压力并导致卡顿。当视图或绑定组确实需要每帧变化时,请缓存你循环使用的小集合。
关于顶点缓冲区布局、attribs 展开技巧和
common.fullScreenTriangle
辅助函数:请查阅
references/pipelines.md

GPU-scoped variables

GPU 作用域变量

tgpu.workgroupVar(schema)
— shared across all threads in a workgroup (compute only).
tgpu.privateVar(schema)
— thread-private.
tgpu.const(schema, value)
— compile-time constant embedded as a WGSL literal. Access all via
.$
. Full examples in
references/shaders.md
.

tgpu.workgroupVar(schema)
—— 在工作组内所有线程间共享(仅计算着色器可用)。
tgpu.privateVar(schema)
—— 线程私有。
tgpu.const(schema, value)
—— 编译时常量,内联为 WGSL 字面量。所有变量通过
.$
访问。完整示例请查阅
references/shaders.md

Slots

插槽

tgpu.slot<T>()
is a typed placeholder; fill with
.with(slot, value)
at pipeline, root, or function scope. Any type fits: GPU values, functions, callbacks. Slots are the idiomatic way to build configurable/reusable shaders.
ts
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 });
Scalar/vector slot with a default:
ts
const colorSlot = tgpu.slot(d.vec4f(1, 0, 0, 1));
pipeline.with(colorSlot, d.vec4f(0, 1, 0, 1)).draw(3);

tgpu.slot<T>()
是类型化占位符;可在管线、root 或函数作用域通过
.with(slot, value)
填充。任何类型都适用:GPU 值、函数、回调。插槽是构建可配置/可复用着色器的惯用方式。
ts
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)); // 调用注入的函数
});

root
  .with(distFnSlot, (pos) => {
    'use gpu';
    return std.length(pos - d.vec3f(0, 0, -5)) - 1.0; // 球体 SDF
  })
  .createComputePipeline({ compute: rayMarcher });
带默认值的标量/向量插槽:
ts
const colorSlot = tgpu.slot(d.vec4f(1, 0, 0, 1));
pipeline.with(colorSlot, d.vec4f(0, 1, 0, 1)).draw(3);

Accessors

访问器

tgpu.accessor(schema, initial?)
is schema-aware - the value can be a buffer binding, a constant, a literal, or a
'use gpu'
function returning one. The shader is agnostic about how the value is sourced. If they can be cleanly used, they should be preferred over slots.
ts
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(...)
Write access:
tgpu.mutableAccessor(schema, initial?)
.

tgpu.accessor(schema, initial?)
支持架构感知——值可以是缓冲区绑定、常量、字面量或返回上述类型的
'use gpu'
函数。着色器无需关心值的来源。如果可以清晰使用,应优先选择访问器而非插槽。
ts
const colorAccess = tgpu.accessor(d.vec3f);

// 填充为 uniform 缓冲区:
root.with(colorAccess, colorUniform).createComputePipeline(...);

// 填充为字面量(内联):
root.with(colorAccess, d.vec3f(1, 0, 0)).createComputePipeline(...);

// 填充为 GPU 函数:
root.with(colorAccess, () => { 'use gpu'; return computeColor(); }).createComputePipeline(...);
写入权限:
tgpu.mutableAccessor(schema, initial?)

Type utilities

类型工具

d.InferInput<typeof Schema>
— CPU-side type accepted by
.write()
.
d.InferGPU<typeof Schema>
— type inside
'use gpu'
functions.
AnyData
(from
'typegpu'
) — broadest schema constraint for generics. Full buffer/texture TypeScript types (
TgpuBuffer
,
TgpuUniform
,
TgpuTexture
, usage flags):
references/types.md
.

d.InferInput<typeof Schema>
——
.write()
接受的 CPU 端类型。
d.InferGPU<typeof Schema>
——
'use gpu'
函数内部的类型。
AnyData
(来自
'typegpu'
)——泛型的最宽架构约束。完整的缓冲区/纹理 TypeScript 类型(
TgpuBuffer
TgpuUniform
TgpuTexture
、使用标志):请查阅
references/types.md

Common pitfalls

常见陷阱

  1. Numeric literals:
    1.0
    may strip ->
    abstractInt
    . Use
    d.f32(1)
    . See types.md.
  2. Outer-scope captures are constants: not runtime-mutable. Use
    createUniform
    /
    createMutable
    . See shaders.md.
  3. TypedArray/ArrayBuffer alignment: bytes copied verbatim.
    vec3f
    elements are 16 bytes (12 + 4 padding). Plain arrays handle padding; typed arrays must include it.
  4. Integer division:
    a / b
    on primitives is
    f32
    . Use
    d.i32()
    /
    d.u32()
    for integer semantics. See types.md.
  5. Uninitialised variables:
    let x;
    is invalid - always initialise so the type can be inferred:
    let x = d.f32(0)
    .
  6. Ternary operators: runtime ternaries aren't supported. Use
    std.select(falseVal, trueVal, condition)
    .
  7. Fragment output is always
    d.vec4f
    , even for fewer-channel formats. A pipeline with
    targets: { format: 'r8unorm' }
    or
    'rg16float'
    still requires
    out: d.vec4f
    and
    return d.vec4f(...)
    . WebGPU drops the unused channels.

  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. 三元运算符:不支持运行时三元运算符。使用
    std.select(falseVal, trueVal, condition)
  7. 片段输出始终为
    d.vec4f
    ,即使格式通道更少。目标格式为
    'r8unorm'
    'rg16float'
    的管线仍需
    out: d.vec4f
    并返回
    d.vec4f(...)
    。WebGPU 会丢弃未使用的通道。

Companion packages

配套包

  • @typegpu/noise
    - real PRNG (
    randf
    ), distributions (uniform, normal, hemisphere, ...), and Perlin noise (
    perlin2d
    /
    perlin3d
    ) with optional precomputed gradient caches (~10x speedups). Prefer over hand-rolled hashes. See
    references/noise.md
    .
  • @typegpu/sdf
    - 2D/3D signed distance primitives (
    sdDisk
    ,
    sdBox2d
    ,
    sdRoundedBox2d
    ,
    sdBezier
    ,
    sdSphere
    ,
    sdBox3d
    ,
    sdCapsule
    ,
    sdPlane
    , ...) and operators (
    opUnion
    ,
    opSmoothUnion
    ,
    opSmoothDifference
    ,
    opExtrudeX/Y/Z
    ). All
    tgpu.fn
    with pinned types, callable directly from
    'use gpu'
    . For ray marching, UI masking, AA vector drawing. See
    references/sdf.md
    .
  • wgpu-matrix
    - canonical math library for TypeGPU. TypeGPU vectors/matrices can be passed as
    dst
    to
    wgpu-matrix
    calls to avoid allocations. See
    references/matrices.md
    for full integration patterns.
  • @typegpu/noise
    - 真正的伪随机数生成器(
    randf
    )、分布(均匀分布、正态分布、半球分布等)以及 2D/3D 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
  • wgpu-matrix
    - TypeGPU 的标准数学库。TypeGPU 的向量/矩阵可作为
    dst
    参数传递给
    wgpu-matrix
    调用以避免分配。完整的集成模式请查阅
    references/matrices.md