Restore abs() in the rect shader, and validate every shader without a device

`7c50a3e` renamed a length's `abs` component to `px` and took the WGSL `abs()`
builtin in the rounded-rect distance with it, so every window failed shader
validation on the first frame while `cargo test` stayed green. `naga` is
reachable through `wgpu`, so a unit test now composes each shader file with the
prelude the way the renderer does and parses and validates it; it reads the
shader directory rather than naming primitives, so a new one is covered by
adding its file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Fable 5.1 committed 2026-09-15 02:16:56 -04:00
1 parent 02ff8c7454
commit f61e8936f1
2 files changed
+38 -2

No files matched your search

+37 -1
View File
@@ -22,6 +22,10 @@ pub use primitive::*;
const PRELUDE: &str = include_str!("./shader/prelude.wgsl");
fn module_source(wgsl: &str) -> String {
format!("{PRELUDE}\n{wgsl}")
}
pub struct UiRenderNode {
shared_layout: BindGroupLayout,
shared_group: BindGroup,
@@ -222,7 +226,7 @@ impl UiRenderNode {
) -> RenderPipeline {
let module = device.create_shader_module(ShaderModuleDescriptor {
label: Some(label),
source: ShaderSource::Wgsl(format!("{PRELUDE}\n{wgsl}").into()),
source: ShaderSource::Wgsl(module_source(wgsl).into()),
});
device.create_render_pipeline(&RenderPipelineDescriptor {
label: Some(label),
@@ -401,3 +405,35 @@ impl ListBuffers {
}
}
}
#[cfg(test)]
mod tests {
use super::module_source;
use wgpu::naga::{
front::wgsl,
valid::{Capabilities, ValidationFlags, Validator},
};
/// Every shader file, composed as the renderer composes it, parses and
/// validates with no device -- so an edit that breaks one fails here and
/// not in the first window opened.
#[test]
fn every_shader_validates() {
let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/src/render/shader");
let mut checked = 0;
for entry in std::fs::read_dir(dir).unwrap() {
let path = entry.unwrap().path();
if path.extension().is_none_or(|e| e != "wgsl") || path.ends_with("prelude.wgsl") {
continue;
}
let source = module_source(&std::fs::read_to_string(&path).unwrap());
let module = wgsl::parse_str(&source)
.unwrap_or_else(|e| panic!("{}: {}", path.display(), e.emit_to_string(&source)));
Validator::new(ValidationFlags::all(), Capabilities::all())
.validate(&module)
.unwrap_or_else(|e| panic!("{}: {e:?}", path.display()));
checked += 1;
}
assert!(checked > 0, "no shaders found in {dir}");
}
}
+1 -1
View File
@@ -34,6 +34,6 @@ fn distance_from_rect(pixel_pos: vec2<f32>, rect_center: vec2<f32>, rect_corner:
// vec from center to pixel
let p = pixel_pos - rect_center;
// vec from inner rect corner to pixel
let q = px(p) - (rect_corner - radius);
let q = abs(p) - (rect_corner - radius);
return length(max(q, vec2(0.0))) - radius;
}