Skip to content

Commit 8144e88

Browse files
committed
[shaders] Support "enable" via post-process directive
The WGSL source currently cannot contain the "enable" directive as naga doesn't support it (see gfx-rs/wgpu#5476). This patch works around this limitation by introducing the "#enable" post-process directive. Lines that start with "#enable" get turned into an inline comment during the pre-process stage and converted to a standard "enable" directive following the intermediate module compilation step. This allows us to pass the WGSL shaders with enable directives on to other WebGPU implementations.
1 parent a9ede86 commit 8144e88

File tree

4 files changed

+50
-9
lines changed

4 files changed

+50
-9
lines changed

crates/shaders/src/compile/mod.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl ShaderInfo {
120120
bindings.sort_by_key(|res| res.location);
121121
let workgroup_size = entry.workgroup_size;
122122
Ok(ShaderInfo {
123-
source,
123+
source: postprocess(&source),
124124
module,
125125
module_info,
126126
workgroup_size,
@@ -182,3 +182,19 @@ impl ShaderInfo {
182182
info
183183
}
184184
}
185+
186+
// TODO: This is a workaround for gfx-rs/wgpu#5476. Since naga can't handle the `enable` directive,
187+
// we allow its use in other WGSL compilers using our own "#enable" post-process directive. Remove
188+
// this mechanism once naga supports the directive.
189+
fn postprocess(wgsl: &str) -> String {
190+
let mut output = String::with_capacity(wgsl.len());
191+
for line in wgsl.lines() {
192+
if line.starts_with("//__#enable") {
193+
output.push_str(&line["//__#".len()..]);
194+
} else {
195+
output.push_str(line);
196+
}
197+
output.push('\n');
198+
}
199+
output
200+
}

crates/shaders/src/compile/preprocess.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ pub fn preprocess(
6565
let directive_is_at_start = line.trim_start().starts_with('#');
6666

6767
match directive {
68-
if_item @ ("ifdef" | "ifndef" | "else" | "endif") if !directive_is_at_start => {
68+
item @ ("ifdef" | "ifndef" | "else" | "endif" | "enable")
69+
if !directive_is_at_start =>
70+
{
6971
eprintln!(
70-
"#{if_item} directives must be the first non_whitespace items on \
72+
"#{item} directives must be the first non_whitespace items on \
7173
their line, ignoring (line {line_number})"
7274
);
7375
break;
@@ -111,7 +113,7 @@ pub fn preprocess(
111113
eprintln!("Mismatched endif (line {line_number})");
112114
}
113115
let remainder = directive_start[directive_len..].trim();
114-
if !remainder.is_empty() {
116+
if !remainder.is_empty() && !remainder.starts_with("//") {
115117
eprintln!(
116118
"#endif directives don't take an argument. `{remainder}` will \
117119
not be in output (line {line_number})"
@@ -151,6 +153,16 @@ pub fn preprocess(
151153
}
152154
continue;
153155
}
156+
"enable" => {
157+
// Turn this directive into a comment. It will be handled as part in
158+
// postprocess.
159+
if stack.iter().all(|item| item.active) {
160+
output.push_str("//__");
161+
output.push_str(line);
162+
output.push('\n');
163+
}
164+
continue 'all_lines;
165+
}
154166
val => {
155167
eprintln!("Unknown preprocessor directive `{val}` (line {line_number})");
156168
}

shader/fine.wgsl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
// To enable multisampled rendering, turn on both the msaa ifdef and one of msaa8
88
// or msaa16.
99

10+
#ifdef r8
11+
// The R8 variant is only available via an internal extension in Dawn
12+
// (see https://dawn.googlesource.com/dawn/+/refs/heads/main/docs/tint/extensions/chromium_internal_graphite.md).
13+
#enable chromium_internal_graphite;
14+
#endif
15+
1016
struct Tile {
1117
backdrop: i32,
1218
segments: u32,
@@ -34,10 +40,6 @@ var<storage> info: array<u32>;
3440

3541
@group(0) @binding(4)
3642
#ifdef r8
37-
// The R8 variant is available via a non-standard extension in Dawn. We can't
38-
// optionally declare that extension here since naga doesn't understand the
39-
// `enable` directive (see https://github.com/gfx-rs/wgpu/issues/5476). The
40-
// directive must be injected by the client via some other means.
4143
var output: texture_storage_2d<r8unorm, write>;
4244
#else
4345
var output: texture_storage_2d<rgba8unorm, write>;
@@ -816,6 +818,8 @@ fn extend_mode(t: f32, mode: u32) -> f32 {
816818

817819
let PIXELS_PER_THREAD = 4u;
818820

821+
#ifndef msaa
822+
819823
// Analytic area antialiasing.
820824
//
821825
// This is currently dead code if msaa is enabled, but it would be fairly straightforward
@@ -878,6 +882,8 @@ fn fill_path(fill: CmdFill, xy: vec2<f32>, result: ptr<function, array<f32, PIXE
878882
*result = area;
879883
}
880884

885+
#endif
886+
881887
// The X size should be 16 / PIXELS_PER_THREAD
882888
@compute @workgroup_size(4, 16)
883889
fn main(

src/shaders/preprocess.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ pub fn preprocess(input: &str, defines: &HashSet<String>, imports: &HashMap<&str
6969
let directive_is_at_start = line.trim_start().starts_with('#');
7070

7171
match directive {
72-
if_item @ ("ifdef" | "ifndef" | "else" | "endif") if !directive_is_at_start => {
72+
if_item @ ("ifdef" | "ifndef" | "else" | "endif" | "enable")
73+
if !directive_is_at_start =>
74+
{
7375
eprintln!("#{if_item} directives must be the first non_whitespace items on their line, ignoring (line {line_number})");
7476
break;
7577
}
@@ -142,6 +144,11 @@ pub fn preprocess(input: &str, defines: &HashSet<String>, imports: &HashMap<&str
142144
}
143145
continue;
144146
}
147+
"enable" => {
148+
// Ignore post-process directive. This is only supported by the shaders crate
149+
// (see #467)
150+
continue 'all_lines;
151+
}
145152
val => {
146153
eprintln!("Unknown preprocessor directive `{val}` (line {line_number})");
147154
}

0 commit comments

Comments
 (0)