WESL for the WGSL Team
community extended WGSL
Overview
WESL intro - features, status, next steps
Conditions and imports - WESL today, WGSL tomorrow?
Possible WGSL tweaks? - const_assert, attributes into grammar
Future features of interest:
Helping WESL - advice, partner intros, spread the word
2
WESL M1 Highlights
3
WESL Demo - WGSL with import and @if
4
mandelbrot.wesl
@fragment
fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
let mandelbrotEscaped = package::mandelbrot::mandelbrot(...);
let color = mandelbrotEscaped;
return vec4(vec3f(color), 1.0);
}
main.wesl
import package::fullscreen_quad::{fullscreen_quad, QuadVertex};
@if(DEBUG)
let color = sinRand(vec2f(mandelbrotEscaped));
@if(!DEBUG)
import random_wgsl::sinRand;
WESL M1 - Key Features
(+ some experimental features, like const injection, code gen virtual modules)
5
WESL is a compatible superset language for WGSL
Adding WESL is easy
use wesl::Wesl;
let wgsl_str = Wesl::new("src/shaders")
.compile("main.wesl")
.unwrap()
.to_string();
import { link } from "wesl";
import appWesl from "./shaders/app.wesl?link";
const linked = await link(appWesl);
linked.createShaderModule(device, {} );
6
Rust
JavaScript / TypeScript
WESL is Ready to Try
7
Can Pioneer Shader Ergonomics in WESL
Build new shader language features in user space
Successful features could graduate to WGSL
8
⚡
Idea
WESL extension
WESL
stable
WGSL
Custom Project
Imports
import package::lighting::VertOut;
import bevy_pbr::{
forward_io::VertexOutput,
pbr_types::{PbrInput, pbr_input_new},
pbr_bindings
};
fn main() {
ui_lib::quad(vec2f(0.0, 1.0), 1.0);
import random_wgsl::sinRand;
9
Import System Details
Users expect side effect free imports..
Examples (w/ WESL approach for local modules.. Libraries tbd)
const_assert (include module level const_asserts if element imported)
enable (must agree with root module enable, else error)
override, var (include iff transitively referenced from root)
entry points (prefer root only for now, tbd)
Mapping of import paths to files is efficient (no file probes required)
10
Module System Future Features
Visibility control
Re-exports
Namespace integration - e.g. each module would get a namespace...
11
A Module is just Shader Text with a Name
12
import { link } from "wesl";
const utils = `const foo = 7;`;
const main = `
import package::utils::foo;
fn main() { let a = foo; }
`;
const weslSrc = { main, utils };
const linked = await link({ weslSrc });
Shader Libraries on NPM and Cargo
Packages are simple: WESL/WGSL text with metadata. (tools available)
Try making a simple shader library and share it with the world!
13
JavaScript: npmjs.com/package/random_wgsl
Conditions
conditional compilation
syntax safe
build time and/or runtime conditions (cf. the shader permutations problem)
@if(legacy_implementation || (is_web_version && xyz_not_supported))
let result = legacy_impl();
14
Any Questions on WESL M1?
15
Roadmap Sketch - Driven by Community
16
Time
Language Server
Linkers (rust, js)
Syntax Highlighter
WESL
Tools
Doc
WGSL Lib Portability
Rich Libraries
Doc Generator
Type Checking
Polyfills
Generics
Interfaces
App Directed Linking
Inheritance
Namespaces
Advanced Libraries
Bundler Plugins
Simple Libraries
Conditionals
Modules
User Documentation
Shared Test Suite
Specification
✔
✔
✔
✔
✔
✔
✔
✔
~
WESL M1 - Starting Engagement Now
Polish our M1 release (docs, error reporting, etc.)
Engage with first users (bevy, lygia, …)
Suggestions on whom to engage?
Any upcoming demo projects that we could help WESLify?
17
Minor WGSL Tweaks
(minor ideas to make things a little better for WESL)
18
Attributes in More Places
@if is allowed in lots of places, many more places than WGSL attributes
WESL has to modify the grammar pervasively
Maintenance issue for WESL
Perhaps WGSL grammar to allow attributes in more places?
19
const_assert in Unreachable Functions?
20
const random = 4; // from fair dice roll
@if(MOBILE)
const NUM_LIGHTS = 4;
fn unused() {
const_assert(NUM_LIGHTS > 4);
}
util
import super::util::random;
fn main() -> u32 { return random; }
main
• unused assert breaks expectations
• can WGSL evolve?
• should WESL diverge?
Source maps
We’re using private source maps for remapping naga/tint error messages.
21
22
23
Source Maps (cont)
We’d like to pass our source maps to WebGPU eventually..
Uncertain priority. Hackery sufficient for naga/tint errors, hopefully.
24
Future WESL Features
25
Reflection
Users continue to ask for reflection
Partner prototypes for reflection are underway using WESL as library
What would make sense for reflection in core WebGPU?
26
TypeGPU Reflection
27
struct BoidState {
position: vec3f,
velocity: vec3f,
}
struct Fish {
kind: u32,
state: BoidState,
}
• type-safe data buffers
• automatic JS value serialization
• optimised close to vanilla performance
// Generated code
export const BoidState = d.struct({
position: d.vec3f,
velocity: d.vec3f,
});
export const Fish = d.struct({
kind: d.u32,
state: BoidState,
});
Generates
import {
BoidState,
Fish
} from './main.wesl?typegpu';
Namespaces
Not enabling for WESL implementations, we need ident binding anyway
Namespaces within files are bonus, but file modularity is the main thing.
We think namespaces can integrate with module system
Uncertain priority.
28
Polyfills
What polyfills are most valuable to WGSL team and WebGPU users?
#4957 binding structs (prototyped)
swizzles
function overrides
generalized aliases
if-expressions/ternary
29
Proposed for WGSL: Binding Structs
import package::uniforms::Uniforms;
struct MyBindings {
@group(0) @binding(0)
particles: ptr<storage, array<u32>, read_write>,
@group(0) @binding(1)
uniforms: ptr<uniform, Uniforms>,
}
@workgroup_size(1)
@compute fn main(b: MyBindings) {
b.particles[0] = b.uniforms.foo;
}
@group(0) @binding(0)
var<storage, read_write> particles : array<u32>;
@group(0) @binding(1)
var<uniform> uniforms : Uniforms;
@workgroup_size(1)
@compute fn main() {
particles[0] = uniforms.foo;
}
struct Uniforms { foo: u32 }
30
import { layouts } from "./app.wesl?bindingLayout";
device.createBindGroupLayout({entries: layouts.myBindings});
WGSL
Proposed WGSL
Reflection
Conditions Alone Get Complicated – Bevy Example
@if(!MAY_DISCARD)
fn prepass_alpha_discard(in: VertexOutput) { }
@if(MAY_DISCARD)
fn prepass_alpha_discard(in: VertexOutput) {
@if(BINDLESS)
let slot = mesh[in.instance_index].material_and_lightmap_bind_group_slot & 0xffffu;
@if(VERTEX_UVS)
{
let uv_transform = pbr_material.uv_transform;
uv = (uv_transform * vec3(uv, 1.0)).xy;
31
What @else should we explore?
Remove known dead conditionals
if(constant TRUE) { ... } else { drop at compile time }
Generics
Something else?
32
Generics
Advanced users and library author feature.
Requires type-inference (already in wesl-rs)
Several old WESL proposals, no consensus yet. (e.g. generics on namespaces?)
What should we look at?
33
Portability for Libraries
WESL M1 supports fn libraries on npm and cargo (yea!)
We need modularity for ‘host code visible’ WGSL/WESL constructs in libraries
34
WGSL Portability: Bindings for libraries?
@group(0) @binding(0) var<uniform> light: DirectionalLight;
35
Which group?
Which binding?
1/12 of max uniform buffers?
Future work for richer portable libraries. WESL for Libraries
Future Tooling
36
Upcoming Tooling
Language server: wgsl_analyzer is understaffed but adding support for WESL (we will contribute)
Syntax Highlighter - possibly based on new WESL tree sitter grammar?
Doc Generator - possibly, user/contributor interest
Online Playground - import libraries, possibly wesl library outputs
37
Improvements to WESL Internals
Type Inference
Error Reporting
Any tips for us?
38
Conclusion
39
Support Common Tooling for WebGPU!
Connect to Users help spread the word, help introduce to partner projects.
Try WESL in your demo app, make a simple library, we’ll help.
Review our M1 features - possibly future WGSL features?
Keep in Touch lots we can do together!
40
Extra Slides
41
wesl-js Runtime Linking
42
Parse
Bind
Emit
wgsl
wgsl & wesl
linking
pkg
import { link } from "wesl";
import app from "./app.wesl?link";
const linked = link(app);
linked.createShaderModule(device, {});
build time
runtime
wesl-js Build-time Linking
43
Parse
Bind
Emit
wgsl
wgsl & wesl
linking
pkg
import code from "./app.wesl?static";
weslDevice.createShaderModule({code});
build time
wesl-js Runtime Linking (Future)
44
Parse
Bind
Emit
wgsl
wgsl & wesl
pkg
import { blink } from “wesl”;
import app from “./app.wesl?blink”;
const linked = blink(app);
linked.createShaderModule(device, {});
build time
runtime
WESL runs in user space
45
Browser
WGSL
HLSL
MSL
SPIR-V
Driver
WESL
Tools &
Libraries
GPU
Name mangling
Falls into categories
Currently WESL implements escaping and hashmap.
https://github.com/wgsl-tooling-wg/wesl-spec/blob/main/NameMangling.md#underscore-count-mangling
46
overrides in libs
What should their public name be?
Heck, what should happen when one does
import mylib::someOverride;
import mylib::someOverride as otherName;
import mylib::someOverride as yetAnotherName;
Ideally all 3 names would be accessible, right? A good host wrapper could make all 3 names accessible and do the rewiring in the background.
47