WESL Logo

WESL Extensions

WESL adds features to WGSL:

#Module System

In WESL, shader code is divided into modules. Modules have addressable names called module paths. Module paths form a tree heirarchy.

#The Module Hierarchy Starts From the File Hierarchy

File and directory names names in applications map automatically into the module path hierarchy. Typically, each module comes from a .wesl or .wgsl file. The module’s path comes from the filesystem path, with :: as a separator.

Applications storing shader code in strings can label module strings with a module path.

#Packages are Also in the Module Heirarchy

npm packages and cargo crates containing shaders also map into the module path heirarchy. The module path for a package starts with the package name, followed by ::.

The application’s own shader modules are addressable with the reserved prefix package::.

#Import Statements

import sdf::primitives::box;      // fn `box` in module `primitives` in lib package `sdf`
import package::binds::mySampler; // var `mySampler` from file `./shaders/binds.wesl` 
import super::util;               // sibling module `./util.wesl`
import package::animals::{        // ok to nest imports with `{ }`
    bird,
    mammals::{ dog, cat }
};

#Special Import Targets

#Qualified Names

WESL also allows a module paths to elements in the shader code wherever identifiers are usable. If foo() is grammatical, then so is super::util::foo().

Qualified names inline in the shader don’t require a corresponding import statement.

import package::math;

fn main() {
    package::foo::bar::my_fn();   // import statement not required.
    let x = math::constants::PI;  // extend from imported module `math` 
}

#The Application Root Module

Linking begins from an application root module. The assembled shader includes only modules transitively referenced by the root module. (The application developer is responsible for specifying the root module when linking.)

Element names in the application root module are guaranteed not be mangled during linking. Developers should place shader elements that are visible from host code into the root module. Host code visible shader elements are: entry points (@compute, @fragment, etc.), override, and @binding var. (Future versions of WESL will relax this restriction.)

#Further Import Details

Global const_assert statements in a module are included only if an element from that module is transitively referenced from the root module.

Global enable statements in a module are included only if an element from that module is transitively referenced from the root module. A corresponding enable statement must be present in the root module.

Function const_assert statements are included only if that function is transitively referenced from the root module.

See the Imports spec for futher details.

#@if attribute

The @if(condition) attribute makes the following element optionally included in the final shader, depending on the condition. The condition is determined by of feature flags which are set when you invoke the linker (wesl-js or wesl-rs).

@if(debug_mode && !raytracing_disabled) // attributes on top-level declarations 
var<private> debug_count_iterations: i32 = 0;

@if(is_2d)
alias point = vec2f;
@if(!is_2d)
alias point = vec3f;

struct Line {
    start_point: point, end_point: point,
    @if(colored) color: vec4f, // attributes on struct members
}

fn fibonacci(n: u32) {
    @if(use_lookup_table) { // attributes on blocks `{ }`
        const lut = array(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144);
        return lut[n];
    }
    // ...
}

The condition is a boolean expression containing: feature names, && (and), || (or), ! (not), true and false.

You can place attributes on declarations (fn, struct, var, const, etc.), statements, struct members, and function parameters.

#Further Conditional Details

See the Conditional Translation spec for futher details.

#Const Injection

The linkers allow host code (Rust or TypeScript/JavaScript) to define shader constants. The constant values will typically be available in the module heirarchy with the constants:: prefix.

See:

#Virtual Modules

The linkers allow host code (Rust or TypeScript/JavaScript) to provide a function that generates WESL shader code. The generated code will be available in the module heirarchy with a custom prefix provided by the caller. e.g. MyGenerated::.