A simple preprocessor for the WebGpu Shading Language.
I needed a preprocessor for some libraries I'm making but found that
wgsl
will not get an official preprocessor, at least not until 1.0. There are
other options like wgsl_preprocessor
and bevy
's preprocessor, but
wgsl_preprocessor
does not have support for conditional code, and bevy
's
preprocessor isn't available as a seperate package.
- Zero dependencies (< 1s compile time on M1 mac)
- Advanced expressions in preprocessor directives:
- Math and comparisons
//:if (1.0 - size * 5.0) / 10.0 > 26.0 //:if width != height || height > 500
- Bit operations
//:if BIT_FLAGS & BIT_3 //:if BIT_FLAGS & 0b1000
- Math and comparisons
- Preprocessor directives are comments, they will not mess up your editor's highlighting
To use wgsl_plus
's features, you need to create a WgslWorkspace
and load
the source code of your shaders into it, this can be done by pointing it to a
folder containing your shaders, or by manually specifying shaders and their
paths:
Automatically:
let mut workspace = WgslWorkspace::scan("shaders").unwrap();
Manually:
let mut workspace = WgslWorkspace::from_memory("shaders", &[
("my-shader.wgsl", include_str!("shaders/my-shader.wgsl")),
("vertex.wgsl", include_str!("shaders/vertex.wgsl")),
]).unwrap();
Then just request the shader like this:
let shader = workspace.get_shader("my-shader.wgsl").unwrap();
This will give you a string containing the source code of your preprocessed shader, that's it.
Now you can set variables to use in your shaders like this:
workspace.set_global_i64("SAMPLE_SIZE", 64);
workspace.set_global_f64("QUALITY", 5.0);
workspace.set_global_bool("DO_STUFF", false);
WGSL Syntax:
//:if <condition> ... //:else ... //:end
Include or exclude a piece of code if a certain condition is true.
Shader code
fn calculate_lighting(idx: u32) -> vec3<f32> {
let light = lights[idx];
//:if quality >= 4.0
let result = do_some_fancy_stuff(light);
//:else
let result = do_the_cheaper_version(light);
//:end
return result;
}
Rust code:
workspace.set_global_f64("quality", 5.0);
Resulting shader:
fn calculate_lighting(idx: u32) -> vec3<f32> {
let light = lights[idx];
let result = do_some_fancy_stuff(light);
return result;
}
WGSL Syntax:
//:const <name>
Insert a variable into the shader as a constant.
Shader code:
//:const SAMPLE_SIZE
Rust code
workspace.set_global_i64("SAMPLE_SIZE", 64);
Resulting shader:
const SAMPLE_SIZE = 64;
WGSL Syntax:
//:include <path>
Include a file into this shader (path is relative to the workspace root).
Shader code (main.wgsl
):
const SOME_CONSTANT = 15;
//:include math.wgsl
fn pi_times_constant() -> f32 {
return SOME_CONSTANT * PI;
}
Shader code (math.wgsl
):
const PI: f32 = 3.1415926535897932384626433832795;
fn pi_multiplied_by(number: f32) -> f32 {
return PI * number;
}
Resulting shader:
const SOME_CONSTANT = 15;
const PI: f32 = 3.1415926535897932384626433832795;
fn pi_multiplied_by(number: f32) -> f32 {
return PI * number;
}
fn pi_times_constant() -> f32 {
return SOME_CONSTANT * PI;
}