1use 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
49pub mod ui_fragment;
51pub mod ui_vertex;
52
53pub 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 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}