Setting up and managing TypeGPU resources in React components is not a trivial task, which is why we provide a set of hooks and utilities to
help with it.
Refer to TypeGPU’s installation guide for setting up TypeGPU if you haven’t already.
After that, install React and @typegpu/react using the package manager of your choice.
We first get a reference to a TypeGPU root via the useRoot hook.
import {
functionuseRoot():TgpuRoot
Returns the TypeGPU root shared between all components
under the nearest provider. (equivalent to calling await tgpu.init())
It's not necessary to use a <Root> provider; if none is found, the global context is used.
If the root hasn't been initialized yet, it will suspend the component until it is.
This hook will throw if there is an error initializing the root (like no WebGPU support).
If you'd like to handle these cases yourself, you can use
useRootOrError
or
useRootWithStatus
instead.
useRoot } from'@typegpu/react';
function
functionMyEffect():void
MyEffect() {
const
const root:TgpuRoot
root =
functionuseRoot():TgpuRoot
Returns the TypeGPU root shared between all components
under the nearest provider. (equivalent to calling await tgpu.init())
It's not necessary to use a <Root> provider; if none is found, the global context is used.
If the root hasn't been initialized yet, it will suspend the component until it is.
This hook will throw if there is an error initializing the root (like no WebGPU support).
If you'd like to handle these cases yourself, you can use
useRootOrError
or
useRootWithStatus
instead.
useRoot();
// the rest of our code will be here
}
Then we create a Render Pipeline that emits a red color over the whole canvas, and we memoize it.
@exampleconst buffer = root.createBuffer(d.vec4f, d.vec4f(0, 1, 2, 3)); // buffer holding a d.vec4f value, with an initial value of vec4f(0, 1, 2, 3);
vec4f(1, 0, 0, 1);
},
}),
[
const root:TgpuRoot
root],
);
We need to update our imports to get access to both d and common from typegpu:
import { d, common } from'typegpu';
Then we call the useConfigureContext hook, which will give us a ref to pass into a <canvas> element, as well as access to the WebGPU context via ctxRef.
Right now, each pixel is calculating exactly the same value, which is why we get a single color output.
The fragment function received contextual information as an argument, and can use it to vary the output
along the image.
const renderPipeline = useMemo(
() =>
root.createRenderPipeline({
vertex: common.fullScreenTriangle,
fragment: () => {
fragment: ({ uv }) => {
'use gpu';
// r g b a
return d.vec4f(1, 0, 0, 1);
return d.vec4f(uv, 0, 1);
},
}),
[root],
);
The uv value is a 2d vector provided by the vertex shader common.fullScreenTriangle, and ranges from 0 to 1 in both axes.
Since it consists of x and y components, it can be placed inside the d.vec4f() constructor and fills up the red and green
channels.
To pass dynamic values to your shader without having to recompile it (compilation takes significant time), you can use useUniform or useMirroredUniform, depending on the situation.
useUniform - allocates a uniform that can be updated outside of the React lifecycle.
useMirroredUniform - allocates a uniform and keeps it synchronized with the latest value.
We’re going to define a time variable to use in our shader. Since it’s a value that will get updated each frame, we want it
to be done outside of the React lifecycle to not trigger deadly re-renders. Let’s choose useUniform:
const root = useRoot();
const time = useUniform(d.f32);
const renderPipeline = useMemo(
() =>
root.createRenderPipeline({
vertex: common.fullScreenTriangle,
fragment: ({ uv }) => {
'use gpu';
return d.vec4f(uv, 0, 1);
return d.vec4f((uv * 5 + time.$) % 1, 0, 1);
},
}),
[root, time],
);
We have just tiled the pattern with multiplication and modulo, but since we’re not updating time, it’s a still image.
We can update time each frame to get the desired result:
Returns the TypeGPU root shared between all components
under the nearest provider. (equivalent to calling await tgpu.init())
It's not necessary to use a <Root> provider; if none is found, the global context is used.
If the root hasn't been initialized yet, it will suspend the component until it is.
This hook will throw if there is an error initializing the root (like no WebGPU support).
If you'd like to handle these cases yourself, you can use
Returns the TypeGPU root shared between all components
under the nearest provider. (equivalent to calling await tgpu.init())
It's not necessary to use a <Root> provider; if none is found, the global context is used.
If the root hasn't been initialized yet, it will suspend the component until it is.
This hook will throw if there is an error initializing the root (like no WebGPU support).
If you'd like to handle these cases yourself, you can use
Allows getting a ref to the component instance.
Once the component unmounts, React will set ref.current to null
(or call the ref with null if you passed a callback ref).
Returns the TypeGPU root shared between all components
under the nearest <Root> provider. (equivalent to calling await tgpu.init()).
It’s not necessary to use a <Root> provider; if none is found, the global root is used instead.
import {
functionuseRoot():TgpuRoot
Returns the TypeGPU root shared between all components
under the nearest provider. (equivalent to calling await tgpu.init())
It's not necessary to use a <Root> provider; if none is found, the global context is used.
If the root hasn't been initialized yet, it will suspend the component until it is.
This hook will throw if there is an error initializing the root (like no WebGPU support).
If you'd like to handle these cases yourself, you can use
useRootOrError
or
useRootWithStatus
instead.
useRoot } from'@typegpu/react';
function
functionMyEffect():void
MyEffect() {
const
const root:TgpuRoot
root =
functionuseRoot():TgpuRoot
Returns the TypeGPU root shared between all components
under the nearest provider. (equivalent to calling await tgpu.init())
It's not necessary to use a <Root> provider; if none is found, the global context is used.
If the root hasn't been initialized yet, it will suspend the component until it is.
This hook will throw if there is an error initializing the root (like no WebGPU support).
If you'd like to handle these cases yourself, you can use
useRootOrError
or
useRootWithStatus
instead.
useRoot();
// ...
}
If the root hasn’t been initialized yet, it will suspend the component until it is.
This hook will also throw if there is an error initializing the root (like no WebGPU support).
If you’d like to handle these cases yourself, you can use useRootOrError or
useRootWithStatus instead.
An initial value given to the uniform buffer on component mount. Alternatively, a function can be passed in to imperatively write to the buffer while its still mapped to CPU memory.
onInit
A function that will be called after the buffer is initialized. An example of where this is useful is populating the buffer with a compute shader.
// creating a resource to be updated outside of the React lifecycle
An initial value given to the mutable buffer on component mount. Alternatively, a function can be passed in to imperatively write to the buffer while its still mapped to CPU memory.
onInit
A function that will be called after the buffer is initialized. An example of where this is useful is populating the buffer with a compute shader.
// creating a mutable resource to be updated outside of the React lifecycle
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.
@param ― callback 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(() => {
'use gpu';
// Direct memory access through .$
const counter:TgpuMutable<d.F32>
counter.
TgpuMutable<F32>.$: number
$ += 1;
});
}, [
const root:TgpuRoot
root,
const counter:TgpuMutable<d.F32>
counter]);
// a function that dispatches the shader and reads the result asynchronously
An initial value given to the readonly buffer on component mount. Alternatively, a function can be passed in to imperatively write to the buffer while its still mapped to CPU memory.
onInit
A function that will be called after the buffer is initialized. An example of where this is useful is populating the buffer with a compute shader.
The <Root> component is a context provider for all of its descendants. All useRoot calls made by components beneath this wrapper
will return the same TgpuRoot object.
Property
Type
Description
root
TgpuRoot|undefined
A custom root to provide for all descendants. If not provided, one will be created on demand.