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.
Creating a render pipeline requires calling the withVertex method first, which accepts TgpuVertexFn and matching vertex attributes.
The attributes are passed in a record, where the keys match the vertex function’s (non-builtin) input parameters, and the values are attributes retrieved
from a specific tgpu.vertexLayout.
If the vertex shader does not use vertex attributes, then the latter argument should be an empty object.
The compatibility between vertex input types and vertex attribute formats is validated at the type level.
const
const VertexStruct: d.WgslStruct<{
position:d.Vec2f;
velocity:d.Vec2f;
}>
VertexStruct =
import d
d.
struct<{
position:d.Vec2f;
velocity:d.Vec2f;
}>(props: {
position: d.Vec2f;
velocity: d.Vec2f;
}): d.WgslStruct<{
position:d.Vec2f;
velocity:d.Vec2f;
}>
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.
The next step is calling the withFragment method, which accepts TgpuFragmentFn and a targets argument defining the
formats and behaviors of the color targets the pipeline writes to.
Each target is specified the same as in the WebGPU API (GPUColorTargetState).
The difference is that when there are multiple targets, they should be passed in a record, not an array.
This way each target is identified by a name and can be validated against the outputs of the fragment function.
Using the pipelines should ensure the compatibility of the vertex output and fragment input on the type level —
withFragment only accepts fragment functions, which all non-builtin parameters are returned in the vertex stage.
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 value.
After calling withFragment, but before createPipeline, it is possible to set additional pipeline settings.
It is done through builder methods like withDepthStencil, withMultisample, withPrimitive.
They accept the same arguments as their corresponding descriptors in the WebGPU API.
Creating a compute pipeline is even easier — the withCompute method accepts just a TgpuComputeFn with no additional parameters.
Please note that compute pipelines are separate identities from render pipelines. You cannot combine withVertex and withFragment methods with withCompute in a singular pipeline.
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.
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 a pair of arguments: a bind group layout and a bind group (render and compute pipelines) or a vertex layout and a vertex buffer (render pipelines only).
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.
Unlike render pipelines, after running this method, the execution is not submitted to the GPU immediately.
In order to do so, root['~unstable'].flush() needs to be run.
However, that is usually not necessary, as it is done automatically when trying to read the result of computation.
The higher-level API has several limitations, therefore another way of executing pipelines is exposed, for some custom, more demanding scenarios. For example, with the high-level API, it is not possible to execute multiple pipelines per one render pass. It also may be missing some more niche features of the WebGPU API.
root['~unstable'].beginRenderPass is a method that mirrors the WebGPU API, but enriches it with a direct TypeGPU resource support.
root['~unstable'].beginRenderPass(
{
colorAttachments: [{
...
}],
},
(pass)=> {
pass.setPipeline(renderPipeline);
pass.setBindGroup(layout, group);
pass.draw(3);
},
);
root['~unstable'].flush();
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, but unlike the root['~unstable'].beginRenderPass API, it also requires unwrapping all the necessary
resources.