@compute.toys is a playground for WebGPU compute shaders. Everything here is written in WGSL, which is WebGPU's native shader language. For up-to-date information on WGSL, please see the
WGSL draft specification. You can also
take a tour of WGSL.
Inputs
@compute.toys supplies keyboard input, mouse input, selectable input textures, custom values controlled by sliders, and the current frame and elapsed time.
Mouse input can be accessed from the
mouse struct:
mouse.pos: vec2i
mouse.click: i32
Timing information is in the
time struct:
time.frame: u32
time.elapsed: f32
Custom uniforms are in the
custom struct:
custom.my_custom_uniform_0: f32
custom.my_custom_uniform_1: f32
Two selectable textures can be accessed from
channel0 and
channel1:
textureSampleLevel(channel0, bilinear, uv, pass, lod)
textureSampleLevel(channel1, bilinear, uv, pass, lod)
Keyboard input can be accessed from the provided
keyDown(keycode: u32) helper function:
keyDown(32) // returns true when the spacebar is pressed
Outputs
For compute shader input and output
@compute.toys provides:
one input texture array
pass_in,
one output storage texture array
pass_out,
and one output screen storage texture
screen.
The shader can write to
pass_out, which will be copied into
pass_in after the current entrypoint has returned.
pass_in will always contain whatever has been written to
pass_out during all of the
previous entrypoints. The contents of
pass_in will not change while an entrypoint is running.
pass_in and
pass_out are both texture arrays with 4 texture layers. For example, you can access the third layer of
pass_in at LOD 0 and coordinate (1,1) by using the built-in helper function:
passLoad(2, vec2i(1,1), 0)
Preprocessor
@compute.toys also provides an experimental WGSL preprocessor. It currently allows the use of a handful of basic directives:
- #define NAME VALUE for simple macros (function-like parameter substitution is not yet supported)
- #include "PATH" for accessing built-in libraries
- #workgroup_count ENTRYPOINT X Y Z for specifying how many workgroups should be dispatched for an entrypoint
- #dispatch_count ENTRYPOINT N for dispatching an entrypoint multiple times in a row
- #storage NAME TYPE for declaring a storage buffer
Storage
Read-write storage buffers can be declared using the
#storage directive. For example, you can create a buffer of atomic counters:
#storage atomic_storage array<atomic<i32>>
You could use WGSL's built-in functions to do atomic operations on this buffer in any order, enabling you to safely perform work across many threads at once and accumulate the result in one place. Note that any writes to read-write storage buffers are immediately visible to subsequent reads (unlike the situation with
pass_in and
pass_out).
The final visual output of every shader is written to the
screen storage texture, which displays the result in the canvas on this page.
Debugging assertions are supported with an
assert helper function:
assert(0, isfinite(col.x))
assert(1, isfinite(col.y))
Examples
Prelude
Every shader begins with a common
prelude. The prelude contains the data inputs and outputs for this shader, as well as a few helper functions and type definitions to make working with
@compute.toys a more streamlined and familiar process. Please refer to the prelude for a complete listing of the available data in your shader.
Here are the current contents of this shader's prelude:
Note: Matrix types in WGSL are stored in column-major order. This means a matrix of type
mat2x3<f32> (aka
mat2x3f or
float2x3) is constructed from 2 column vectors of type
vec3<f32> (aka
vec3f or
float3). This is backward from HLSL and convention in mathematics.