Skip to content

Pipelines

TypeGPU introduces a custom API to easily define and execute render and compute pipelines. It abstracts away the standard WebGPU procedures to offer a convenient, type-safe way to run shaders on the GPU.

A pipeline can be defined with one of the following methods on the root object:

const
const renderPipeline: TgpuRenderPipeline<d.Vec4f>
renderPipeline
=
const root: TgpuRoot
root
.
WithBinding.createRenderPipeline<{}, {}, {}, d.Vec4f>(descriptor: TgpuRenderPipeline<in Targets = never>.DescriptorBase & {
attribs?: {} | undefined;
vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<{}>);
fragment: TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{}>>) => d.v4f | (AnyAutoCustoms & Partial<...>));
targets?: TgpuColorTargetState;
}): TgpuRenderPipeline<...> (+2 overloads)
createRenderPipeline
({
vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<{}>)
vertex
:
const mainVertex: TgpuVertexFn<{}, {}>
mainVertex
,
fragment: TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{}>>) => d.v4f | (AnyAutoCustoms & Partial<InferGPURecord<{
readonly $fragDepth: d.BuiltinFragDepth;
readonly $sampleMask: d.BuiltinSampleMask;
}>>))
fragment
:
const mainFragment: TgpuFragmentFn<{}, d.Vec4f>
mainFragment
,
targets?: TgpuColorTargetState
targets
: {
format?: GPUTextureFormat | undefined

The

GPUTextureFormat

of this color target. The pipeline will only be compatible with

GPURenderPassEncoder

s which use a

GPUTextureView

of this format in the corresponding color attachment.

@defaultnavigator.gpu.getPreferredCanvasFormat()

format
:
const presentationFormat: "rgba8unorm"
presentationFormat
},
});
const
const computePipeline1: TgpuComputePipeline
computePipeline1
=
const root: TgpuRoot
root
.
WithBinding.createComputePipeline<{}>(descriptor: TgpuComputePipeline.Descriptor<{}>): TgpuComputePipeline
createComputePipeline
({
compute: TgpuComputeFn<{}>
compute
:
const mainCompute: TgpuComputeFn<{}>
mainCompute
,
});
const
const computePipeline2: TgpuGuardedComputePipeline<[x: number, y: number, z: number]>
computePipeline2
=
const root: TgpuRoot
root
.
WithBinding.createGuardedComputePipeline<[x: number, y: number, z: number]>(callback: (x: number, y: number, z: number) => void): TgpuGuardedComputePipeline<[x: number, y: number, z: number]>

Creates a compute pipeline that executes the given callback in an exact number of threads. This is different from createComputePipeline() in that it does a bounds check on the thread id, where as regular pipelines do not and work in units of workgroups.

@paramcallback A function converted to WGSL and executed on the GPU. It can accept up to 3 parameters (x, y, z) which correspond to the global invocation ID of the executing thread.

@example

If no parameters are provided, the callback will be executed once, in a single thread.

const fooPipeline = root
.createGuardedComputePipeline(() => {
'use gpu';
console.log('Hello, GPU!');
});
fooPipeline.dispatchThreads();
// [GPU] Hello, GPU!

@example

One parameter means n-threads will be executed in parallel.

const fooPipeline = root
.createGuardedComputePipeline((x) => {
'use gpu';
if (x % 16 === 0) {
// Logging every 16th thread
console.log('I am the', x, 'thread');
}
});
// executing 512 threads
fooPipeline.dispatchThreads(512);
// [GPU] I am the 256 thread
// [GPU] I am the 272 thread
// ... (30 hidden logs)
// [GPU] I am the 16 thread
// [GPU] I am the 240 thread

createGuardedComputePipeline
((
x: number
x
,
y: number
y
,
z: number
z
) => {
'use gpu';
// ...
});

The createRenderPipeline method creates a render pipeline by accepting an options object that specifies the vertex function, fragment function, targets, and optional additional settings.

  • vertex: The TgpuVertexFn or 'use gpu' callback to use as the vertex shader.
  • fragment: The TgpuFragmentFn or 'use gpu' callback to use as the fragment shader.
  • targets: A record defining the formats and behaviors of the color targets, similar to WebGPU’s GPUColorTargetState, but as a record with named targets.
  • depthStencil (optional): Depth-stencil state, same as WebGPU’s GPUDepthStencilState.
  • multisample (optional): Multisample state, same as WebGPU’s GPUMultisampleState.
  • primitive (optional): Primitive state, same as WebGPU’s GPUPrimitiveState.

The vertex function’s input parameters (non-builtin) are matched to vertex attributes specified in the pipeline’s vertex layout when executing. Vertex attributes are validated at the type level for compatibility.

const
const vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec2f>>
vertexLayout
=
const tgpu: {
const: typeof import("node_modules/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/src/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/src/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
.
vertexLayout: <d.WgslArray<d.Vec2f>>(schemaForCount: (count: number) => d.WgslArray<d.Vec2f>, stepMode?: "vertex" | "instance") => TgpuVertexLayout<d.WgslArray<d.Vec2f>>
vertexLayout
(
import d
d
.
arrayOf<d.Vec2f>(elementType: d.Vec2f): (elementCount: number) => d.WgslArray<d.Vec2f> (+2 overloads)
export arrayOf

@location. Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).

arrayOf
(
import d
d
.
const vec2f: d.Vec2f
vec2f
));
const
const renderPipeline: TgpuRenderPipeline<d.Vec4f>
renderPipeline
=
const root: TgpuRoot
root
.
WithBinding.createRenderPipeline<{
pos: d.Vec2f;
}, {
pos: TgpuVertexAttrib<"float32x2">;
}, {}, d.Vec4f>(descriptor: TgpuRenderPipeline<in Targets = never>.DescriptorBase & {
attribs?: {
pos: TgpuVertexAttrib<"float32x2">;
} | undefined;
vertex: TgpuVertexFn<{
pos: d.Vec2f;
}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{
pos: TgpuVertexAttrib<"float32x2">;
}>>>) => AutoVertexOut<{}>);
fragment: TgpuFragmentFn<...> | ((input: AutoFragmentIn<...>) => d.v4f | (AnyAutoCustoms & Partial<...>));
targets?: TgpuColorTargetState;
}): TgpuRenderPipeline<...> (+2 overloads)
createRenderPipeline
({
attribs?: {
pos: TgpuVertexAttrib<"float32x2">;
} | undefined
attribs
: {
pos: TgpuVertexAttrib<"float32x2">
pos
:
const vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec2f>>
vertexLayout
.
TgpuVertexLayout<WgslArray<Vec2f>>.attrib: TgpuVertexAttrib<"float32x2">
attrib
},
vertex: TgpuVertexFn<{
pos: d.Vec2f;
}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{
pos: TgpuVertexAttrib<"float32x2">;
}>>>) => AutoVertexOut<{}>)
vertex
:
const mainVertex: TgpuVertexFn<{
pos: d.Vec2f;
}, {}>
mainVertex
,
fragment: TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{}>>) => d.v4f | (AnyAutoCustoms & Partial<InferGPURecord<{
readonly $fragDepth: d.BuiltinFragDepth;
readonly $sampleMask: d.BuiltinSampleMask;
}>>))
fragment
:
const mainFragment: TgpuFragmentFn<{}, d.Vec4f>
mainFragment
,
targets?: TgpuColorTargetState
targets
: {
format?: GPUTextureFormat | undefined

The

GPUTextureFormat

of this color target. The pipeline will only be compatible with

GPURenderPassEncoder

s which use a

GPUTextureView

of this format in the corresponding color attachment.

@defaultnavigator.gpu.getPreferredCanvasFormat()

format
:
const presentationFormat: "rgba8unorm"
presentationFormat
},
// Additional options can be specified here
TgpuRenderPipeline<in Targets = never>.DescriptorBase.depthStencil?: GPUDepthStencilState | undefined

