Buffers
Memory on the GPU can be allocated and managed through buffers. That way, WGSL shaders can be provided with an additional context, or retrieve the results of parallel computation back to JS. When creating a buffer, a schema for the contained values has to be provided, which allows for:
- Calculating the required size of the buffer,
- Automatic conversion to-and-from a binary representation,
- Type-safe APIs for writing and reading.
As an example, let’s create a buffer for storing particles.
import const tgpu: { fn: { <Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>; <Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>; }; ... 7 more ...; '~unstable': { ...; };}
tgpu from 'typegpu';import * as import d
d from 'typegpu/data';
// Defining a struct typeconst const Particle: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>
Particle = import d
d.struct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>(props: { position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}): d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>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.
struct({ position: d.Vec3f
position: import d
d.const vec3f: d.Vec3fexport vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f, velocity: d.Vec3f
velocity: import d
d.const vec3f: d.Vec3fexport vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f, health: d.F32
health: import d
d.const f32: d.F32export 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.
f32,});
// Utility for creating a random particlefunction function createParticle(): d.Infer<typeof Particle>
createParticle(): import d
d.type Infer<T> = T extends { readonly [$repr]: infer TRepr;} ? TRepr : Texport Infer
Extracts the inferred representation of a resource.
For inferring types as seen by the GPU, see
InferGPU
Infer<typeof const Particle: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>
Particle> { return { position: d.v3f
position: import d
d.function vec3f(x: number, y: number, z: number): d.v3f (+5 overloads)export vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f(var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.Math.random(): number
Returns a pseudorandom number between 0 and 1.
random(), 2, var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math.Math.random(): number
Returns a pseudorandom number between 0 and 1.
random()), velocity: d.v3f
velocity: import d
d.function vec3f(x: number, y: number, z: number): d.v3f (+5 overloads)export vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f(0, 9.8, 0), health: number
health: 100, };}
const const root: TgpuRoot
root = await const tgpu: { fn: { <Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>; <Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>; }; ... 7 more ...; '~unstable': { ...; };}
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
init();
// Creating and initializing a buffer.const const buffer: TgpuBuffer<d.WgslArray<d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>>> & StorageFlag
buffer = const root: TgpuRoot
root .TgpuRoot.createBuffer<d.WgslArray<d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>>>(typeSchema: d.WgslArray<d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>>, initial?: { ...;}[] | undefined): TgpuBuffer<...> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer( import d
d.arrayOf<d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>>(elementType: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>, elementCount: number): d.WgslArray<...> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(const Particle: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>
Particle, 100), // <- holds 100 particles var Array: ArrayConstructor
Array.ArrayConstructor.from<unknown>(iterable: Iterable<unknown> | ArrayLike<unknown>): unknown[] (+3 overloads)
Creates an array from an iterable object.
from({ ArrayLike<T>.length: number
length: 100 }).Array<unknown>.map<{ position: d.v3f; velocity: d.v3f; health: number;}>(callbackfn: (value: unknown, index: number, array: unknown[]) => { position: d.v3f; velocity: d.v3f; health: number;}, thisArg?: any): { position: d.v3f; velocity: d.v3f; health: number;}[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
map(function createParticle(): d.Infer<typeof Particle>
createParticle), // <- initial value ) .TgpuBuffer<WgslArray<WgslStruct<{ position: Vec3f; velocity: Vec3f; health: F32; }>>>.$usage<["storage"]>(usages_0: "storage"): TgpuBuffer<d.WgslArray<d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>>> & StorageFlag
$usage('storage'); // <- can be used as a "storage buffer"
// -// --// --- Shader omitted for brevity...// --// -
// Reading back from the bufferconst value = await const buffer: TgpuBuffer<d.WgslArray<d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; health: d.F32;}>>> & StorageFlag
buffer.TgpuBuffer<WgslArray<WgslStruct<{ position: Vec3f; velocity: Vec3f; health: F32; }>>>.read(): Promise<{ position: d.v3f; velocity: d.v3f; health: number;}[]>
read();const value: { position: d.v3f; velocity: d.v3f; health: number;}[]
This buffer can then be used and/or updated by a WGSL shader.
Creating a buffer
Section titled “Creating a buffer”To create a buffer, you will need to define its schema by composing data types imported from typegpu/data
. Every WGSL data-type can be represented as JS schemas, including
structs and arrays. They will be explored in more detail in a following chapter.
const countBuffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.U32>(typeSchema: d.U32, initial?: number | undefined): TgpuBuffer<d.U32> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.const u32: d.U32export 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.
u32);const countBuffer: TgpuBuffer<d.U32>
const listBuffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslArray<d.F32>>(typeSchema: d.WgslArray<d.F32>, initial?: number[] | undefined): TgpuBuffer<d.WgslArray<d.F32>> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.arrayOf<d.F32>(elementType: d.F32, elementCount: number): d.WgslArray<d.F32> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const f32: d.F32export 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.
f32, 10));const listBuffer: TgpuBuffer<d.WgslArray<d.F32>>
const uniformsBuffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslStruct<{ a: d.F32; b: d.F32;}>>(typeSchema: d.WgslStruct<{ a: d.F32; b: d.F32;}>, initial?: { a: number; b: number;} | undefined): TgpuBuffer<d.WgslStruct<{ a: d.F32; b: d.F32;}>> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.struct<{ a: d.F32; b: d.F32;}>(props: { a: d.F32; b: d.F32;}): d.WgslStruct<{ a: d.F32; b: d.F32;}>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.
struct({ a: d.F32
a: import d
d.const f32: d.F32export 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.
f32, b: d.F32
b: import d
d.const f32: d.F32export 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.
f32 }));const uniformsBuffer: TgpuBuffer<d.WgslStruct<{ a: d.F32; b: d.F32;}>>
Usage flags
Section titled “Usage flags”To be able to use these buffers in WGSL shaders, we have to declare their usage upfront with .$usage(...)
.
const const buffer: TgpuBuffer<d.U32> & UniformFlag & StorageFlag & VertexFlag
buffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.U32>(typeSchema: d.U32, initial?: number | undefined): TgpuBuffer<d.U32> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.const u32: d.U32export 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.
u32) .TgpuBuffer<U32>.$usage<["uniform"]>(usages_0: "uniform"): TgpuBuffer<d.U32> & UniformFlag
$usage('uniform') .TgpuBuffer<U32>.$usage<["uniform" | "storage" | "vertex", ...("uniform" | "storage" | "vertex")[]]>(usages_0: "uniform" | "storage" | "vertex", ...usages: ("uniform" | "storage" | "vertex")[]): TgpuBuffer<d.U32> & UniformFlag & StorageFlag & VertexFlag
$usage(' ')uniformstoragevertex
You can also add all flags in a single $usage()
.
const const buffer: TgpuBuffer<d.U32> & UniformFlag & StorageFlag & VertexFlag
buffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.U32>(typeSchema: d.U32, initial?: number | undefined): TgpuBuffer<d.U32> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.const u32: d.U32export 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.
u32) .TgpuBuffer<U32>.$usage<["uniform" | "storage" | "vertex", ...("uniform" | "storage" | "vertex")[]]>(usages_0: "uniform" | "storage" | "vertex", ...usages: ("uniform" | "storage" | "vertex")[]): TgpuBuffer<d.U32> & UniformFlag & StorageFlag & VertexFlag
$usage('uniform', 'storage', ' ');uniformstoragevertex
Additional flags
Section titled “Additional flags”It is also possible to add any of the GPUBufferUsage
flags to a typed buffer object, using the .$addFlags
method.
Though it shouldn’t be necessary in most scenarios as majority of the flags are handled automatically by the library
or indirectly through the .$usage
method.
const buffer: TgpuBuffer<d.F32>
buffer.TgpuBuffer<F32>.$addFlags(flags: GPUBufferUsageFlags): TgpuBuffer<d.F32>
$addFlags(var GPUBufferUsage: GPUBufferUsage
GPUBufferUsage.GPUBufferUsage.QUERY_RESOLVE: number
QUERY_RESOLVE);
Flags can only be added this way if the typed buffer was not created with an existing GPU buffer. If it was, then all flags need to be provided to the existing buffer when constructing it.
Initial value
Section titled “Initial value”You can also pass an initial value to the root.createBuffer
function.
When the buffer is created, it will be mapped at creation, and the initial value will be written to the buffer.
// Will be initialized to `100`const const buffer1: TgpuBuffer<d.U32>
buffer1 = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.U32>(typeSchema: d.U32, initial?: number | undefined): TgpuBuffer<d.U32> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.const u32: d.U32export 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.
u32, 100);
// Will be initialized to an array of two vec3fs with the specified values.const const buffer2: TgpuBuffer<d.WgslArray<d.Vec3f>>
buffer2 = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslArray<d.Vec3f>>(typeSchema: d.WgslArray<d.Vec3f>, initial?: d.v3f[] | undefined): TgpuBuffer<d.WgslArray<d.Vec3f>> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.arrayOf<d.Vec3f>(elementType: d.Vec3f, elementCount: number): d.WgslArray<d.Vec3f> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const vec3f: d.Vec3fexport vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f, 2), [ import d
d.function vec3f(x: number, y: number, z: number): d.v3f (+5 overloads)export vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f(0, 1, 2), import d
d.function vec3f(x: number, y: number, z: number): d.v3f (+5 overloads)export vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f(3, 4, 5),]);
Using an existing buffer
Section titled “Using an existing buffer”You can also create a buffer using an existing WebGPU buffer. This is useful when you have existing logic but want to introduce type-safe data operations.
// A raw WebGPU bufferconst const existingBuffer: GPUBuffer
existingBuffer = const root: TgpuRoot
root.TgpuRoot.device: GPUDevice
The GPU device associated with this root.
device.GPUDevice.createBuffer(descriptor: GPUBufferDescriptor): GPUBuffer
Creates a
GPUBuffer
.
createBuffer({ GPUBufferDescriptor.size: number
The size of the buffer in bytes.
size: 4, GPUBufferDescriptor.usage: number
The allowed usages for the buffer.
usage: var GPUBufferUsage: GPUBufferUsage
GPUBufferUsage.GPUBufferUsage.STORAGE: number
STORAGE | var GPUBufferUsage: GPUBufferUsage
GPUBufferUsage.GPUBufferUsage.COPY_DST: number
COPY_DST,});
const const buffer: TgpuBuffer<d.U32>
buffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.U32>(typeSchema: d.U32, gpuBuffer: GPUBuffer): TgpuBuffer<d.U32> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.const u32: d.U32export 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.
u32, const existingBuffer: GPUBuffer
existingBuffer);
const buffer: TgpuBuffer<d.U32>
buffer.TgpuBuffer<U32>.write(data: number): void
write(12); // Writing to `existingBuffer` through a type-safe API
Writing to a buffer
Section titled “Writing to a buffer”To write data to a buffer, you can use the .write(value)
method. The typed schema enables auto-complete as well as static validation of this
method’s arguments.
const const Particle: d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>
Particle = import d
d.struct<{ position: d.Vec2f; health: d.U32;}>(props: { position: d.Vec2f; health: d.U32;}): d.WgslStruct<{ position: d.Vec2f; health: 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.
struct({ position: d.Vec2f
position: import d
d.const vec2f: d.Vec2fexport vec2f
Schema representing vec2f - a vector with 2 elements of type f32.
Also a constructor function for this vector value.
vec2f, health: d.U32
health: import d
d.const u32: d.U32export 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.
u32,});
const const particleBuffer: TgpuBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>
particleBuffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>(typeSchema: d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>, initial?: { position: d.v2f; health: number;} | undefined): TgpuBuffer<...> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(const Particle: d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>
Particle);
const particleBuffer: TgpuBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>
particleBuffer.TgpuBuffer<WgslStruct<{ position: Vec2f; health: U32; }>>.write(data: { position: d.v2f; health: number;}): void
write({ position: d.v2f
position: import d
d.function vec2f(x: number, y: number): d.v2f (+3 overloads)export vec2f
Schema representing vec2f - a vector with 2 elements of type f32.
Also a constructor function for this vector value.
vec2f(1.0, 2.0), heal heal: any
health});
Partial writes
Section titled “Partial writes”When you want to update only a subset of a buffer’s fields, you can use the .writePartial(data)
method. This method updates only the fields provided in the data
object and leaves the rest unchanged.
The format of the data
value depends on your schema type:
-
For
d.struct
schemas: Provide an object with keys corresponding to the subset of the schema’s fields you wish to update. -
For
d.array
schemas: Provide an array of objects. Each object should specify:idx
: the index of the element to update.value
: the new value for that element.
const const Planet: d.WgslStruct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>
Planet = import d
d.struct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>(props: { radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}): d.WgslStruct<...>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.
struct({ radius: d.F32
radius: import d
d.const f32: d.F32export 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.
f32, mass: d.F32
mass: import d
d.const f32: d.F32export 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.
f32, position: d.Vec3f
position: import d
d.const vec3f: d.Vec3fexport vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f, colors: d.WgslArray<d.Vec3f>
colors: import d
d.arrayOf<d.Vec3f>(elementType: d.Vec3f, elementCount: number): d.WgslArray<d.Vec3f> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const vec3f: d.Vec3fexport vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f, 5),});
const const planetBuffer: TgpuBuffer<d.WgslStruct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>>
planetBuffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslStruct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>>(typeSchema: d.WgslStruct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>, initial?: { ...;} | undefined): TgpuBuffer<...> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(const Planet: d.WgslStruct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>
Planet);
const planetBuffer: TgpuBuffer<d.WgslStruct<{ radius: d.F32; mass: d.F32; position: d.Vec3f; colors: d.WgslArray<d.Vec3f>;}>>
planetBuffer.TgpuBuffer<WgslStruct<{ radius: F32; mass: F32; position: Vec3f; colors: WgslArray<Vec3f>; }>>.writePartial(data: { radius?: number | undefined; mass?: number | undefined; position?: d.v3f | undefined; colors?: { idx: number; value: d.v3f | undefined; }[] | undefined;} | undefined): void
writePartial({ mass?: number | undefined
mass: 123.1, colors?: { idx: number; value: d.v3f | undefined;}[] | undefined
colors: [ { idx: number
idx: 2, value: d.v3f | undefined
value: import d
d.function vec3f(x: number, y: number, z: number): d.v3f (+5 overloads)export vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f(1, 0, 0) }, { idx: number
idx: 4, value: d.v3f | undefined
value: import d
d.function vec3f(x: number, y: number, z: number): d.v3f (+5 overloads)export vec3f
Schema representing vec3f - a vector with 3 elements of type f32.
Also a constructor function for this vector value.
vec3f(0, 0, 1) }, ],});
Copying
Section titled “Copying”There’s also an option to copy value from another typed buffer using the .copyFrom(buffer)
method,
as long as both buffers have a matching data schema.
const const backupParticleBuffer: TgpuBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>
backupParticleBuffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>(typeSchema: d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>, initial?: { position: d.v2f; health: number;} | undefined): TgpuBuffer<...> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(const Particle: d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>
Particle);const backupParticleBuffer: TgpuBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>
backupParticleBuffer.TgpuBuffer<WgslStruct<{ position: Vec2f; health: U32; }>>.copyFrom(srcBuffer: TgpuBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32 | d.Atomic<d.U32> | DecoratedLocation<d.U32>;}>>): void
copyFrom(const particleBuffer: TgpuBuffer<d.WgslStruct<{ position: d.Vec2f; health: d.U32;}>>
particleBuffer);
Reading from a buffer
Section titled “Reading from a buffer”To read data from a buffer, you can use the .read()
method.
It returns a promise that resolves to the data read from the buffer.
const const buffer: TgpuBuffer<d.WgslArray<d.U32>>
buffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslArray<d.U32>>(typeSchema: d.WgslArray<d.U32>, initial?: number[] | undefined): TgpuBuffer<d.WgslArray<d.U32>> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.arrayOf<d.U32>(elementType: d.U32, elementCount: number): d.WgslArray<d.U32> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const u32: d.U32export 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.
u32, 10));
const data = await const buffer: TgpuBuffer<d.WgslArray<d.U32>>
buffer.TgpuBuffer<WgslArray<U32>>.read(): Promise<number[]>
read();const data: number[]
Index Buffers
Section titled “Index Buffers”TypeGPU also allows for the use of index buffers. An index buffer is a buffer containing a sequence of u32
or u16
indices, which indicate in which order vertices will be processed. This is particulary useful for rendering geometry, where vertices are often shared between multiple primitives (e.g., triangles), as it avoids duplicating vertex data.
You may refer to the pipelines section for usage details.
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?: number[] | undefined): TgpuBuffer<d.WgslArray<d.U16>> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.arrayOf<d.U16>(elementType: d.U16, elementCount: number): d.WgslArray<d.U16> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const u16: d.U16export 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');
Binding buffers
Section titled “Binding buffers”To use a buffer in a shader, it needs to be bound using a bind group. There are two approaches provided by TypeGPU.
Manual binding
Section titled “Manual binding”The default option is to create bind group layouts and bind your buffers via bind groups. Read more in the chapter dedicated to bind groups.
const const pointsBuffer: TgpuBuffer<d.WgslArray<d.Vec2i>> & StorageFlag
pointsBuffer = const root: TgpuRoot
root .TgpuRoot.createBuffer<d.WgslArray<d.Vec2i>>(typeSchema: d.WgslArray<d.Vec2i>, initial?: d.v2i[] | undefined): TgpuBuffer<d.WgslArray<d.Vec2i>> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(import d
d.arrayOf<d.Vec2i>(elementType: d.Vec2i, elementCount: number): d.WgslArray<d.Vec2i> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const vec2i: d.Vec2iexport vec2i
Schema representing vec2i - a vector with 2 elements of type i32.
Also a constructor function for this vector value.
vec2i, 100)) .TgpuBuffer<WgslArray<Vec2i>>.$usage<["storage"]>(usages_0: "storage"): TgpuBuffer<d.WgslArray<d.Vec2i>> & StorageFlag
$usage('storage');
const const bindGroupLayout: TgpuBindGroupLayout<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroupLayout = const tgpu: { fn: { <Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>; <Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>; }; ... 7 more ...; '~unstable': { ...; };}
tgpu.bindGroupLayout: <{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>(entries: { points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}) => TgpuBindGroupLayout<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroupLayout({ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable";}
points: { storage: d.WgslArray<d.Vec2i>
storage: import d
d.arrayOf<d.Vec2i>(elementType: d.Vec2i, elementCount: number): d.WgslArray<d.Vec2i> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const vec2i: d.Vec2iexport vec2i
Schema representing vec2i - a vector with 2 elements of type i32.
Also a constructor function for this vector value.
vec2i, 100), access: "mutable"
access: 'mutable' },});
const const bindGroup: TgpuBindGroup<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroup = const root: TgpuRoot
root .TgpuRoot.createBindGroup<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>(layout: TgpuBindGroupLayout<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>, entries: ExtractBindGroupInputFromLayout<...>): TgpuBindGroup<...>
Creates a group of resources that can be bound to a shader based on a specified layout.
createBindGroup(const bindGroupLayout: TgpuBindGroupLayout<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroupLayout, { points: TgpuBuffer<d.WgslArray<d.Vec2i>> & StorageFlag
points: const pointsBuffer: TgpuBuffer<d.WgslArray<d.Vec2i>> & StorageFlag
pointsBuffer });
const const mainCompute: TgpuComputeFn<{ gid: d.BuiltinGlobalInvocationId;}>
mainCompute = const tgpu: { fn: { <Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>; <Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>; }; ... 7 more ...; '~unstable': { ...; };}
tgpu['~unstable'].computeFn: <{ gid: d.BuiltinGlobalInvocationId;}>(options: { in: { gid: d.BuiltinGlobalInvocationId; }; workgroupSize: number[];}) => TgpuComputeFnShell<{ gid: d.BuiltinGlobalInvocationId;}> (+1 overload)
computeFn({ in: { gid: d.BuiltinGlobalInvocationId;}
in: { gid: d.BuiltinGlobalInvocationId
gid: import d
d.const builtin: { readonly vertexIndex: d.BuiltinVertexIndex; readonly instanceIndex: d.BuiltinInstanceIndex; readonly position: d.BuiltinPosition; readonly clipDistances: d.BuiltinClipDistances; ... 10 more ...; readonly subgroupSize: BuiltinSubgroupSize;}export builtin
builtin.globalInvocationId: d.BuiltinGlobalInvocationId
globalInvocationId }, workgroupSize: number[]
workgroupSize: [1],})((input: { gid: d.v3u;}
input) => { // Access and modify the bound buffer via the layout const bindGroupLayout: TgpuBindGroupLayout<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroupLayout.TgpuBindGroupLayout<{ points: { storage: WgslArray<Vec2i>; access: "mutable"; }; }>.$: { points: d.v2i[];}
$.points: d.v2i[]
points[input: { gid: d.v3u;}
input.gid: d.v3u
gid[0]] = import d
d.function vec2i(x: number, y: number): d.v2i (+3 overloads)export vec2i
Schema representing vec2i - a vector with 2 elements of type i32.
Also a constructor function for this vector value.
vec2i(1, 2);});
const const pipeline: TgpuComputePipeline
pipeline = const root: TgpuRoot
root['~unstable'] .withCompute<{ gid: d.BuiltinGlobalInvocationId;}>(entryFn: TgpuComputeFn<{ gid: d.BuiltinGlobalInvocationId;}>): WithCompute
withCompute(const mainCompute: TgpuComputeFn<{ gid: d.BuiltinGlobalInvocationId;}>
mainCompute) .WithCompute.createPipeline(): TgpuComputePipeline
createPipeline();
const pipeline: TgpuComputePipeline
pipeline .TgpuComputePipeline.with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: TgpuBindGroup): TgpuComputePipeline
with(const bindGroupLayout: TgpuBindGroupLayout<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroupLayout, const bindGroup: TgpuBindGroup<{ points: { storage: d.WgslArray<d.Vec2i>; access: "mutable"; };}>
bindGroup) .TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number | undefined, z?: number | undefined): void
dispatchWorkgroups(100);
Using fixed resources
Section titled “Using fixed resources”For scenarios where buffers remain consistent across multiple compute or render operations, TypeGPU offers fixed resources that appear bindless from a code perspective. Fixed buffers are created using dedicated root methods.
WGSL type | TypeGPU constructor |
---|---|
var<uniform> | root.createUniform() |
var<storage, read> | root.createReadonly() |
var<storage, read_write> | root.createMutable() |
const const pointsBuffer: TgpuMutable<d.WgslArray<d.Vec2i>>
pointsBuffer = const root: TgpuRoot
root.TgpuRoot.createMutable<d.WgslArray<d.Vec2i>>(typeSchema: d.WgslArray<d.Vec2i>, initial?: d.v2i[] | undefined): TgpuMutable<d.WgslArray<d.Vec2i>> (+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
.
createMutable(import d
d.arrayOf<d.Vec2i>(elementType: d.Vec2i, elementCount: number): d.WgslArray<d.Vec2i> (+1 overload)export arrayOf
Creates an array schema that can be used to construct gpu buffers.
Describes arrays with fixed-size length, storing elements of the same type.
arrayOf(import d
d.const vec2i: d.Vec2iexport vec2i
Schema representing vec2i - a vector with 2 elements of type i32.
Also a constructor function for this vector value.
vec2i, 100));
const const mainCompute: TgpuComputeFn<{ gid: d.BuiltinGlobalInvocationId;}>
mainCompute = const tgpu: { fn: { <Args extends d.AnyData[] | []>(argTypes: Args, returnType?: undefined): TgpuFnShell<Args, d.Void>; <Args extends d.AnyData[] | [], Return extends d.AnyData>(argTypes: Args, returnType: Return): TgpuFnShell<Args, Return>; }; ... 7 more ...; '~unstable': { ...; };}
tgpu['~unstable'].computeFn: <{ gid: d.BuiltinGlobalInvocationId;}>(options: { in: { gid: d.BuiltinGlobalInvocationId; }; workgroupSize: number[];}) => TgpuComputeFnShell<{ gid: d.BuiltinGlobalInvocationId;}> (+1 overload)
computeFn({ in: { gid: d.BuiltinGlobalInvocationId;}
in: { gid: d.BuiltinGlobalInvocationId
gid: import d
d.const builtin: { readonly vertexIndex: d.BuiltinVertexIndex; readonly instanceIndex: d.BuiltinInstanceIndex; readonly position: d.BuiltinPosition; readonly clipDistances: d.BuiltinClipDistances; ... 10 more ...; readonly subgroupSize: BuiltinSubgroupSize;}export builtin
builtin.globalInvocationId: d.BuiltinGlobalInvocationId
globalInvocationId }, workgroupSize: number[]
workgroupSize: [1],})((input: { gid: d.v3u;}
input) => { // Access and modify the fixed buffer directly const pointsBuffer: TgpuMutable<d.WgslArray<d.Vec2i>>
pointsBuffer.TgpuMutable<WgslArray<Vec2i>>.$: d.v2i[]
$[input: { gid: d.v3u;}
input.gid: d.v3u
gid[0]] = import d
d.function vec2i(): d.v2i (+3 overloads)export vec2i
Schema representing vec2i - a vector with 2 elements of type i32.
Also a constructor function for this vector value.
vec2i();});
const const pipeline: TgpuComputePipeline
pipeline = const root: TgpuRoot
root['~unstable'].withCompute<{ gid: d.BuiltinGlobalInvocationId;}>(entryFn: TgpuComputeFn<{ gid: d.BuiltinGlobalInvocationId;}>): WithCompute
withCompute(const mainCompute: TgpuComputeFn<{ gid: d.BuiltinGlobalInvocationId;}>
mainCompute).WithCompute.createPipeline(): TgpuComputePipeline
createPipeline();const pipeline: TgpuComputePipeline
pipeline.TgpuComputePipeline.dispatchWorkgroups(x: number, y?: number | undefined, z?: number | undefined): void
dispatchWorkgroups(100);
TypeGPU automatically generates a “catch-all” bind group and populates it with the fixed resources.
You can also use resolveWithContext
to access the automatically generated bind group and layout containing your fixed resources.