Merge PR #2989: GPU-TESTS: every WGSL kernel parses and validates in the test run, without a GPU

This commit is contained in:
aiongg
2026-10-31 02:11:06 +00:00
4 changed files with 60 additions and 0 deletions
+11
View File
@@ -865,6 +865,17 @@ pub fn groups1(n: usize) -> [u32; 3] {
#[cfg(test)]
mod tests {
/// Every kernel module, as it is handed to the device (the generated constants, the bindings, `common.wgsl`, the
/// module), parses and validates without a GPU.
#[test]
fn every_kernel_module_validates_without_a_gpu() {
let consts = super::constants();
for m in super::MODULES {
let name = m.entries.first().copied().unwrap_or("?");
assert_eq!(crate::validate_wgsl(name, &super::module_source(m, &consts)), Ok(()));
}
}
#[test]
fn limits_override_parses() {
assert_eq!(super::parse_limits("webgpu"), Some((128 << 20, 256 << 20)));
+7
View File
@@ -817,6 +817,13 @@ static PER_CONV: AtomicBool = AtomicBool::new(false);
mod tests {
use super::*;
/// The face detector's kernels validate without a GPU.
#[test]
fn the_face_kernels_validate_without_a_gpu() {
assert_eq!(crate::validate_wgsl("faces_conv", include_str!("wgsl/faces_conv.wgsl")), Ok(()));
assert_eq!(crate::validate_wgsl("faces_ops", include_str!("wgsl/faces_ops.wgsl")), Ok(()));
}
fn gpu() -> Option<Arc<FaceGpu>> {
match accelerator() {
Ok(a) => Some(a),
+23
View File
@@ -572,10 +572,33 @@ pub fn try_render(
}
}
/// Tests: parse and validate a WGSL module with naga, as wgpu does before it hands a shader to the driver. Needs no
/// GPU, so kernels that don't build (a duplicate definition after a merge, a type error) fail every test run, not only
/// the GPU ones.
#[cfg(test)]
pub(crate) fn validate_wgsl(name: &str, src: &str) -> Result<(), String> {
use wgpu::naga;
let module = naga::front::wgsl::parse_str(src).map_err(|e| format!("{name}: {}", e.emit_to_string(src)))?;
naga::valid::Validator::new(naga::valid::ValidationFlags::all(), naga::valid::Capabilities::all())
.validate(&module)
.map(|_| ())
.map_err(|e| format!("{name}: {}", e.emit_to_string(src)))
}
#[cfg(test)]
mod tests {
use lightcraft_pipeline::{OutputDepth, Overlay};
/// The helper refuses what a driver would: a function defined twice (what an automatic merge of two branches
/// touching the same kernel can leave).
#[test]
fn wgsl_validation_catches_a_duplicate_definition() {
let ok = "fn f() -> f32 { return 1.0; }\n@compute @workgroup_size(1) fn main() { _ = f(); }\n";
assert_eq!(super::validate_wgsl("ok", ok), Ok(()));
let twice = format!("fn f() -> f32 {{ return 2.0; }}\n{ok}");
assert!(super::validate_wgsl("twice", &twice).is_err());
}
// Given a request the 8-bit kernel can't draw (high bit depth, a soft proof, Visualize HDR),
// the GPU doesn't take it; the loupe and diagnostic overlays it can draw stay on the GPU
#[test]
+19
View File
@@ -925,6 +925,25 @@ impl TileRunner for NetRunner {
#[cfg(test)]
mod tests {
use super::*;
/// The denoise kernels validate without a GPU: the pooling kernel, and the convolution for every block size the
/// planner picks for these layer widths and channel counts.
#[test]
fn the_network_kernels_validate_without_a_gpu() {
assert_eq!(crate::validate_wgsl("nn_pool", include_str!("wgsl/nn_pool.wgsl")), Ok(()));
let mut sizes: Vec<(u32, u32, u32)> = Vec::new();
for n in [1usize, 3, 8, 16, 24, 32, 48, 64, 96, 128, 256, 512] {
for cin in [1usize, 3, 4, 8, 16, 32, 48, 64, 128, 256, 512] {
let b = blocks(n, cin);
if !sizes.contains(&b) {
sizes.push(b);
}
}
}
for (bm, bn, bk) in sizes {
assert_eq!(crate::validate_wgsl(&format!("nn_conv {bm}x{bn}x{bk}"), &conv_source(bm, bn, bk)), Ok(()));
}
}
use lightcraft_denoise::{onnx, reference, synthetic};
fn net(tile: u64, ch: u64, depth: usize, seed: u64) -> Net {