Describes the optional depth-stencil properties, including the testing, operations, and bias.

depthStencil
: {
GPUDepthStencilState.format: GPUTextureFormat

The

GPUTextureViewDescriptor#format

of

GPURenderPassDescriptor#depthStencilAttachment

this

GPURenderPipeline

will be compatible with.

format
: 'depth24plus',
GPUDepthStencilState.depthWriteEnabled?: boolean | undefined

Indicates if this

GPURenderPipeline

can modify

GPURenderPassDescriptor#depthStencilAttachment

depth values.

depthWriteEnabled
: true,
GPUDepthStencilState.depthCompare?: GPUCompareFunction | undefined

The comparison operation used to test fragment depths against

GPURenderPassDescriptor#depthStencilAttachment

depth values.

depthCompare
: 'less',
},
TgpuRenderPipeline<in Targets = never>.DescriptorBase.multisample?: GPUMultisampleState | undefined

Describes the multi-sampling properties of the pipeline.

multisample
: {
GPUMultisampleState.count?: number | undefined

Number of samples per pixel. This

GPURenderPipeline

will be compatible only with attachment textures (

GPURenderPassDescriptor#colorAttachments

and

GPURenderPassDescriptor#depthStencilAttachment

) with matching

GPUTextureDescriptor#sampleCount

s.

count
: 4,
},
TgpuRenderPipeline<in Targets = never>.DescriptorBase.primitive?: TgpuPrimitiveState

Describes the primitive-related properties of the pipeline.

primitive
: {
topology: "triangle-list"
topology
: 'triangle-list' },
});

Using the pipelines should ensure the compatibility of the vertex output and fragment input on the type level. These parameters are identified by their names, not by their numeric location index. In general, when using vertex and fragment functions with TypeGPU pipelines, it is not necessary to set locations on the IO struct properties. The library automatically matches up the corresponding members (by their names) and assigns common locations to them. When a custom location is provided by the user (via the d.location attribute function) it is respected by the automatic assignment procedure, as long as there is no conflict between vertex and fragment location values.

