wesl

Crates.iowesl
lib.rswesl
version0.2.0
created_at2025-02-20 00:17:04.427562+00
updated_at2025-07-28 09:32:36.731177+00
descriptionThe WESL rust compiler
homepage
repositoryhttps://github.com/wgsl-tooling-wg/wesl-rs
max_upload_size
id1562025
size666,095
Mathis Brossier (k2d222)

documentation

https://docs.rs/wesl

README

WESL: A Community Standard for Enhanced WGSL

This is the crate for all your WESL needs.

See also the standalone CLI.

Basic Usage

See [Wesl] for an overview of the high-level API.

# use wesl::{Wesl, VirtualResolver};
let compiler = Wesl::new("src/shaders");
# // just adding a virtual file here so the doctest runs without a filesystem
# let mut resolver = VirtualResolver::new();
# resolver.add_module("package::main".parse().unwrap(), "fn my_fn() {}".into());
# let compiler = compiler.set_custom_resolver(resolver);

// compile a WESL file to a WGSL string
let wgsl_str = compiler
    .compile(&"package::main".parse().unwrap())
    .inspect_err(|e| eprintln!("WESL error: {e}")) // pretty errors with `display()`
    .unwrap()
    .to_string();

Usage in build.rs

In your Rust project you probably want to have your WESL code converted automatically to a WGSL string at build-time, unless your WGSL code must be assembled at runtime.

Add this crate to your build dependencies in Cargo.toml:

[build-dependencies]
wesl = "0.1"

Create the build.rs file with the following content:

# use wesl::{Wesl, FileResolver};
fn main() {
    Wesl::new("src/shaders")
        .build_artifact(&"package::main".parse().unwrap(), "my_shader");
}

Include the compiled WGSL string in your code:

let module = device.create_shader_module(ShaderModuleDescriptor {
    label: Some("my_shader"),
    source: ShaderSource::Wgsl(include_wesl!("my_shader")),
});

Write shaders inline with the [quote_module] macro

The quote feature flag provides the quote_*! macros which let one write WGSL shaders directly in source code. This has a few advantages:

  • Like quote, it supports local variable injection. This can be used e.g. to customize a shader module at runtime.
  • The module is parsed at build-time, and syntax errors will be reported at the right location in the macro invocation.
  • WGSL and Rust have a similar syntax. Your Rust syntax highlighter will also highlight injected WGSL code.
use wesl::syntax::*; // this is necessary for the quote_module macro

// this i64 value is computed at runtime and injected into the shader.
let num_iterations = 8i64;

// the following variable has type `TranslationUnit`.
let wgsl = wesl::quote_module! {
    @fragment
    fn fs_main(@location(0) in_color: vec4<f32>) -> @location(0) vec4<f32> {
        for (let i = 0; i < #num_iterations; i++) {
            // ...
        }
        return in_color;
    }
};

One can inject variables into the following places by prefixing the name with a # symbol:

Code location Injected type Indirectly supported injection type with Into
name of a global declaration GlobalDeclaration Declaration TypeAlias Struct Function ConstAssert
name of a struct member StructMember
name of an attribute, after @ Attribute BuiltinValue InterpolateAttribute WorkgroupSizeAttribute TypeConstraint CustomAttribute
type or identifier expression Expression LiteralExpression ParenthesizedExpression NamedComponentExpression IndexingExpression UnaryExpression BinaryExpression FunctionCallExpression TypeOrIdentifierExpression and transitively: bool i64 (AbstractInt) f64 (AbstractFloat) i32 u32 f32 Ident
name of an attribute preceding and empty block statement Statement CompoundStatement AssignmentStatement IncrementStatement DecrementStatement IfStatement SwitchStatement LoopStatement ForStatement WhileStatement BreakStatement ContinueStatement ReturnStatement DiscardStatement FunctionCallStatement ConstAssertStatement DeclarationStatement
use wesl::syntax::*; // this is necessary for the quote_module macro

let inject_struct = Struct::new(Ident::new("mystruct".to_string()));
let inject_func = Function::new(Ident::new("myfunc".to_string()));
let inject_stmt = Statement::Void;
let inject_expr = 1f32;
let wgsl = wesl::quote_module! {
    struct #inject_struct { dummy: u32 } // structs cannot be empty
    fn #inject_func() {}
    fn foo() {
        @#inject_stmt {}
        let x: f32 = #inject_expr;
    }
};

Evaluating const-expressions

This is an advanced and experimental feature. wesl-rs supports evaluation and execution of WESL code with the eval feature flag. Early evaluation (in particular of const-expressions) helps developers to catch bugs early by improving the validation and error reporting capabilities of WESL. Full evaluation of const-expressions can be enabled with the lower compiler option.

Additionally, the eval feature adds support for user-defined @const attributes on functions, which allows one to precompute data ahead of time, and ensure that code has no runtime dependencies.

The eval/exec implementation is tested with the WebGPU Conformance Test Suite.

# use wesl::{Wesl, VirtualResolver, eval_str};
// ...standalone expression
let wgsl_expr = eval_str("abs(3 - 5)").unwrap().to_string();
assert_eq!(wgsl_expr, "2");

// ...expression using declarations in a WESL file
let source = "const my_const = 4; @const fn my_fn(v: u32) -> u32 { return v * 10; }";
# let mut resolver = VirtualResolver::new();
# resolver.add_module("package::source".parse().unwrap(), source.into());
# let compiler = Wesl::new_barebones().set_custom_resolver(resolver);
let wgsl_expr = compiler
    .compile(&"package::source".parse().unwrap()).unwrap()
    .eval("my_fn(my_const) + 2").unwrap()
    .to_string();
assert_eq!(wgsl_expr, "42u");

Features

name description Status/Specification
generics user-defined type-generators and generic functions experimental
package create shader libraries published to crates.io experimental
eval execute shader code on the CPU and @const attribute experimental
naga_ext enable all Naga/WGPU extensions experimental
serde derive Serialize and Deserialize for syntax nodes
Commit count: 432

cargo fmt