Skip to main content

renderling/
linkage.rs

1//! Provides convenient wrappers around renderling shader linkage.
2//!
3//! For internal use.
4// # Warning!
5// Please don't put anything in `crates/renderling/src/linkage/*`.
6// The files there are all auto-generated by the shader compilation machinery.
7// It is common to delete everything in that directory and regenerate.
8use std::sync::Arc;
9
10pub mod atlas_blit_fragment;
11pub mod atlas_blit_vertex;
12pub mod bloom_downsample_fragment;
13pub mod bloom_mix_fragment;
14pub mod bloom_upsample_fragment;
15pub mod bloom_vertex;
16pub mod brdf_lut_convolution_fragment;
17pub mod brdf_lut_convolution_vertex;
18pub mod compositor_fragment;
19pub mod compositor_vertex;
20pub mod compute_copy_depth_to_pyramid;
21pub mod compute_copy_depth_to_pyramid_multisampled;
22pub mod compute_culling;
23pub mod compute_downsample_depth_pyramid;
24pub mod cubemap_sampling_test_fragment;
25pub mod cubemap_sampling_test_vertex;
26pub mod debug_overlay_fragment;
27pub mod debug_overlay_vertex;
28pub mod di_convolution_fragment;
29pub mod generate_mipmap_fragment;
30pub mod generate_mipmap_vertex;
31pub mod light_tiling_bin_lights;
32pub mod light_tiling_clear_tiles;
33pub mod light_tiling_compute_tile_min_and_max_depth;
34pub mod light_tiling_compute_tile_min_and_max_depth_multisampled;
35pub mod light_tiling_depth_pre_pass;
36pub mod prefilter_environment_cubemap_fragment;
37pub mod prefilter_environment_cubemap_vertex;
38pub mod primitive_fragment;
39pub mod primitive_vertex;
40pub mod shadow_mapping_fragment;
41pub mod shadow_mapping_vertex;
42pub mod skybox_cubemap_fragment;
43pub mod skybox_cubemap_vertex;
44pub mod skybox_equirectangular_fragment;
45pub mod skybox_vertex;
46pub mod tonemapping_fragment;
47pub mod tonemapping_vertex;
48
49// 2D/UI shaders
50pub mod ui_fragment;
51pub mod ui_vertex;
52
53// Tutorial shaders
54pub mod implicit_isosceles_vertex;
55pub mod passthru_fragment;
56pub mod slabbed_renderlet;
57pub mod slabbed_vertices;
58pub mod slabbed_vertices_no_instance;
59
60pub struct ShaderLinkage {
61    pub module: Arc<wgpu::ShaderModule>,
62    pub entry_point: &'static str,
63}
64
65#[cfg(test)]
66mod test {
67    use naga::valid::ValidationFlags;
68
69    #[test]
70    // Ensure that the shaders can be converted to WGSL.
71    // This is necessary for WASM using WebGPU, because WebGPU only accepts
72    // WGSL as a shading language.
73    fn validate_shaders() {
74        fn validate_src(path: &std::path::PathBuf) {
75            log::info!("validating source");
76            log::info!("  reading '{}'", path.display());
77            let bytes = std::fs::read(path).unwrap();
78            log::info!("  {:0.2}k bytes read", bytes.len() as f32 / 1000.0);
79            let opts = naga::front::spv::Options::default();
80            let module = match naga::front::spv::parse_u8_slice(&bytes, &opts) {
81                Ok(m) => m,
82                Err(e) => {
83                    log::error!("{e}");
84                    panic!("SPIR-V parse error");
85                }
86            };
87            log::info!("  SPIR-V parsed");
88            let mut validator =
89                naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
90            let is_valid;
91            let info = match validator.validate(&module) {
92                Ok(i) => {
93                    is_valid = true;
94                    log::info!("  SPIR-V validated");
95                    i
96                }
97                Err(e) => {
98                    log::error!("{}", e.emit_to_string(""));
99                    is_valid = false;
100                    let mut validator = naga::valid::Validator::new(
101                        ValidationFlags::empty(),
102                        naga::valid::Capabilities::empty(),
103                    );
104                    validator.validate(&module).unwrap()
105                }
106            };
107            let wgsl = naga::back::wgsl::write_string(
108                &module,
109                &info,
110                naga::back::wgsl::WriterFlags::empty(),
111            )
112            .unwrap();
113            log::info!("  output WGSL generated");
114
115            let print_var_name = path
116                .file_stem()
117                .unwrap()
118                .to_str()
119                .unwrap()
120                .replace('-', "_");
121            let maybe_output_path = if std::env::var("print_wgsl").is_ok() || !is_valid {
122                let dir = std::path::PathBuf::from("../../test_output");
123                std::fs::create_dir_all(&dir).unwrap();
124                let output_path = dir.join(print_var_name).with_extension("wgsl");
125                log::info!("writing WGSL to '{}'", output_path.display());
126                Some(output_path)
127            } else {
128                log::info!("  to save the generated WGSL, use an env var 'print_wgsl=1'");
129                None
130            };
131
132            if let Some(output_path) = maybe_output_path {
133                std::fs::write(&output_path, &wgsl).unwrap();
134                log::info!("  wrote generated WGSL to {}", output_path.display());
135            }
136
137            if !is_valid {
138                panic!("SPIR-V validation error");
139            }
140
141            let module = match naga::front::wgsl::parse_str(&wgsl) {
142                Ok(m) => m,
143                Err(e) => {
144                    log::error!("{}", e.emit_to_string(&wgsl));
145                    panic!("wgsl parse error");
146                }
147            };
148            log::info!("  output WGSL parsed");
149            let mut validator =
150                naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
151            let _info = match validator.validate(&module) {
152                Ok(i) => i,
153                Err(e) => {
154                    log::error!("{}", e.emit_to_string(&wgsl));
155                    panic!("wgsl validation error");
156                }
157            };
158            log::info!("  wgsl output validated");
159        }
160
161        let may_entries = std::fs::read_dir("src/linkage").unwrap();
162        for may_entry in may_entries {
163            let entry = may_entry.unwrap();
164            let path = entry.path();
165            let ext = path.extension().unwrap().to_str().unwrap();
166            if let Ok(filename) = std::env::var("only_shader") {
167                let stem = path.file_stem().unwrap().to_str().unwrap();
168                if filename != stem {
169                    log::info!(
170                        "  '{}' doesn't match 'only_shader' env '{}', skipping",
171                        filename,
172                        stem
173                    );
174                    continue;
175                }
176            }
177            if ext == "spv" {
178                validate_src(&path);
179            }
180        }
181    }
182}