import {
const tgpu: {
const: typeof import("node_modules/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/src/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/src/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
,
import d
d
} from 'typegpu';
const
const vertex: TgpuVertexFn<{}, {}>
vertex
=
const tgpu: {
const: typeof import("node_modules/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/src/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/src/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
.
vertexFn: <{
pos: d.BuiltinPosition;
}>(options: {
out: {
pos: d.BuiltinPosition;
};
}) => TgpuVertexFnShell<{}, {
pos: d.BuiltinPosition;
}> (+1 overload)
vertexFn
({
out: {
pos: d.BuiltinPosition;
}
out
: {
pos: d.BuiltinPosition
pos
:
import d
d
.
const builtin: {
readonly vertexIndex: d.BuiltinVertexIndex;
readonly instanceIndex: d.BuiltinInstanceIndex;
readonly clipDistances: d.BuiltinClipDistances;
readonly position: d.BuiltinPosition;
readonly frontFacing: d.BuiltinFrontFacing;
readonly fragDepth: d.BuiltinFragDepth;
readonly primitiveIndex: d.BuiltinPrimitiveIndex;
readonly sampleIndex: d.BuiltinSampleIndex;
readonly sampleMask: d.BuiltinSampleMask;
readonly localInvocationId: d.BuiltinLocalInvocationId;
readonly localInvocationIndex: d.BuiltinLocalInvocationIndex;
... 8 more ...;
readonly numSubgroups: d.BuiltinNumSubgroups;
}
export builtin
builtin
.
position: d.BuiltinPosition
position
},
})`(...)`;
const
const fragment: TgpuFragmentFn<{
uv: d.Vec2f;
}, d.Vec4f>
fragment
=
const tgpu: {
const: typeof import("node_modules/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/src/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/src/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
.
fragmentFn: <{
uv: d.Vec2f;
}, d.Vec4f>(options: {
in: {
uv: d.Vec2f;
};
out: d.Vec4f;
}) => TgpuFragmentFnShell<{
uv: d.Vec2f;
}, d.Vec4f> (+1 overload)
fragmentFn
({
in: {
uv: d.Vec2f;
}
in
: {
uv: d.Vec2f
uv
:
import d
d
.
const vec2f: d.Vec2f
vec2f
},
out: d.Vec4f
out
:
import d
d
.
const vec4f: d.Vec4f
vec4f
,
})`(...)`;
const
const root: TgpuRoot
root
= await
const tgpu: {
const: typeof import("node_modules/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/src/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/src/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
.
init: (options?: InitOptions) => Promise<TgpuRoot>

Requests a new GPU device and creates a root around it. If a specific device should be used instead, use

@seeinitFromDevice. *

@example

When given no options, the function will ask the browser for a suitable GPU device.

const root = await tgpu.init();

@example

If there are specific options that should be used when requesting a device, you can pass those in.

const adapterOptions: GPURequestAdapterOptions = ...;
const deviceDescriptor: GPUDeviceDescriptor = ...;
const root = await tgpu.init({ adapter: adapterOptions, device: deviceDescriptor });

init
();
const root: TgpuRoot
root
.
WithBinding.createRenderPipeline<{}, {}, {}, d.Vec4f>(descriptor: TgpuRenderPipeline<in Targets = never>.DescriptorBase & {
attribs?: {} | undefined;
vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<{}>);
fragment: TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{}>>) => d.v4f | (AnyAutoCustoms & Partial<...>));
targets?: TgpuColorTargetState;
}): TgpuRenderPipeline<...> (+2 overloads)
createRenderPipeline
({
vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<{}>)
vertex
,
fragment,
Error ts(2769) ― No overload matches this call. Overload 3 of 3, '(descriptor: DescriptorBase & ({ attribs?: {} | undefined; vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<...>); fragment: ((input: AutoFragmentIn<...>) => v4f | (AnyAutoCustoms & Partial<...>)) | TgpuFragmentFn<...>; targets?: TgpuColorTargetState; } | { ...; })): TgpuRenderPipeline<...> | TgpuRenderPipeline<...>', gave the following error. Type 'TgpuFragmentFn<{ uv: Vec2f; }, Vec4f>' is not assignable to type '((input: AutoFragmentIn<InferGPURecord<{}>>) => v4f | (AnyAutoCustoms & Partial<InferGPURecord<{ readonly $fragDepth: BuiltinFragDepth; readonly $sampleMask: BuiltinSampleMask; }>>)) | TgpuFragmentFn<...> | TgpuFragmentFn<...> | ((input: AutoFragmentIn<...>) => undefined) | undefined'. Type 'TgpuFragmentFn<{ uv: Vec2f; }, Vec4f>' is not assignable to type 'TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, Vec4f>'. Property 'uv' is missing in type '{} & Record<string, AnyFragmentInputBuiltin>' but required in type '{ uv: Vec2f; }'.
targets?: TgpuColorTargetState
targets
: {
format?: GPUTextureFormat | undefined

The

GPUTextureFormat

of this color target. The pipeline will only be compatible with

GPURenderPassEncoder

s which use a

GPUTextureView

of this format in the corresponding color attachment.

@defaultnavigator.gpu.getPreferredCanvasFormat()

format
: 'bgra8unorm' },
});

The createComputePipeline method creates a compute pipeline by accepting an options object with the compute function.

  • compute: The TgpuComputeFn to use as the compute shader.
const
const computePipeline: TgpuComputePipeline
computePipeline
=
const root: TgpuRoot
root
.
WithBinding.createComputePipeline<{}>(descriptor: TgpuComputePipeline.Descriptor<{}>): TgpuComputePipeline
createComputePipeline
({
compute: TgpuComputeFn<{}>
compute
:
const mainCompute: TgpuComputeFn<{}>
mainCompute
,
});

The createGuardedComputePipeline method streamlines running simple computations on the GPU. Instead of dispatching workgroups, the guarded pipeline allows calling an exact number of GPU threads. Think of it as a parallelized for loop. Under the hood, it creates a compute pipeline that calls the provided callback only if the current thread ID is within the requested range.

const
const data: TgpuMutable<d.WgslArray<d.U32>>
data
=
const root: TgpuRoot
root
.
TgpuRoot.createMutable<d.WgslArray<d.U32>>(typeSchema: d.WgslArray<d.U32>, initial?: ((buffer: TgpuBuffer<NoInfer<d.WgslArray<d.U32>>>) => void) | d.InferInput<NoInfer<d.WgslArray<d.U32>>> | undefined): TgpuMutable<d.WgslArray<d.U32>> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader. Can be mutated in-place on the GPU. For a general-purpose buffer, use

TgpuRoot.createBuffer

.

@paramtypeSchema The type of data that this buffer will hold.

@paraminitial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createMutable
(
import d
d
.
arrayOf<d.U32>(elementType: d.U32, elementCount: number): d.WgslArray<d.U32> (+2 overloads)
export arrayOf

@location. Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).

arrayOf
(
import d
d
.
const u32: d.U32
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
, 8), [0, 1, 2, 3, 4, 5, 6, 7]);
const
const doubleUpPipeline: TgpuGuardedComputePipeline<[x: number]>
doubleUpPipeline
=
const root: TgpuRoot
root
.
WithBinding.createGuardedComputePipeline<[x: number]>(callback: (x: number) => void): TgpuGuardedComputePipeline<[x: number]>

Creates a compute pipeline that executes the given callback in an exact number of threads. This is different from createComputePipeline() in that it does a bounds check on the thread id, where as regular pipelines do not and work in units of workgroups.

@paramcallback A function converted to WGSL and executed on the GPU. It can accept up to 3 parameters (x, y, z) which correspond to the global invocation ID of the executing thread.

@example

If no parameters are provided, the callback will be executed once, in a single thread.

const fooPipeline = root
.createGuardedComputePipeline(() => {
'use gpu';
console.log('Hello, GPU!');
});
fooPipeline.dispatchThreads();
// [GPU] Hello, GPU!

@example

One parameter means n-threads will be executed in parallel.

const fooPipeline = root
.createGuardedComputePipeline((x) => {
'use gpu';
if (x % 16 === 0) {
// Logging every 16th thread
console.log('I am the', x, 'thread');
}
});
// executing 512 threads
fooPipeline.dispatchThreads(512);
// [GPU] I am the 256 thread
// [GPU] I am the 272 thread
// ... (30 hidden logs)
// [GPU] I am the 16 thread
// [GPU] I am the 240 thread

createGuardedComputePipeline
((
x: number
x
) => {
'use gpu';
const data: TgpuMutable<d.WgslArray<d.U32>>
data
.
TgpuMutable<WgslArray<U32>>.$: number[]
$
[
x: number
x
] *= 2;
});
const doubleUpPipeline: TgpuGuardedComputePipeline<[x: number]>
doubleUpPipeline
.
TgpuGuardedComputePipeline<[x: number]>.dispatchThreads(x: number): void

Dispatches the pipeline. Unlike TgpuComputePipeline.dispatchWorkgroups(), this method takes in the number of threads to run in each dimension.

Under the hood, the number of expected threads is sent as a uniform, and "guarded" by a bounds check.

dispatchThreads
(8);
const doubleUpPipeline: TgpuGuardedComputePipeline<[x: number]>
doubleUpPipeline
.
TgpuGuardedComputePipeline<[x: number]>.dispatchThreads(x: number): void

Dispatches the pipeline. Unlike TgpuComputePipeline.dispatchWorkgroups(), this method takes in the number of threads to run in each dimension.

Under the hood, the number of expected threads is sent as a uniform, and "guarded" by a bounds check.

dispatchThreads
(8);
const doubleUpPipeline: TgpuGuardedComputePipeline<[x: number]>
doubleUpPipeline
.
TgpuGuardedComputePipeline<[x: number]>.dispatchThreads(x: number): void

Dispatches the pipeline. Unlike TgpuComputePipeline.dispatchWorkgroups(), this method takes in the number of threads to run in each dimension.

Under the hood, the number of expected threads is sent as a uniform, and "guarded" by a bounds check.

dispatchThreads
(5);
// the command encoder will queue the read after `doubleUpPipeline`
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(await
const data: TgpuMutable<d.WgslArray<d.U32>>
data
.
TgpuBufferBindingBase<WgslArray<U32>>.read(): Promise<number[]>
read
()); // [0, 8, 16, 24, 32, 20, 24, 28]

The callback can have up to three arguments (dimensions). createGuardedComputePipeline can simplify writing a pipeline helping reduce serialization overhead when initializing buffers with data. Buffer initialization commonly uses random number generators. For that, you can use the @typegpu/noise library.

import {
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
sample: typeof randFloat01;
sampleExclusive: typeof randUniformExclusive;
normal: typeof randNormal;
exponential: typeof randExponential;
cauchy: typeof randCauchy;
bernoulli: typeof randBernoulli;
... 7 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
} from '@typegpu/noise';
const
const root: TgpuRoot
root
= await
const tgpu: {
const: typeof import("node_modules/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("node_modules/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("node_modules/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("node_modules/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("node_modules/typegpu/src/core/root/init").init;
initFromDevice: typeof import("node_modules/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("node_modules/typegpu/src/core/slot/slot").slot;
lazy: typeof import("node_modules/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("node_modules/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
.
init: (options?: InitOptions) => Promise<TgpuRoot>

Requests a new GPU device and creates a root around it. If a specific device should be used instead, use

@seeinitFromDevice. *

@example

When given no options, the function will ask the browser for a suitable GPU device.

const root = await tgpu.init();

@example

If there are specific options that should be used when requesting a device, you can pass those in.

const adapterOptions: GPURequestAdapterOptions = ...;
const deviceDescriptor: GPUDeviceDescriptor = ...;
const root = await tgpu.init({ adapter: adapterOptions, device: deviceDescriptor });

init
();
// buffer of 1024x512 floats
const
const waterLevelMutable: TgpuMutable<d.WgslArray<d.WgslArray<d.F32>>>
waterLevelMutable
=
const root: TgpuRoot
root
.
TgpuRoot.createMutable<d.WgslArray<d.WgslArray<d.F32>>>(typeSchema: d.WgslArray<d.WgslArray<d.F32>>, initial?: ((buffer: TgpuBuffer<NoInfer<d.WgslArray<d.WgslArray<d.F32>>>>) => void) | d.InferInput<NoInfer<d.WgslArray<d.WgslArray<d.F32>>>> | undefined): TgpuMutable<d.WgslArray<d.WgslArray<d.F32>>> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader. Can be mutated in-place on the GPU. For a general-purpose buffer, use

TgpuRoot.createBuffer

.

@paramtypeSchema The type of data that this buffer will hold.

@paraminitial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createMutable
(
import d
d
.
arrayOf<d.WgslArray<d.F32>>(elementType: d.WgslArray<d.F32>, elementCount: number): d.WgslArray<d.WgslArray<d.F32>> (+2 overloads)
export arrayOf

@location. Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).

arrayOf
(
import d
d
.
arrayOf<d.F32>(elementType: d.F32, elementCount: number): d.WgslArray<d.F32> (+2 overloads)
export arrayOf

@location. Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).

arrayOf
(
import d
d
.
const f32: d.F32
export f32

A schema that represents a 32-bit float value. (equivalent to f32 in WGSL)

Can also be called to cast a value to an f32.

@example const value = f32(); // 0

@example const value = f32(1.23); // 1.23

@example const value = f32(true); // 1

f32
, 512), 1024),
);
const root: TgpuRoot
root
.
WithBinding.createGuardedComputePipeline<[x: number, y: number]>(callback: (x: number, y: number) => void): TgpuGuardedComputePipeline<[x: number, y: number]>

Creates a compute pipeline that executes the given callback in an exact number of threads. This is different from createComputePipeline() in that it does a bounds check on the thread id, where as regular pipelines do not and work in units of workgroups.

@paramcallback A function converted to WGSL and executed on the GPU. It can accept up to 3 parameters (x, y, z) which correspond to the global invocation ID of the executing thread.

@example

If no parameters are provided, the callback will be executed once, in a single thread.

const fooPipeline = root
.createGuardedComputePipeline(() => {
'use gpu';
console.log('Hello, GPU!');
});
fooPipeline.dispatchThreads();
// [GPU] Hello, GPU!

@example

One parameter means n-threads will be executed in parallel.

const fooPipeline = root
.createGuardedComputePipeline((x) => {
'use gpu';
if (x % 16 === 0) {
// Logging every 16th thread
console.log('I am the', x, 'thread');
}
});
// executing 512 threads
fooPipeline.dispatchThreads(512);
// [GPU] I am the 256 thread
// [GPU] I am the 272 thread
// ... (30 hidden logs)
// [GPU] I am the 16 thread
// [GPU] I am the 240 thread

createGuardedComputePipeline
((
x: number
x
,
y: number
y
) => {
'use gpu';
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
sample: typeof randFloat01;
sampleExclusive: typeof randUniformExclusive;
normal: typeof randNormal;
exponential: typeof randExponential;
cauchy: typeof randCauchy;
bernoulli: typeof randBernoulli;
... 7 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
seed2: (seed: d.v2f) => void

Threads do not share the generator's State. As a result, unless you change the seed in each thread, each thread will produce the same sequence. randf.randSeed2 sets the private seed of the thread.

@paramseed seed value to set. For the best results, all elements should be in [-1000, 1000] range.

seed2
(
import d
d
.
const vec2f: d.Vec2f
(x: number, y: number) => d.v2f (+3 overloads)
vec2f
(
x: number
x
,
y: number
y
).
vecInfixNotation<v2f>.div(other: number | d.v2f): d.v2f
div
(1024));
const waterLevelMutable: TgpuMutable<d.WgslArray<d.WgslArray<d.F32>>>
waterLevelMutable
.
TgpuMutable<WgslArray<WgslArray<F32>>>.$: number[][]
$
[
x: number
x
][
y: number
y
] = 10 +
const randf: {
seed: typeof randSeed;
seed2: typeof randSeed2;
seed3: typeof randSeed3;
seed4: typeof randSeed4;
sample: typeof randFloat01;
sampleExclusive: typeof randUniformExclusive;
normal: typeof randNormal;
exponential: typeof randExponential;
cauchy: typeof randCauchy;
bernoulli: typeof randBernoulli;
... 7 more ...;
onUnitSphere: typeof randOnUnitSphere;
}
randf
.
sample: () => number

Returns a random f32 value in [0, 1) range.

sample
();
}).
TgpuGuardedComputePipeline<[x: number, y: number]>.dispatchThreads(x: number, y: number): void

Dispatches the pipeline. Unlike TgpuComputePipeline.dispatchWorkgroups(), this method takes in the number of threads to run in each dimension.

Under the hood, the number of expected threads is sent as a uniform, and "guarded" by a bounds check.

dispatchThreads
(1024, 512);
// callback will be called for x in range 0..1023 and y in range 0..511
// (optional) read values in JS
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err

@seesource

console
.
Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100

log
(await
const waterLevelMutable: TgpuMutable<d.WgslArray<d.WgslArray<d.F32>>>
waterLevelMutable
.
TgpuBufferBindingBase<WgslArray<WgslArray<F32>>>.read(): Promise<number[][]>
read
());

Pipeline initialization involves resolving the pipeline code, creating the shader module, and creating the underlying WebGPU pipeline. This happens automatically the first time the pipeline is executed via .draw, .dispatchWorkgroups, or a similar method. The initSync method lets you start the initialization early. To wait until initialization actually finishes on the device (fully avoiding a stall on first execution), use initAsync instead.

// Automatically calls `pipeline.initSync();`,
// then enqueues a dispatch.
const pipeline: TgpuComputePipeline
pipeline
.
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number, z?: number): void
dispatchWorkgroups
(1);
// If not already initialized, runs JS initialization,
// and issues pipeline initialization steps on the device.
const pipeline: TgpuComputePipeline
pipeline
.
TgpuComputePipeline.initSync(): void

Immediately resolves the pipeline and creates WebGPU resources. NOTE: it is not necessary to initialize pipelines manually.

initSync
();
// Enqueues a dispatch.
// Stall partially avoided because the pipeline is already resolved.
const pipeline: TgpuComputePipeline
pipeline
.
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number, z?: number): void
dispatchWorkgroups
(1);
// If not already initialized, runs JS initialization,
// and issues pipeline initialization steps on the device.
const
const initPromise: Promise<void>
initPromise
=
const pipeline: TgpuComputePipeline
pipeline
.
TgpuComputePipeline.initAsync(): Promise<void>

Immediately resolves the pipeline, then awaits device.createComputePipelineAsync(). NOTE: it is not necessary to initialize pipelines manually.

initAsync
();
// Awaits until the initialization finishes.
await
const initPromise: Promise<void>
initPromise
;
// Enqueues a dispatch.
// Stall avoided, the pipeline is already resolved and initialized on the device.
const pipeline: TgpuComputePipeline
pipeline
.
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number, z?: number): void
dispatchWorkgroups
(1);
renderPipeline
.withColorAttachment({ view: context })
.draw(3);
computePipeline.dispatchWorkgroups(16);
guardedComputePipeline.dispatchThreads(4);

Render pipelines require specifying a color attachment for each target. The attachments are specified in the same way as in the WebGPU API (but accept both TypeGPU resources and regular WebGPU ones). However, similar to the targets argument, multiple targets need to be passed in as a record, with each target identified by name.

Similarly, when using withDepthStencil it is necessary to pass in a depth stencil attachment, via the withDepthStencilAttachment method.

renderPipeline
.withColorAttachment({
color: {
view: msaaTextureView,
resolveTarget: context,
loadOp: 'clear',
storeOp: 'store',
},
shadow: {
view: shadowTextureView,
clearValue: [1, 1, 1, 1],
loadOp: 'clear',
storeOp: 'store',
},
})
.withDepthStencilAttachment({
view: depthTextureView,
depthClearValue: 1,
depthLoadOp: 'clear',
depthStoreOp: 'store',
})
.draw(vertexCount);

Before executing pipelines, it is necessary to bind all of the utilized resources, like bind groups, vertex buffers and slots. It is done using the with method. It accepts either a bind group (render and compute pipelines) or a vertex layout and a vertex buffer (render pipelines only).

// vertex layout
const vertexLayout = tgpu.vertexLayout(
d.disarrayOf(d.float16),
'vertex',
);
const vertexBuffer = root
.createBuffer(d.disarrayOf(d.float16, 8), [0, 0, 1, 0, 0, 1, 1, 1])
.$usage('vertex');
// bind group layout
const bindGroupLayout = tgpu.bindGroupLayout({
size: { uniform: d.vec2u },
});
const sizeBuffer = root
.createBuffer(d.vec2u, d.vec2u(64, 64))
.$usage('uniform');
const bindGroup = root.createBindGroup(bindGroupLayout, {
size: sizeBuffer,
});
// binding and execution
renderPipeline
.with(vertexLayout, vertexBuffer)
.with(bindGroup)
.draw(8);
computePipeline
.with(bindGroup)
.dispatchWorkgroups(1);

Pipelines also expose the withPerformanceCallback and withTimestampWrites methods for timing the execution time on the GPU. For more info about them, refer to the Timing Your Pipelines guide.

After creating the render pipeline and setting all of the attachments, it can be put to use by calling the draw method. It accepts the number of vertices and optionally the instance count, first vertex index and first instance index. After calling the method, the shader is set for execution immediately.

Compute pipelines are executed using the dispatchWorkgroups method, which accepts the number of workgroups in each dimension.

The drawIndexed is analogous to draw, but takes advantage of index buffer to explicitly map vertex data onto primitives. When using an index buffer, you don’t need to list every vertex for every primitive explicitly. Instead, you provide a list of unique vertices in a vertex buffer. Then, the index buffer defines how these vertices are connected to form primitives.

const
const indexBuffer: TgpuBuffer<d.WgslArray<d.U16>> & IndexFlag
indexBuffer
=
const root: TgpuRoot
root
.
TgpuRoot.createBuffer<d.WgslArray<d.U16>>(typeSchema: d.WgslArray<d.U16>, initial?: ((buffer: TgpuBuffer<NoInfer<d.WgslArray<d.U16>>>) => void) | d.InferInput<NoInfer<d.WgslArray<d.U16>>> | undefined): TgpuBuffer<d.WgslArray<d.U16>> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader.

@remarksTyped wrapper around a GPUBuffer.

@paramtypeSchema The type of data that this buffer will hold.

@paraminitial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createBuffer
(
import d
d
.
arrayOf<d.U16>(elementType: d.U16, elementCount: number): d.WgslArray<d.U16> (+2 overloads)
export arrayOf

@location. Wrap align/size in a struct instead, e.g. d.arrayOf(d.struct({ value: d.align(16, d.u32) }), n).

arrayOf
(
import d
d
.
const u16: d.U16
export u16
u16
, 6), [0, 2, 1, 0, 3, 2])
.
TgpuBuffer<WgslArray<U16>>.$usage<["index"]>(usages_0: "index"): TgpuBuffer<d.WgslArray<d.U16>> & IndexFlag
$usage
('index');
const
const pipeline: TgpuRenderPipeline<d.Vec4f> & HasIndexBuffer
pipeline
=
const root: TgpuRoot
root
.
WithBinding.createRenderPipeline<{
color: d.Vec4f;
}, {
color: TgpuVertexAttrib<"float32x4">;
}, {
color: d.Vec4f;
}, d.Vec4f>(descriptor: TgpuRenderPipeline<in Targets = never>.DescriptorBase & {
attribs?: {
color: TgpuVertexAttrib<"float32x4">;
} | undefined;
vertex: TgpuVertexFn<{
color: d.Vec4f;
}, {
color: d.Vec4f;
}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{
color: TgpuVertexAttrib<"float32x4">;
}>>>) => AutoVertexOut<...>);
fragment: TgpuFragmentFn<...> | ((input: AutoFragmentIn<...>) => d.v4f | (AnyAutoCustoms & Partial<...>));
targets?: TgpuColorTargetState;
}): TgpuRenderPipeline<...> (+2 overloads)
createRenderPipeline
({
attribs?: {
color: TgpuVertexAttrib<"float32x4">;
} | undefined
attribs
: {
color: TgpuVertexAttrib<"float32x4">
color
:
const vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec4f>>
vertexLayout
.
TgpuVertexLayout<WgslArray<Vec4f>>.attrib: TgpuVertexAttrib<"float32x4">
attrib
},
vertex: TgpuVertexFn<{
color: d.Vec4f;
}, {
color: d.Vec4f;
}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{
color: TgpuVertexAttrib<"float32x4">;
}>>>) => AutoVertexOut<AnyAutoCustoms>)
vertex
,
fragment: TgpuFragmentFn<{
color: d.Vec4f;
} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{
color: d.Vec4f;
}>>) => d.v4f | (AnyAutoCustoms & Partial<InferGPURecord<{
readonly $fragDepth: d.BuiltinFragDepth;
readonly $sampleMask: d.BuiltinSampleMask;
}>>))
fragment
:
const mainFragment: TgpuFragmentFn<{
color: d.Vec4f;
}, d.Vec4f>
mainFragment
,
targets?: TgpuColorTargetState
targets
: {
format?: GPUTextureFormat | undefined

The

GPUTextureFormat

of this color target. The pipeline will only be compatible with

GPURenderPassEncoder

s which use a

GPUTextureView

of this format in the corresponding color attachment.

@defaultnavigator.gpu.getPreferredCanvasFormat()

format
:
const presentationFormat: "rgba8unorm"
presentationFormat
},
})
.
TgpuRenderPipeline<Vec4f>.withIndexBuffer(buffer: TgpuBuffer<d.BaseData> & IndexFlag, offsetElements?: number, sizeElements?: number): TgpuRenderPipeline<d.Vec4f> & HasIndexBuffer (+1 overload)
withIndexBuffer
(
const indexBuffer: TgpuBuffer<d.WgslArray<d.U16>> & IndexFlag
indexBuffer
);
const pipeline: TgpuRenderPipeline<d.Vec4f> & HasIndexBuffer
pipeline
.
TgpuRenderPipeline<Vec4f>.with<d.WgslArray<d.Vec4f>>(vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec4f>>, buffer: GPUBuffer | (TgpuBuffer<d.WgslArray<d.Vec4f>> & VertexFlag)): TgpuRenderPipeline<d.Vec4f> & HasIndexBuffer (+8 overloads)
with
(
const vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec4f>>
vertexLayout
,
const colorBuffer: TgpuBuffer<d.WgslArray<d.Vec4f>> & VertexFlag
colorBuffer
)
.
HasIndexBuffer.drawIndexed(indexCount: number, instanceCount?: number, firstIndex?: number, baseVertex?: number, firstInstance?: number): void
drawIndexed
(6);

