//! # Validation WGSL (naga) //! //! The `standard_shader.wgsl` shader is not yet loaded by a `RenderPipeline` (see Steps 3–5): //! this offline validation via `wgpu::naga` is therefore the **only** guarantee of its validity until //! it is wired in. It protects against future regressions (shader re-edits, layout changes) //! without requiring a GPU context. //! //! No new dependency is introduced: `wgpu` re-exports `naga`, already a `wsg-lib` dependency. use wgpu::naga; /// Parses and fully validates the embedded `standard_shader.wgsl` shader via naga. /// A failure here means the shader would be rejected by `Device::create_shader_module` at Step 3. #[test] fn standard_shader_is_valid_wgsl() { let src = include_str!("../src/shaders/standard_shader.wgsl"); let module = naga::front::wgsl::parse_str(src) .unwrap_or_else(|e| panic!("standard_shader.wgsl: parsing error: {e:?}")); let mut validator = naga::valid::Validator::new( naga::valid::ValidationFlags::all(), naga::valid::Capabilities::all(), ); validator .validate(&module) .unwrap_or_else(|e| panic!("standard_shader.wgsl: validation failed: {e:?}")); // Contract: exactly the two expected entry points vs_main / fs_main. assert!(module.entry_points.len() >= 2, "vs_main + fs_main expected"); } /// Parses and fully validates the embedded `shadow_shader.wgsl` shader (Step 14, D4) via naga. /// The "shadow" pipeline is wired directly by `build_shadow_pipeline` (bypassing the /// PipelineCache), so this offline validation is the guarantee of its validity. The contract /// expects a single entry point (`vs_main` — pipeline with no fragment stage). #[test] fn shadow_shader_is_valid_wgsl() { let src = include_str!("../src/shaders/shadow_shader.wgsl"); let module = naga::front::wgsl::parse_str(src) .unwrap_or_else(|e| panic!("shadow_shader.wgsl: parsing error: {e:?}")); let mut validator = naga::valid::Validator::new( naga::valid::ValidationFlags::all(), naga::valid::Capabilities::all(), ); validator .validate(&module) .unwrap_or_else(|e| panic!("shadow_shader.wgsl: validation failed: {e:?}")); let entry_names: Vec<&str> = module .entry_points .iter() .map(|ep| ep.name.as_str()) .collect(); assert_eq!(entry_names, vec!["vs_main"], "only vs_main expected"); } /// Parses and fully validates the embedded `gpu_driven.wgsl` compute shader (Phase 3, Step 15) /// via naga. The renderer compiles it directly into two `ComputePipeline`s (one per entry point), /// so this offline validation is the guarantee of its validity. The contract expects exactly the /// two compute entry points: `compute_matrices` and `cull`. #[test] fn gpu_driven_shader_is_valid_wgsl() { let src = include_str!("../src/shaders/gpu_driven.wgsl"); let module = naga::front::wgsl::parse_str(src) .unwrap_or_else(|e| panic!("gpu_driven.wgsl: parsing error: {e:?}")); let mut validator = naga::valid::Validator::new( naga::valid::ValidationFlags::all(), naga::valid::Capabilities::all(), ); validator .validate(&module) .unwrap_or_else(|e| panic!("gpu_driven.wgsl: validation failed: {e:?}")); let mut entry_names: Vec<&str> = module .entry_points .iter() .map(|ep| ep.name.as_str()) .collect(); entry_names.sort(); assert_eq!( entry_names, vec!["compute_matrices", "cull"], "the two compute entry points are expected" ); }