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 compute_copy_depth_to_pyramid;
19pub mod compute_copy_depth_to_pyramid_multisampled;
20pub mod compute_culling;
21pub mod compute_downsample_depth_pyramid;
22pub mod cubemap_sampling_test_fragment;
23pub mod cubemap_sampling_test_vertex;
24pub mod debug_overlay_fragment;
25pub mod debug_overlay_vertex;
26pub mod di_convolution_fragment;
27pub mod generate_mipmap_fragment;
28pub mod generate_mipmap_vertex;
29pub mod light_tiling_bin_lights;
30pub mod light_tiling_clear_tiles;
31pub mod light_tiling_compute_tile_min_and_max_depth;
32pub mod light_tiling_compute_tile_min_and_max_depth_multisampled;
33pub mod light_tiling_depth_pre_pass;
34pub mod prefilter_environment_cubemap_fragment;
35pub mod prefilter_environment_cubemap_vertex;
36pub mod primitive_fragment;
37pub mod primitive_vertex;
38pub mod shadow_mapping_fragment;
39pub mod shadow_mapping_vertex;
40pub mod skybox_cubemap_fragment;
41pub mod skybox_cubemap_vertex;
42pub mod skybox_equirectangular_fragment;
43pub mod skybox_vertex;
44pub mod tonemapping_fragment;
45pub mod tonemapping_vertex;
46
47pub mod implicit_isosceles_vertex;
49pub mod passthru_fragment;
50pub mod slabbed_renderlet;
51pub mod slabbed_vertices;
52pub mod slabbed_vertices_no_instance;
53
54pub struct ShaderLinkage {
55 pub module: Arc<wgpu::ShaderModule>,
56 pub entry_point: &'static str,
57}
58
59#[cfg(test)]
60mod test {
61 use naga::valid::ValidationFlags;
62
63 #[test]
64 fn validate_shaders() {
68 fn validate_src(path: &std::path::PathBuf) {
69 log::info!("validating source");
70 log::info!(" reading '{}'", path.display());
71 let bytes = std::fs::read(path).unwrap();
72 log::info!(" {:0.2}k bytes read", bytes.len() as f32 / 1000.0);
73 let opts = naga::front::spv::Options::default();
74 let module = match naga::front::spv::parse_u8_slice(&bytes, &opts) {
75 Ok(m) => m,
76 Err(e) => {
77 log::error!("{e}");
78 panic!("SPIR-V parse error");
79 }
80 };
81 log::info!(" SPIR-V parsed");
82 let mut validator =
83 naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
84 let is_valid;
85 let info = match validator.validate(&module) {
86 Ok(i) => {
87 is_valid = true;
88 log::info!(" SPIR-V validated");
89 i
90 }
91 Err(e) => {
92 log::error!("{}", e.emit_to_string(""));
93 is_valid = false;
94 let mut validator = naga::valid::Validator::new(
95 ValidationFlags::empty(),
96 naga::valid::Capabilities::empty(),
97 );
98 validator.validate(&module).unwrap()
99 }
100 };
101 let wgsl = naga::back::wgsl::write_string(
102 &module,
103 &info,
104 naga::back::wgsl::WriterFlags::empty(),
105 )
106 .unwrap();
107 log::info!(" output WGSL generated");
108
109 let print_var_name = path
110 .file_stem()
111 .unwrap()
112 .to_str()
113 .unwrap()
114 .replace('-', "_");
115 let maybe_output_path = if std::env::var("print_wgsl").is_ok() || !is_valid {
116 let dir = std::path::PathBuf::from("../../test_output");
117 std::fs::create_dir_all(&dir).unwrap();
118 let output_path = dir.join(print_var_name).with_extension("wgsl");
119 log::info!("writing WGSL to '{}'", output_path.display());
120 Some(output_path)
121 } else {
122 log::info!(" to save the generated WGSL, use an env var 'print_wgsl=1'");
123 None
124 };
125
126 if let Some(output_path) = maybe_output_path {
127 std::fs::write(&output_path, &wgsl).unwrap();
128 log::info!(" wrote generated WGSL to {}", output_path.display());
129 }
130
131 if !is_valid {
132 panic!("SPIR-V validation error");
133 }
134
135 let module = match naga::front::wgsl::parse_str(&wgsl) {
136 Ok(m) => m,
137 Err(e) => {
138 log::error!("{}", e.emit_to_string(&wgsl));
139 panic!("wgsl parse error");
140 }
141 };
142 log::info!(" output WGSL parsed");
143 let mut validator =
144 naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
145 let _info = match validator.validate(&module) {
146 Ok(i) => i,
147 Err(e) => {
148 log::error!("{}", e.emit_to_string(&wgsl));
149 panic!("wgsl validation error");
150 }
151 };
152 log::info!(" wgsl output validated");
153 }
154
155 let may_entries = std::fs::read_dir("src/linkage").unwrap();
156 for may_entry in may_entries {
157 let entry = may_entry.unwrap();
158 let path = entry.path();
159 let ext = path.extension().unwrap().to_str().unwrap();
160 if let Ok(filename) = std::env::var("only_shader") {
161 let stem = path.file_stem().unwrap().to_str().unwrap();
162 if filename != stem {
163 log::info!(
164 " '{}' doesn't match 'only_shader' env '{}', skipping",
165 filename,
166 stem
167 );
168 continue;
169 }
170 }
171 if ext == "spv" {
172 validate_src(&path);
173 }
174 }
175 }
176}