Indirect methods read their execution parameters from a buffer, which allows a previous GPU operation to determine the amount of later work. Mark a typed buffer with .$usage('indirect') and use one of:

MethodConsecutive values read from the buffer
dispatchWorkgroupsIndirectx: u32, y: u32, z: u32
drawIndirectvertexCount: u32, instanceCount: u32, firstVertex: u32, firstInstance: u32
drawIndexedIndirectindexCount: u32, instanceCount: u32, firstIndex: u32, baseVertex: i32, firstInstance: u32
const
const DispatchArgs: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>
DispatchArgs
=
import d
d
.
struct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>(props: {
x: d.U32;
y: d.U32;
z: d.U32;
}): d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>
export struct

Creates a struct schema that can be used to construct GPU buffers. Ensures proper alignment and padding of properties (as opposed to a d.unstruct schema). The order of members matches the passed in properties object.

@example const CircleStruct = d.struct({ radius: d.f32, pos: d.vec3f });

@paramprops Record with string keys and TgpuData values, each entry describing one struct member.

struct
({
x: d.U32
x
:
import d
d
.
const u32: d.U32
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
,
y: d.U32
y
:
import d
d
.
const u32: d.U32
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
,
z: d.U32
z
:
import d
d
.
const u32: d.U32
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
,
});
const
const dispatchArgs: TgpuBuffer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>> & IndirectFlag
dispatchArgs
=
const root: TgpuRoot
root
.
TgpuRoot.createBuffer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>>(typeSchema: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>, initial?: ((buffer: TgpuBuffer<NoInfer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>>>) => void) | d.InferInput<NoInfer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>>> | undefined): TgpuBuffer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader.

