Crates.io | wesl |
lib.rs | wesl |
version | 0.2.0 |
created_at | 2025-02-20 00:17:04.427562+00 |
updated_at | 2025-07-28 09:32:36.731177+00 |
description | The WESL rust compiler |
homepage | |
repository | https://github.com/wgsl-tooling-wg/wesl-rs |
max_upload_size | |
id | 1562025 |
size | 666,095 |
This is the crate for all your WESL needs.
See also the standalone CLI.
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();
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")),
});
quote_module
] macroThe quote
feature flag provides the quote_*!
macros which let one write WGSL shaders
directly in source code. This has a few advantages:
quote
, it supports local variable injection. This can be
used e.g. to customize a shader module at runtime.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;
}
};
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");
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 |