Skip to content

Roots

Roots are responsible for resource allocation and management. Whether you’d like to wrap an existing WebGPU buffer with a typed shell or create a brand new buffer, roots are the place to start.

You can create a root using the tgpu.init function. It requests a GPU device with default requirements. An optional parameter can be passed in with special requirements for the GPU device.

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

@moduletypegpu

tgpu
from 'typegpu';
const
const root: TgpuRoot
root
= await
const tgpu: {
const: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/root/init").init;
initFromDevice: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/slot/slot").slot;
lazy: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/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
();

If you already have a device that you want to use, you can pass it into tgpu.initFromDevice instead.

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

@moduletypegpu

tgpu
from 'typegpu';
const
const adapter: GPUAdapter | null
adapter
= await
var navigator: Navigator

The Window.navigator read-only property returns a reference to the Navigator object, which has methods and properties about the application running the script.

MDN Reference

navigator
.
NavigatorGPU.gpu: GPU

A global singleton providing top-level entry points like

GPU#requestAdapter

.

gpu
.
GPU.requestAdapter(options?: GPURequestAdapterOptions): Promise<GPUAdapter | null>

Requests an adapter from the user agent. The user agent chooses whether to return an adapter, and, if so, chooses according to the provided options.

@paramoptions - Criteria used to select the adapter.

requestAdapter
();
const
const device: GPUDevice
device
= await
const adapter: GPUAdapter | null
adapter
?.
GPUAdapter.requestDevice(descriptor?: GPUDeviceDescriptor): Promise<GPUDevice>

Requests a device from the adapter. This is a one-time action: if a device is returned successfully, the adapter becomes

adapter# [[state]]#"consumed"

.

@paramdescriptor - Description of the GPUDevice to request.

requestDevice
() as
interface GPUDevice
GPUDevice
;
// using a preexisting device
const
const root: TgpuRoot
root
=
const tgpu: {
const: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/constant/tgpuConstant").constant;
fn: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/function/tgpuFn").fn;
comptime: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/function/comptime").comptime;
resolve: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/resolve/tgpuResolve").resolve;
resolveWithContext: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/resolve/tgpuResolve").resolveWithContext;
init: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/root/init").init;
initFromDevice: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/root/init").initFromDevice;
slot: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/slot/slot").slot;
lazy: typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/core/slot/lazy").lazy;
... 10 more ...;
'~unstable': typeof import("/home/runner/work/TypeGPU/TypeGPU/packages/typegpu/src/tgpuUnstable");
}

@moduletypegpu

tgpu
.
initFromDevice: (options: InitFromDeviceOptions) => TgpuRoot

Creates a root from the given device, instead of requesting it like

@seeinit. *

@example

const device: GPUDevice = ...;
const root = tgpu.initFromDevice({ device });

initFromDevice
({
device: GPUDevice
device
});

In order to draw on a canvas, you need to create and configure a context that will be later passed to a pipeline.

const
const context: GPUCanvasContext
context
=
const canvas: HTMLCanvasElement
canvas
.
HTMLCanvasElement.getContext(contextId: "webgpu"): GPUCanvasContext | null (+5 overloads)

The HTMLCanvasElement.getContext() method returns a drawing context on the canvas, or null if the context identifier is not supported, or the canvas has already been set to a different context mode.

MDN Reference

getContext
("webgpu") as
interface GPUCanvasContext
GPUCanvasContext
;
const
const presentationFormat: GPUTextureFormat
presentationFormat
=
var navigator: Navigator

The Window.navigator read-only property returns a reference to the Navigator object, which has methods and properties about the application running the script.

MDN Reference

navigator
.
NavigatorGPU.gpu: GPU

A global singleton providing top-level entry points like

GPU#requestAdapter

.

gpu
.
GPU.getPreferredCanvasFormat(): GPUTextureFormat

Returns an optimal

GPUTextureFormat

for displaying 8-bit depth, standard dynamic range content on this system. Must only return

GPUTextureFormat

"rgba8unorm" or

GPUTextureFormat

"bgra8unorm". The returned value can be passed as the

GPUCanvasConfiguration#format

to

GPUCanvasContext#configure

calls on a

GPUCanvasContext

to ensure the associated canvas is able to display its contents efficiently. Note: Canvases which are not displayed to the screen may or may not benefit from using this format.

getPreferredCanvasFormat
();
const context: GPUCanvasContext
context
.
GPUCanvasContext.configure(configuration: GPUCanvasConfiguration): undefined

Configures the context for this canvas. This clears the drawing buffer to transparent black (in [$Replace the drawing buffer$]). See

GPUCanvasContext#getConfiguration

for information on feature detection.

@paramconfiguration - Desired configuration for the context.

configure
({
GPUCanvasConfiguration.device: GPUDevice

The

GPUDevice

that textures returned by

GPUCanvasContext#getCurrentTexture

will be compatible with.

device
:
const root: TgpuRoot
root
.
TgpuRoot.device: GPUDevice

The GPU device associated with this root.

device
,
GPUCanvasConfiguration.format: GPUTextureFormat

The format that textures returned by

GPUCanvasContext#getCurrentTexture

will have. Must be one of the Supported context formats.

format
:
const presentationFormat: GPUTextureFormat
presentationFormat
,
GPUCanvasConfiguration.alphaMode?: GPUCanvasAlphaMode

Determines the effect that alpha values will have on the content of textures returned by

GPUCanvasContext#getCurrentTexture

when read, displayed, or used as an image source.

alphaMode
: 'premultiplied',
});

TypeGPU streamlines this process with root.configureContext method.

// format defaults to `navigator.gpu.getPreferredCanvasFormat()`
const
const context: GPUCanvasContext
context
=
const root: TgpuRoot
root
.
TgpuRoot.configureContext(options: ConfigureContextOptions): GPUCanvasContext

Creates and configures context for the provided canvas. Automatically sets the format to navigator.gpu.getPreferredCanvasFormat() if not provided.

@throwsAn error if no context could be obtained

configureContext
({
canvas: HTMLCanvasElement | OffscreenCanvas

The canvas for which a context will be created and configured.

canvas
,
alphaMode?: GPUCanvasAlphaMode

Determines the effect that alpha values will have on the content of textures returned by

GPUCanvasContext#getCurrentTexture

when read, displayed, or used as an image source.

alphaMode
: 'premultiplied' });

Resources independent from the device, such as bind group layouts, functions, variables, TypeGPU slots etc., are created using dedicated tgpu.* methods.

Resources requiring a device, such as bind groups, buffers, samplers, pipelines, etc., are created using root.create* methods.

For more information, see corresponding chapters.

Calling root.destroy() will call device.destroy() to let the browser know that it can free up all the resources.

If the root was created via tgpu.initFromDevice(), this method does nothing.

root.destroy(); // <- frees up all the resources

Treat roots as their own separate universes, meaning resources created from the same root can interact with each other, while resources created by seperate roots can have a hard time interacting. This usually means creating just one root at the start of the program is the safest bet, but there are exceptions.

If you cannot control the lifetime of the GPU device you are to use for computing/rendering, but are instead given the device in a lifecycle hook (e.g., react-native-wgpu), you can create a new root each time, as long as you recreate every resource as well.

example.tsx
import React from 'react';
function SceneView() {
const ref = useWebGPU(({ context, device, presentationFormat }) => {
const root = tgpu.initFromDevice({ device });
// create all resources...
});
// ...
}

It is common practice to pass a GPUDevice to classes or functions for them to allocate their required resources. At first glance, this poses a problem when trying to incorporate TypeGPU, since we would need to pass a root around instead of a device for all functionality that wants to move towards a typed API. We can create a global mapping between devices and roots to solve this.

You can copy and paste the utility below that implements a basic global cache for roots.

roots.ts
const deviceToRootMap = new WeakMap<GPUDevice, TgpuRoot>();
function getOrInitRoot(device: GPUDevice): TgpuRoot {
let root = deviceToRootMap.get(device);
if (!root) {
root = tgpu.initFromDevice({ device });
deviceToRootMap.set(device, root);
}
return root;
}

If you reuse the same getOrInitRoot function across code that has to create resources, the root will be shared across them.

class GameObject {
constructor(device: GPUDevice) {
const root = getOrInitRoot(device);
// create all resources...
}
}