@remarksTyped wrapper around a GPUBuffer.

@paramtypeSchema The type of data that this buffer will hold.

@paraminitial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createBuffer
(
const DispatchArgs: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>
DispatchArgs
, {
x: number
x
: 16,
y: number
y
: 8,
z: number
z
: 1 })
.
TgpuBuffer<WgslStruct<{ x: U32; y: U32; z: U32; }>>.$usage<["indirect"]>(usages_0: "indirect"): TgpuBuffer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>> & IndirectFlag
$usage
('indirect');
const computePipeline: TgpuComputePipeline
computePipeline
.
TgpuComputePipeline.dispatchWorkgroupsIndirect<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>>(indirectBuffer: (TgpuBuffer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>> & IndirectFlag) | GPUBuffer, start?: PrimitiveOffsetInfo | number): void

Dispatches compute workgroups using parameters read from a buffer. The buffer must contain 3 consecutive u32 values (x, y, z workgroup counts). To get the correct offset within complex data structures, use d.memoryLayoutOf(...).

@paramindirectBuffer - Buffer marked with 'indirect' usage containing dispatch parameters or raw GPUBuffer

@paramstart - PrimitiveOffsetInfo pointing to the first dispatch parameter. If not provided, starts at offset 0. To obtain safe offsets, use d.memoryLayoutOf(...).

