WESL Interoperability
We are working with WESL, a community standard for enhanced WGSL, to enable hybrid programs that mix-and-match between shader-centric and host-centric approaches. Shaders written in WGSL or WESL can be reflected into JS/TS as TypeGPU definitions, with proper types generated on the fly.
- ✨ Take advantage of type-safe buffers while keeping your shaders in WESL
- ⚔️ Eliminate manual byte alignment and padding
Setting up
This functionality is provided as an extension to wesl-plugin
.
Consult their documentation on what to install, and how to use it with your bundler of choice.
Our official wesl-ext-typepgu
package extends the capabilities of wesl-plugin
.
Install it before proceeding.
npm add --save-dev wesl-ext-typegpu
pnpm add -D wesl-ext-typegpu
yarn add -D wesl-ext-typegpu
Next up, reference the extension in your bundler’s configuration. Below is an example using Vite.
import { defineConfig } from "vite";import weslPlugin from "wesl-plugin/vite";import { linkBuildExtension } from "wesl-plugin";import { typegpuExtension } from "wesl-ext-typegpu";
export default defineConfig({ plugins: [weslPlugin({ extensions: [linkBuildExtension, typegpuExtension] })],});
And finally, to let the TypeScript language server know where to look for typing of the .wgsl/.wesl you’re importing, change the following in your tsconfig.json:
{ // ... "include": [/* all other files you're including */, ".wesl/**/*"] // ...}
Reflection
Let’s say we have to following shader program, split across two files.
struct BoidState { position: vec3f, velocity: vec3f,}
struct Fish { kind: u32, state: BoidState,}
use package::shared::Fish;
@group(0) @binding(0) var<storage, read_write> fish: array<Fish>;
@compute @workgroup_size(32)fn main() { // ...}
Given a shader written in WGSL/WESL, we can use the ?typegpu
query parameter to import
reified references to any struct definition.
// Importing a WGSL struct into JSimport { import Fish
Fish } from './shaders/shared.wesl?typegpu';
const const FishArray: (n: number) => d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>
FishArray = (n: number
n: number) => import d
d.arrayOf<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>(elementType: d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>, elementCount: number): d.WgslArray<...>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 Fish: d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>
Fish, n: number
n);
const buffer = const root: TgpuRoot
root.TgpuRoot.createBuffer<d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>>(typeSchema: d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>, initial?: { ...;}[] | undefined): TgpuBuffer<...> (+1 overload)
Allocates memory on the GPU, allows passing data between host and shader.
createBuffer(const FishArray: (n: number) => d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>
FishArray(512)).TgpuBuffer<WgslArray<WgslStruct<{ kind: U32; state: WgslStruct<{ position: Vec3f; velocity: Vec3f; }>; }>>>.$usage<["storage"]>(usages_0: "storage"): TgpuBuffer<d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>> & StorageFlag
$usage('storage');const buffer: TgpuBuffer<d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>> & StorageFlag
// Updating the 123rd fish's positionconst buffer: TgpuBuffer<d.WgslArray<d.WgslStruct<{ kind: d.U32; state: d.WgslStruct<{ position: d.Vec3f; velocity: d.Vec3f; }>;}>>> & StorageFlag
buffer.TgpuBuffer<WgslArray<WgslStruct<{ kind: U32; state: WgslStruct<{ position: Vec3f; velocity: Vec3f; }>; }>>>.writePartial(data: { idx: number; value: { kind?: number | undefined; state?: { position?: d.v3f | undefined; velocity?: d.v3f | undefined; }; };}[]): void
writePartial([ { idx: number
idx: 123, value: { kind?: number | undefined; state?: { position?: d.v3f | undefined; velocity?: d.v3f | undefined; };}
value: { state?: { position?: d.v3f | undefined; velocity?: d.v3f | undefined;}
state: { posit posit: any
position }, } }]);