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.
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.
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@param ― callbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.
@param ― thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
This buffer can then be used and/or updated by a WGSL shader.
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.
@param ― typeSchema The type of data that this buffer will hold.
@param ― initial The initial value of the buffer. (optional)
createBuffer(
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.
@param ― elementType The type of elements in the array.
@param ― elementCount The number of elements in the array.
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.
@exampleconst value = f32(true); // 1
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;
}>> (+1overload)
Allocates memory on the GPU, allows passing data between host and shader.
@param ― typeSchema The type of data that this buffer will hold.
@param ― initial The initial value of the buffer. (optional)
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.
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.
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
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.
@param ― typeSchema The type of data that this buffer will hold.
@param ― initial The initial value of the buffer. (optional)
createBuffer(
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.
@exampleconst value = u32(3.14); // 3
@exampleconst value = u32(-1); // 4294967295
@exampleconst value = u32(-3.1); // 0
u32, 100);
// Will be initialized to an array of two vec3fs with the specified values.
@exampleconst buffer = root.createBuffer(d.vec3f, d.vec3f(0, 1, 2)); // buffer holding a d.vec3f value, with an initial value of vec3f(0, 1, 2);
vec3f(3, 4, 5),
]);
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.
Allocates memory on the GPU, allows passing data between host and shader.
@param ― typeSchema The type of data that this buffer will hold.
@param ― gpuBuffer A vanilla WebGPU buffer.
createBuffer(
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.
@exampleconst value = u32(3.14); // 3
@exampleconst value = u32(-1); // 4294967295
@exampleconst value = u32(-3.1); // 0
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
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.
@exampleconst buffer = root.createBuffer(d.vec2f, d.vec2f(0, 1)); // buffer holding a d.vec2f value, with an initial value of vec2f(0, 1);
vec2f(1.0, 2.0),
heal
heal: any
health
});
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.
@exampleconst buffer = root.createBuffer(d.vec3f, d.vec3f(0, 1, 2)); // buffer holding a d.vec3f value, with an initial value of vec3f(0, 1, 2);
vec3f(0, 0, 1) },
],
});
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.