dispatchWorkgroupsIndirect
(
const dispatchArgs: TgpuBuffer<d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>> & IndirectFlag
dispatchArgs
);

For an argument block nested inside a larger schema, pass the offset returned by d.memoryLayoutOf:

const
const FrameCommands: d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>
FrameCommands
=
import d
d
.
struct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>(props: {
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}): d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>
export struct

Creates a struct schema that can be used to construct GPU buffers. Ensures proper alignment and padding of properties (as opposed to a d.unstruct schema). The order of members matches the passed in properties object.

@example const CircleStruct = d.struct({ radius: d.f32, pos: d.vec3f });

@paramprops Record with string keys and TgpuData values, each entry describing one struct member.

struct
({
frameIndex: d.U32
frameIndex
:
import d
d
.
const u32: d.U32
export u32

A schema that represents an unsigned 32-bit integer value. (equivalent to u32 in WGSL)

Can also be called to cast a value to an u32 in accordance with WGSL casting rules.

@example const value = u32(); // 0

@example const value = u32(7); // 7

@example const value = u32(3.14); // 3

@example const value = u32(-1); // 4294967295

@example const value = u32(-3.1); // 0

u32
,
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>
dispatch
:
const DispatchArgs: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>
DispatchArgs
,
});
const
const commands: TgpuBuffer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>> & IndirectFlag
commands
=
const root: TgpuRoot
root
.
TgpuRoot.createBuffer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>>(typeSchema: d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>, initial?: ((buffer: TgpuBuffer<NoInfer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>>>) => void) | d.InferInput<NoInfer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>>> | undefined): TgpuBuffer<...> (+1 overload)

Allocates memory on the GPU, allows passing data between host and shader.

@remarksTyped wrapper around a GPUBuffer.

@paramtypeSchema The type of data that this buffer will hold.

@paraminitial Either initial value of the buffer, or an initializer to execute on the mapped buffer. (optional)

createBuffer
(
const FrameCommands: d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>
FrameCommands
).
TgpuBuffer<WgslStruct<{ frameIndex: U32; dispatch: WgslStruct<{ x: U32; y: U32; z: U32; }>; }>>.$usage<["indirect"]>(usages_0: "indirect"): TgpuBuffer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>> & IndirectFlag
$usage
('indirect');
const
const dispatchOffset: PrimitiveOffsetInfo
dispatchOffset
=
import d
d
.
memoryLayoutOf<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>>(schema: d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>, accessor?: ((proxy: {
frameIndex: number;
dispatch: {
x: number;
y: number;
z: number;
};
}) => unknown) | undefined): PrimitiveOffsetInfo
export memoryLayoutOf

A function that retrieves offset and information for a specific primitive within a data schema. Example usage:

const Boid = d.struct({
position: d.vec3f,
velocity: d.vec3f,
});
const memLayout = d.memoryLayoutOf(Boid, (b) => b.velocity.y);
console.log(memLayout.offset); // Byte offset of velocity.y within Boid (here 20 bytes)
console.log(memLayout.contiguous); // Contiguous bytes available from that offset (here 8 bytes)

@paramschema - The data schema to analyze.

@paramaccessor - Optional function that accesses a specific element within the schema. If omitted, uses the root offset (0).

@returnsAn object containing the offset and contiguous byte information.

memoryLayoutOf
(
const FrameCommands: d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>
FrameCommands
, (
value: {
frameIndex: number;
dispatch: {
x: number;
y: number;
z: number;
};
}
value
) =>
value: {
frameIndex: number;
dispatch: {
x: number;
y: number;
z: number;
};
}
value
.
dispatch: {
x: number;
y: number;
z: number;
}
dispatch
);
const computePipeline: TgpuComputePipeline
computePipeline
.
TgpuComputePipeline.dispatchWorkgroupsIndirect<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>>(indirectBuffer: (TgpuBuffer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>> & IndirectFlag) | GPUBuffer, start?: PrimitiveOffsetInfo | number): void

Dispatches compute workgroups using parameters read from a buffer. The buffer must contain 3 consecutive u32 values (x, y, z workgroup counts). To get the correct offset within complex data structures, use d.memoryLayoutOf(...).

@paramindirectBuffer - Buffer marked with 'indirect' usage containing dispatch parameters or raw GPUBuffer

@paramstart - PrimitiveOffsetInfo pointing to the first dispatch parameter. If not provided, starts at offset 0. To obtain safe offsets, use d.memoryLayoutOf(...).

dispatchWorkgroupsIndirect
(
const commands: TgpuBuffer<d.WgslStruct<{
frameIndex: d.U32;
dispatch: d.WgslStruct<{
x: d.U32;
y: d.U32;
z: d.U32;
}>;
}>> & IndirectFlag
commands
,
const dispatchOffset: PrimitiveOffsetInfo
dispatchOffset
);

Raw GPUBuffer objects are also accepted. Offsets must be aligned to four bytes, and the required values must fit in a contiguous region of the buffer. drawIndexedIndirect additionally requires an index buffer bound with .withIndexBuffer(...).

Render and compute pipelines can record commands into encoders that your application manages. Pass an existing encoder to .with(...) before drawing or dispatching:

const
const encoder: GPUCommandEncoder
encoder
=
const root: TgpuRoot
root
.
TgpuRoot.device: GPUDevice

The GPU device associated with this root.

device
.
GPUDevice.createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): GPUCommandEncoder (+1 overload)

Creates a

GPUCommandEncoder

.

@paramdescriptor - Description of the GPUCommandEncoder to create.

createCommandEncoder
();
const computePipeline: TgpuComputePipeline
computePipeline
.
TgpuComputePipeline.with(encoder: GPUCommandEncoder): TgpuComputePipeline (+6 overloads)
with
(
const encoder: GPUCommandEncoder
encoder
)
.
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number, z?: number): void
dispatchWorkgroups
(16);
const renderPipeline: TgpuRenderPipeline<d.Vec4f>
renderPipeline
.
TgpuRenderPipeline<Vec4f>.with(encoder: GPUCommandEncoder): TgpuRenderPipeline<d.Vec4f> (+8 overloads)
with
(
const encoder: GPUCommandEncoder
encoder
)
.
TgpuRenderPipeline<Vec4f>.withColorAttachment(attachment: ColorAttachment): TgpuRenderPipeline<d.Vec4f>

Attaches texture views to the pipeline's targets (outputs).

@example // Draw 3 vertices onto the context's canvas pipeline .withColorAttachment({ view: context }) .draw(3)

@paramattachment The object should match the shape returned by the fragment shader, with values matching the ColorAttachment type.

withColorAttachment
({
ColorAttachment.view: GPUCanvasContext | (ColorTextureConstraint & RenderFlag) | GPUTextureView | TgpuTextureView<d.WgslTexture<WgslTextureProps>> | TgpuTextureRenderView

A

GPUTextureView

describing the texture subresource that will be output to for this color attachment.

view
:
const context: GPUCanvasContext
context
})
.
TgpuRenderPipeline<Vec4f>.draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void
draw
(3);
const root: TgpuRoot
root
.
TgpuRoot.device: GPUDevice

The GPU device associated with this root.

device
.
GPUDevice.queue: GPUQueue

The queue read-only property of the GPUDevice interface returns the primary GPUQueue for the device.

MDN Reference

The primary

GPUQueue

for this device.

queue
.
GPUQueue.submit(commandBuffers: Iterable<GPUCommandBuffer>): undefined (+2 overloads)

Schedules the execution of the command buffers by the GPU on this queue. Submitted command buffers cannot be used again. commandBuffers:

submit
([
const encoder: GPUCommandEncoder
encoder
.
GPUCommandEncoder.finish(descriptor?: GPUCommandBufferDescriptor): GPUCommandBuffer (+1 overload)

Completes recording of the commands sequence and returns a corresponding

GPUCommandBuffer

. descriptor:

finish
()]);

With a GPUCommandEncoder, TypeGPU creates and ends the needed pass but leaves finishing and submitting the command buffer to you.

You can instead record directly into an existing pass:

const
const computePass: GPUComputePassEncoder
computePass
=
const encoder: GPUCommandEncoder
encoder
.
GPUCommandEncoder.beginComputePass(descriptor?: GPUComputePassDescriptor): GPUComputePassEncoder (+1 overload)

The beginComputePass() method of the GPUCommandEncoder interface starts encoding a compute pass, returning a GPUComputePassEncoder that can be used to control computation.

MDN Reference

beginComputePass
();
const computePipeline: TgpuComputePipeline
computePipeline
.
TgpuComputePipeline.with(pass: GPUComputePassEncoder): TgpuComputePipeline (+6 overloads)
with
(
const computePass: GPUComputePassEncoder
computePass
).
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number, z?: number): void
dispatchWorkgroups
(16);
const computePass: GPUComputePassEncoder
computePass
.
GPUComputePassEncoder.end(): undefined (+1 overload)

Completes recording of the compute pass commands sequence.

end
();
const
const renderPass: GPURenderPassEncoder
renderPass
=
const encoder: GPUCommandEncoder
encoder
.
GPUCommandEncoder.beginRenderPass(descriptor: GPURenderPassDescriptor): GPURenderPassEncoder (+1 overload)

Begins encoding a render pass described by descriptor.

@paramdescriptor - Description of the GPURenderPassEncoder to create.

beginRenderPass
(
const renderPassDescriptor: GPURenderPassDescriptor
renderPassDescriptor
);
const renderPipeline: TgpuRenderPipeline<d.Vec4f>
renderPipeline
.
TgpuRenderPipeline<Vec4f>.with(pass: GPURenderPassEncoder): TgpuRenderPipeline<d.Vec4f> (+8 overloads)
with
(
const renderPass: GPURenderPassEncoder
renderPass
).
TgpuRenderPipeline<Vec4f>.draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void
draw
(3);
const renderPass: GPURenderPassEncoder
renderPass
.
GPURenderPassEncoder.end(): undefined (+1 overload)

Completes recording of the render pass commands sequence.

end
();

When passed a GPUComputePassEncoder or GPURenderPassEncoder, TypeGPU applies its pipeline and bind-group state to that pass without ending it. Render pipelines also accept a GPURenderBundleEncoder, allowing .draw(...), .drawIndexed(...), and their indirect variants to be recorded in a render bundle. The caller remains responsible for ending the pass or finishing the bundle.

When a pipeline is executed directly via draw or dispatchWorkgroups, it records its own pass and submits it to the GPU queue immediately. For scenarios that require more control, such as executing multiple pipelines in a single render pass or batching multiple passes into a single submission, TypeGPU provides a typed equivalent of the WebGPU command encoder. It can be created with the createCommandEncoder method on the root object and mirrors GPUCommandEncoder, while accepting TypeGPU resources directly.

const
const encoder: TgpuCommandEncoder
encoder
=
const root: TgpuRoot
root
['~unstable'].
function createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): TgpuCommandEncoder

Creates a

TgpuCommandEncoder

for batching multiple render/compute passes (and draws within them) into a single submission.

@example

const encoder = root['~unstable'].createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{ view: msaaTexture, resolveTarget: context }],
});
scenePipeline.with(pass).draw(vertexCount);
skyPipeline.with(pass).draw(3);
pass.end();
encoder.submit();

createCommandEncoder
();
const
const pass: TgpuRenderPass
pass
=
const encoder: TgpuCommandEncoder
encoder
.
TgpuCommandEncoder.beginRenderPass(descriptor: TgpuRenderPassDescriptor): TgpuRenderPass

Begins recording a render pass. Attachment views accept TypeGPU textures, texture views and canvas contexts, next to raw

GPUTextureView

s.

beginRenderPass
({
TgpuRenderPassDescriptor.colorAttachments?: ColorAttachment | readonly (ColorAttachment | null)[] | undefined
colorAttachments
: [{
ColorAttachment.view: (ColorTextureConstraint & RenderFlag) | GPUTextureView | TgpuTextureView<d.WgslTexture<WgslTextureProps>> | TgpuTextureRenderView | GPUCanvasContext

A

GPUTextureView

describing the texture subresource that will be output to for this color attachment.

view
:
const msaaTexture: TgpuTexture<{
size: [256, 256];
format: "rgba8unorm";
sampleCount: 4;
}> & RenderFlag
msaaTexture
,
ColorAttachment.resolveTarget?: (ColorTextureConstraint & RenderFlag) | GPUTextureView | TgpuTextureView<d.WgslTexture<WgslTextureProps>> | TgpuTextureRenderView | GPUCanvasContext | undefined

A

GPUTextureView

describing the texture subresource that will receive the resolved output for this color attachment if

GPURenderPassColorAttachment#view

is multisampled.

resolveTarget
:
const context: GPUCanvasContext
context
,
}],
TgpuRenderPassDescriptor.depthStencilAttachment?: DepthStencilAttachment | undefined
depthStencilAttachment
: {
DepthStencilAttachment.view: GPUTextureView | TgpuTextureRenderView | (DepthStencilTextureConstraint & RenderFlag) | TgpuTextureView<d.WgslTextureDepth2d | d.WgslTextureDepthMultisampled2d>

The texture subresource that will be output to and read from for this depth/stencil attachment.

view
:
const depthTexture: TgpuTexture<{
size: [256, 256];
format: "depth24plus";
}> & RenderFlag
depthTexture
,
},
});
const scenePipeline: TgpuRenderPipeline<d.Vec4f>
scenePipeline
.
TgpuRenderPipeline<Vec4f>.with(pass: TgpuRenderCommands): TgpuRenderPipeline<d.Vec4f> (+8 overloads)

Directs subsequent draw calls into the given render pass or render bundle encoder, letting multiple pipelines share one pass (and one submission).

with
(
const pass: TgpuRenderPass
pass
).
TgpuRenderPipeline<Vec4f>.draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void
draw
(
const mesh: {
vertexCount: number;
}
mesh
.
vertexCount: number
vertexCount
);
const lightPipeline: TgpuRenderPipeline<d.Vec4f>
lightPipeline
.
TgpuRenderPipeline<Vec4f>.with(pass: TgpuRenderCommands): TgpuRenderPipeline<d.Vec4f> (+8 overloads)

Directs subsequent draw calls into the given render pass or render bundle encoder, letting multiple pipelines share one pass (and one submission).

with
(
const pass: TgpuRenderPass
pass
).
TgpuRenderPipeline<Vec4f>.draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void
draw
(6,
const lightCount: 4
lightCount
);
const skyPipeline: TgpuRenderPipeline<d.Vec4f>
skyPipeline
.
TgpuRenderPipeline<Vec4f>.with(pass: TgpuRenderCommands): TgpuRenderPipeline<d.Vec4f> (+8 overloads)

Directs subsequent draw calls into the given render pass or render bundle encoder, letting multiple pipelines share one pass (and one submission).

with
(
const pass: TgpuRenderPass
pass
).
TgpuRenderPipeline<Vec4f>.draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void
draw
(3);
const pass: TgpuRenderPass
pass
.
TgpuRenderPass.end(): void

Completes the recording of this render pass

end
();
const encoder: TgpuCommandEncoder
encoder
.
TgpuCommandEncoder.submit(): void

Finishes the recording and submits the resulting command buffer to the device queue

submit
();

The beginRenderPass method accepts a descriptor similar to WebGPU’s GPURenderPassDescriptor, with a few conveniences:

  • Attachment views can be TypeGPU textures, texture views and canvas contexts, as well as raw GPUTextureViews.
  • loadOp, storeOp and depthClearValue default to 'clear', 'store' and 1 respectively. A single color attachment does not need to be wrapped in an array.
  • occlusionQuerySet and timestampWrites accept TypeGPU query sets as well as raw GPUQuerySets.

There are two equivalent ways to execute pipelines in a pass. Passing the pass to pipeline.with(pass) keeps the pipeline-centric API, together with all of its with* methods. Alternatively, the pass itself mirrors the GPURenderPassEncoder API, while accepting TypeGPU resources.

const pass: TgpuRenderPass
pass
.
TgpuRenderCommands.setPipeline(pipeline: TgpuRenderPipeline): void

Sets the current

TgpuRenderPipeline

for subsequent draw calls

setPipeline
(
const renderPipeline: TgpuRenderPipeline<d.Vec4f>
renderPipeline
);
const pass: TgpuRenderPass
pass
.
TgpuRenderCommands.setBindGroup(bindGroup: TgpuBindGroup): void (+1 overload)

Associates a bind group with the layout it was created from

setBindGroup
(
const bindGroup: TgpuBindGroup<{
size: {
uniform: d.Vec2u;
};
}>
bindGroup
);
const pass: TgpuRenderPass
pass
.
TgpuRenderCommands.setVertexBuffer<d.WgslArray<d.Vec2f>>(vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec2f>>, buffer: GPUBuffer | (TgpuBuffer<d.WgslArray<d.Vec2f>> & VertexFlag), offset?: number, size?: number): void

Binds a vertex buffer to the given vertex layout

setVertexBuffer
(
const vertexLayout: TgpuVertexLayout<d.WgslArray<d.Vec2f>>
vertexLayout
,
const vertexBuffer: TgpuBuffer<d.WgslArray<d.Vec2f>> & VertexFlag
vertexBuffer
);
const pass: TgpuRenderPass
pass
.
TgpuRenderCommands.draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void
draw
(3);

In both cases, the pipeline, bind groups, vertex and index buffers, and the stencil reference are applied lazily when a draw call is recorded, and only if they changed since the previous one. Both styles operate on the same pass state and follow the WebGPU ordering rules: executing a pipeline sets the resources bound to it (pipeline.with(bindGroup)) on the pass, later set* calls overwrite them, and all state persists until overwritten.

Compute passes work the same way:

const
const encoder: TgpuCommandEncoder
encoder
=
const root: TgpuRoot
root
['~unstable'].
function createCommandEncoder(descriptor?: GPUCommandEncoderDescriptor): TgpuCommandEncoder

Creates a

TgpuCommandEncoder

for batching multiple render/compute passes (and draws within them) into a single submission.

@example

const encoder = root['~unstable'].createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{ view: msaaTexture, resolveTarget: context }],
});
scenePipeline.with(pass).draw(vertexCount);
skyPipeline.with(pass).draw(3);
pass.end();
encoder.submit();

createCommandEncoder
();
const
const pass: TgpuComputePass
pass
=
const encoder: TgpuCommandEncoder
encoder
.
TgpuCommandEncoder.beginComputePass(descriptor?: TgpuComputePassDescriptor): TgpuComputePass

Begins recording a compute pass

beginComputePass
();
const computePipeline: TgpuComputePipeline
computePipeline
.
TgpuComputePipeline.with(pass: TgpuComputePass): TgpuComputePipeline (+6 overloads)

Directs subsequent dispatches into the given compute pass, letting multiple pipelines share one pass (and one submission).

with
(
const pass: TgpuComputePass
pass
).
TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number, z?: number): void
dispatchWorkgroups
(16);
const pass: TgpuComputePass
pass
.
TgpuComputePass.end(): void

Completes the recording of this compute pass

end
();
const encoder: TgpuCommandEncoder
encoder
.
TgpuCommandEncoder.submit(): void

Finishes the recording and submits the resulting command buffer to the device queue

submit
();

Calling encoder.submit() finishes the recording and submits it to the device queue. Shader console.log output and performance callbacks are processed as part of that submission.

Guarded compute pipelines (createGuardedComputePipeline) cannot record into passes or encoders, every dispatchThreads call submits on its own.

Whenever something is not covered by the typed API, the underlying WebGPU resources remain accessible:

  • root.unwrap(encoder) and root.unwrap(pass) return the raw GPUCommandEncoder, GPURenderPassEncoder or GPUComputePassEncoder, which can be used e.g. for texture copies. Commands recorded this way are invisible to TypeGPU, so after unwrapping a pass, every draw applies its full state again.
  • encoder.finish() returns the raw GPUCommandBuffer without submitting it, allowing manual batching via device.queue.submit([...]). TypeGPU never sees such a submission, so shader logs and performance callbacks are not processed for it.

Raw GPUCommandEncoders and pass encoders can also be passed to pipeline.with(...) directly, with the same limitations, since TypeGPU cannot know when they are submitted, nor what state has been set on them.

It is also possible to access the underlying WebGPU resources for the TypeGPU pipelines, by calling root.unwrap(pipeline). That way, they can be used with a regular WebGPU API, though this also requires unwrapping all the necessary resources.

const
const pipeline: TgpuRenderPipeline<d.Vec4f>
pipeline
=
const root: TgpuRoot
root
.
WithBinding.createRenderPipeline<{}, {}, {}, d.Vec4f>(descriptor: TgpuRenderPipeline<in Targets = never>.DescriptorBase & {
attribs?: {} | undefined;
vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<{}>);
fragment: TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{}>>) => d.v4f | (AnyAutoCustoms & Partial<...>));
targets?: TgpuColorTargetState;
}): TgpuRenderPipeline<...> (+2 overloads)
createRenderPipeline
({
vertex: TgpuVertexFn<{}, {}> | ((input: AutoVertexIn<InferGPURecord<AttribRecordToDefaultDataTypes<{}>>>) => AutoVertexOut<{}>)
vertex
:
const mainVertex: TgpuVertexFn<{}, {}>
mainVertex
,
fragment: TgpuFragmentFn<{} & Record<string, AnyFragmentInputBuiltin>, d.Vec4f> | ((input: AutoFragmentIn<InferGPURecord<{}>>) => d.v4f | (AnyAutoCustoms & Partial<InferGPURecord<{
readonly $fragDepth: d.BuiltinFragDepth;
readonly $sampleMask: d.BuiltinSampleMask;
}>>))
fragment
:
const mainFragment: TgpuFragmentFn<{}, d.Vec4f>
mainFragment
,
targets?: TgpuColorTargetState
targets
: {
format?: GPUTextureFormat | undefined

The

GPUTextureFormat

of this color target. The pipeline will only be compatible with

GPURenderPassEncoder

s which use a

GPUTextureView

of this format in the corresponding color attachment.

@defaultnavigator.gpu.getPreferredCanvasFormat()

format
: 'rg8unorm' },
});
const rawPipeline =
const root: TgpuRoot
root
.
Unwrapper.unwrap(resource: TgpuRenderPipeline): GPURenderPipeline (+15 overloads)
unwrap
(
const pipeline: TgpuRenderPipeline<d.Vec4f>
pipeline
);
const rawPipeline: GPURenderPipeline