1#[cfg(not(target_os = "wasi"))]
2pub mod dxc;
3pub mod glslang;
4pub mod naga;
5pub mod validator;
6
7#[cfg(test)]
8mod tests {
9 use std::{collections::HashMap, path::Path};
10
11 use crate::shader::{
12 ShaderCompilationParams, ShaderContextParams, ShaderParams, ShaderStage, ShadingLanguage,
13 };
14
15 use super::validator::*;
16 use super::*;
17
18 fn create_test_validator(shading_language: ShadingLanguage) -> Box<dyn ValidatorImpl> {
19 match shading_language {
21 ShadingLanguage::Wgsl => Box::new(naga::Naga::new()),
22 #[cfg(not(target_os = "wasi"))]
23 ShadingLanguage::Hlsl => Box::new(dxc::Dxc::new().unwrap()),
24 #[cfg(target_os = "wasi")]
25 ShadingLanguage::Hlsl => Box::new(glslang::Glslang::hlsl()),
26 ShadingLanguage::Glsl => Box::new(glslang::Glslang::glsl()),
27 }
28 }
29
30 #[test]
31 fn glsl_ok() {
32 let validator = create_test_validator(ShadingLanguage::Glsl);
33 let file_path = Path::new("./test/glsl/ok.frag.glsl");
34 let shader_content = std::fs::read_to_string(file_path).unwrap();
35 match validator.validate_shader(
36 &shader_content,
37 file_path,
38 &ShaderParams::default(),
39 &mut default_include_callback,
40 ) {
41 Ok(result) => {
42 println!("Diagnostic should be empty: {:#?}", result);
43 assert!(result.is_empty())
44 }
45 Err(err) => panic!("{}", err),
46 }
47 }
48
49 #[test]
50 fn glsl_include_config() {
51 let validator = create_test_validator(ShadingLanguage::Glsl);
52 let file_path = Path::new("./test/glsl/include-config.frag.glsl");
53 let shader_content = std::fs::read_to_string(file_path).unwrap();
54 match validator.validate_shader(
55 &shader_content,
56 file_path,
57 &ShaderParams {
58 context: ShaderContextParams {
59 includes: vec!["./test/glsl/inc0/".into()],
60 ..Default::default()
61 },
62 ..Default::default()
63 },
64 &mut default_include_callback,
65 ) {
66 Ok(result) => {
67 println!("Diagnostic should be empty: {:#?}", result);
68 assert!(result.is_empty())
69 }
70 Err(err) => panic!("{}", err),
71 };
72 }
73
74 #[test]
75 fn glsl_include_level() {
76 let validator = create_test_validator(ShadingLanguage::Glsl);
77 let file_path = Path::new("./test/glsl/include-level.comp.glsl");
78 let shader_content = std::fs::read_to_string(file_path).unwrap();
79 match validator.validate_shader(
80 &shader_content,
81 file_path,
82 &ShaderParams {
83 context: ShaderContextParams {
84 includes: vec!["./test/glsl/inc0/".into()],
85 ..Default::default()
86 },
87 ..Default::default()
88 },
89 &mut default_include_callback,
90 ) {
91 Ok(result) => {
92 println!("Diagnostic should be empty: {:#?}", result);
93 assert!(result.is_empty())
94 }
95 Err(err) => panic!("{}", err),
96 };
97 }
98
99 #[test]
100 fn glsl_no_stage() {
101 let validator = create_test_validator(ShadingLanguage::Glsl);
102 let file_path = Path::new("./test/glsl/nostage.glsl");
103 let shader_content = std::fs::read_to_string(file_path).unwrap();
104 match validator.validate_shader(
105 &shader_content,
106 file_path,
107 &ShaderParams {
108 context: ShaderContextParams {
109 includes: vec!["./test/glsl/inc0/".into()],
110 ..Default::default()
111 },
112 ..Default::default()
113 },
114 &mut default_include_callback,
115 ) {
116 Ok(result) => {
117 println!("Diagnostic should be empty: {:#?}", result);
118 assert!(result.is_empty())
119 }
120 Err(err) => panic!("{}", err),
121 };
122 }
123
124 #[test]
125 fn glsl_macro() {
126 let validator = create_test_validator(ShadingLanguage::Glsl);
127 let file_path = Path::new("./test/glsl/macro.frag.glsl");
128 let shader_content = std::fs::read_to_string(file_path).unwrap();
129 match validator.validate_shader(
130 &shader_content,
131 file_path,
132 &ShaderParams {
133 context: ShaderContextParams {
134 defines: HashMap::from([("CUSTOM_MACRO".into(), "42".into())]),
135 ..Default::default()
136 },
137 ..Default::default()
138 },
139 &mut default_include_callback,
140 ) {
141 Ok(result) => {
142 println!("Diagnostic should be empty: {:#?}", result);
143 assert!(result.is_empty())
144 }
145 Err(err) => panic!("{}", err),
146 };
147 }
148
149 #[test]
150 fn glsl_error_parsing() {
151 let validator = create_test_validator(ShadingLanguage::Glsl);
152 let file_path = Path::new("./test/glsl/error-parsing.frag.glsl");
153 let shader_content = std::fs::read_to_string(file_path).unwrap();
154 match validator.validate_shader(
155 &shader_content,
156 file_path,
157 &ShaderParams::default(),
158 &mut default_include_callback,
159 ) {
160 Ok(result) => {
161 let diags = result.diagnostics;
162 println!("Diagnostic should not be empty: {:#?}", diags);
163 assert!(diags[0].range.file_path.exists());
164 assert_eq!(diags[0].error, String::from(" '#include' : Could not process include directive for header name: ./level1.glsl\n"));
165 }
166 Err(err) => panic!("{}", err),
167 };
168 }
169
170 #[test]
171 fn hlsl_ok() {
172 let validator = create_test_validator(ShadingLanguage::Hlsl);
173 let file_path = Path::new("./test/hlsl/ok.hlsl");
174 let shader_content = std::fs::read_to_string(file_path).unwrap();
175 match validator.validate_shader(
176 &shader_content,
177 file_path,
178 &ShaderParams::default(),
179 &mut default_include_callback,
180 ) {
181 Ok(result) => {
182 println!("Diagnostic should be empty: {:#?}", result);
183 assert!(result.is_empty())
184 }
185 Err(err) => panic!("{}", err),
186 };
187 }
188
189 #[test]
190 fn hlsl_include_config() {
191 let validator = create_test_validator(ShadingLanguage::Hlsl);
192 let file_path = Path::new("./test/hlsl/include-config.hlsl");
193 let shader_content = std::fs::read_to_string(file_path).unwrap();
194 match validator.validate_shader(
195 &shader_content,
196 file_path,
197 &ShaderParams {
198 context: ShaderContextParams {
199 includes: vec!["./test/hlsl/inc0/".into()],
200 ..Default::default()
201 },
202 ..Default::default()
203 },
204 &mut default_include_callback,
205 ) {
206 Ok(result) => {
207 println!("Diagnostic should be empty: {:#?}", result);
208 assert!(result.is_empty())
209 }
210 Err(err) => panic!("{}", err),
211 };
212 }
213
214 #[test]
215 fn hlsl_include_parent_folder() {
216 let validator = create_test_validator(ShadingLanguage::Hlsl);
217 let file_path = Path::new("./test/hlsl/folder/folder-file.hlsl");
218 let shader_content = std::fs::read_to_string(file_path).unwrap();
219 match validator.validate_shader(
220 &shader_content,
221 file_path,
222 &ShaderParams {
223 context: ShaderContextParams {
224 includes: vec!["./test/hlsl/".into()],
225 ..Default::default()
226 },
227 ..Default::default()
228 },
229 &mut default_include_callback,
230 ) {
231 Ok(result) => {
232 println!("Diagnostic should be empty: {:#?}", result);
233 assert!(result.is_empty())
234 }
235 Err(err) => panic!("{}", err),
236 };
237 }
238
239 #[test]
240 fn hlsl_include_level() {
241 let validator = create_test_validator(ShadingLanguage::Hlsl);
242 let file_path = Path::new("./test/hlsl/include-level.hlsl");
243 let shader_content = std::fs::read_to_string(file_path).unwrap();
244 match validator.validate_shader(
245 &shader_content,
246 file_path,
247 &ShaderParams {
248 context: ShaderContextParams {
249 includes: vec!["./test/hlsl/inc0/".into()],
250 ..Default::default()
251 },
252 compilation: ShaderCompilationParams {
253 entry_point: Some("compute".into()),
254 shader_stage: Some(ShaderStage::Compute),
255 ..Default::default()
256 },
257 ..Default::default()
258 },
259 &mut default_include_callback,
260 ) {
261 Ok(result) => {
262 println!("Diagnostic should be empty: {:#?}", result);
263 assert!(result.is_empty())
264 }
265 Err(err) => panic!("{}", err),
266 };
267 }
268
269 #[test]
270 fn hlsl_macro() {
271 let validator = create_test_validator(ShadingLanguage::Hlsl);
272 let file_path = Path::new("./test/hlsl/macro.hlsl");
273 let shader_content = std::fs::read_to_string(file_path).unwrap();
274 match validator.validate_shader(
275 &shader_content,
276 file_path,
277 &ShaderParams {
278 context: ShaderContextParams {
279 defines: HashMap::from([("CUSTOM_MACRO".into(), "42".into())]),
280 ..Default::default()
281 },
282 ..Default::default()
283 },
284 &mut default_include_callback,
285 ) {
286 Ok(result) => {
287 println!("Diagnostic should be empty: {:#?}", result);
288 assert!(result.is_empty())
289 }
290 Err(err) => panic!("{}", err),
291 };
292 }
293
294 #[test]
295 #[cfg(not(target_os = "wasi"))] fn hlsl_16bits_types_ok() {
297 use crate::shader::HlslCompilationParams;
298
299 let validator = create_test_validator(ShadingLanguage::Hlsl);
300 let file_path = Path::new("./test/hlsl/16bit-types.hlsl");
301 let shader_content = std::fs::read_to_string(file_path).unwrap();
302 match validator.validate_shader(
303 &shader_content,
304 file_path,
305 &ShaderParams {
306 compilation: ShaderCompilationParams {
307 hlsl: HlslCompilationParams {
308 enable16bit_types: true,
309 ..Default::default()
310 },
311 ..Default::default()
312 },
313 ..Default::default()
314 },
315 &mut default_include_callback,
316 ) {
317 Ok(result) => {
318 println!("Diagnostic should be empty: {:#?}", result);
319 assert!(result.is_empty())
320 }
321 Err(err) => panic!("{}", err),
322 };
323 }
324
325 #[test]
326 #[cfg(not(target_os = "wasi"))] fn hlsl_spirv_ok() {
328 use crate::shader::HlslCompilationParams;
329
330 let validator = create_test_validator(ShadingLanguage::Hlsl);
331 let file_path = Path::new("./test/hlsl/spirv-shader.hlsl");
332 let shader_content = std::fs::read_to_string(file_path).unwrap();
333 match validator.validate_shader(
335 &shader_content,
336 file_path,
337 &ShaderParams {
338 compilation: ShaderCompilationParams {
339 hlsl: HlslCompilationParams {
340 spirv: false,
341 ..Default::default()
342 },
343 ..Default::default()
344 },
345 ..Default::default()
346 },
347 &mut default_include_callback,
348 ) {
349 Ok(result) => {
350 println!("Diagnostic should not be empty: {:#?}", result);
351 assert!(!result.is_empty())
352 }
353 Err(err) => panic!("{}", err),
354 };
355 match validator.validate_shader(
357 &shader_content,
358 file_path,
359 &ShaderParams {
360 compilation: ShaderCompilationParams {
361 hlsl: HlslCompilationParams {
362 spirv: true,
363 ..Default::default()
364 },
365 ..Default::default()
366 },
367 ..Default::default()
368 },
369 &mut default_include_callback,
370 ) {
371 Ok(result) => {
372 println!("Diagnostic should be empty: {:#?}", result);
373 assert!(result.is_empty())
374 }
375 Err(err) => panic!("{}", err),
376 };
377 }
378
379 #[test]
380 fn glsl_stages() {
381 #[rustfmt::skip] let stages = vec![
383 ("graphics.vert.glsl", "VSMain", ShaderStage::Vertex),
384 ("graphics.frag.glsl", "PSMain", ShaderStage::Fragment),
385 ("graphics.geom.glsl", "GSMain", ShaderStage::Geometry),
386 ("graphics.tesc.glsl", "TCSMain", ShaderStage::TesselationControl),
387 ("graphics.tese.glsl", "TESMain", ShaderStage::TesselationEvaluation),
388 ("compute.comp.glsl", "CSMain", ShaderStage::Compute),
389 ("mesh.task.glsl", "TSMain", ShaderStage::Task),
390 ("mesh.mesh.glsl", "MSMain", ShaderStage::Mesh),
391 ("raytracing.rgen.glsl", "RayGenMain", ShaderStage::RayGeneration,),
392 ("raytracing.rint.glsl", "IntersectionMain", ShaderStage::Intersect),
393 ("raytracing.rmiss.glsl", "MissMain", ShaderStage::Miss),
394 ("raytracing.rahit.glsl", "AnyHitMain", ShaderStage::AnyHit),
395 ("raytracing.rchit.glsl", "ClosestHitMain", ShaderStage::ClosestHit),
396 ("raytracing.rcall.glsl", "CallableMain", ShaderStage::Callable),
397 ];
398 let validator = create_test_validator(ShadingLanguage::Glsl);
399 for (file_name, entry_point, shader_stage) in stages {
400 let file_path = Path::new("./test/glsl/stages/").join(file_name);
401 let shader_content = std::fs::read_to_string(&file_path).unwrap();
402 match validator.validate_shader(
403 &shader_content,
404 &file_path,
405 &ShaderParams {
406 compilation: ShaderCompilationParams {
407 entry_point: Some(entry_point.into()),
408 shader_stage: Some(shader_stage),
409 ..Default::default()
410 },
411 ..Default::default()
412 },
413 &mut default_include_callback,
414 ) {
415 Ok(result) => {
416 println!(
417 "Diagnostic should be empty for stage {:?}: {:#?}",
418 shader_stage, result
419 );
420 assert!(result.is_empty())
421 }
422 Err(err) => panic!("{}", err),
423 };
424 }
425 }
426
427 #[test]
428 fn hlsl_stages() {
429 #[rustfmt::skip] let stages = vec![
431 ("graphics.hlsl", "VSMain", ShaderStage::Vertex),
432 ("graphics.hlsl", "PSMain", ShaderStage::Fragment),
433 #[cfg(not(target_os= "wasi"))] ("graphics.hlsl", "GSMain", ShaderStage::Geometry),
435 ("graphics.hlsl", "HSMain", ShaderStage::TesselationControl),
436 ("graphics.hlsl", "DSMain", ShaderStage::TesselationEvaluation),
437 ("compute.hlsl", "CSMain", ShaderStage::Compute),
438 ("mesh.hlsl", "ASMain", ShaderStage::Task),
439 ("mesh.hlsl", "MSMain", ShaderStage::Mesh),
440 ("raytracing.hlsl", "RayGenMain", ShaderStage::RayGeneration),
441 ("raytracing.hlsl", "IntersectionMain", ShaderStage::Intersect),
442 ("raytracing.hlsl", "MissMain", ShaderStage::Miss),
443 ("raytracing.hlsl", "AnyHitMain", ShaderStage::AnyHit),
444 ("raytracing.hlsl", "ClosestHitMain", ShaderStage::ClosestHit),
445 ("raytracing.hlsl", "CallableMain", ShaderStage::Callable),
446 ];
447 let validator = create_test_validator(ShadingLanguage::Hlsl);
448 for (file_name, entry_point, shader_stage) in stages {
449 let file_path = Path::new("./test/hlsl/stages/").join(file_name);
450 let shader_content = std::fs::read_to_string(&file_path).unwrap();
451 if validator.support(shader_stage) {
453 match validator.validate_shader(
454 &shader_content,
455 &file_path,
456 &ShaderParams {
457 compilation: ShaderCompilationParams {
458 entry_point: Some(entry_point.into()),
459 shader_stage: Some(shader_stage),
460 ..Default::default()
461 },
462 ..Default::default()
463 },
464 &mut default_include_callback,
465 ) {
466 Ok(result) => {
467 println!(
468 "Diagnostic should be empty for stage {:?}: {:#?}",
469 shader_stage, result
470 );
471 assert!(result.is_empty())
472 }
473 Err(err) => panic!("{}", err),
474 };
475 }
476 }
477 }
478
479 #[test]
480 fn wgsl_stages() {
481 #[rustfmt::skip] let stages = vec![
488 ("graphics.wgsl", "VSMain", ShaderStage::Vertex),
489 ("graphics.wgsl", "PSMain", ShaderStage::Fragment),
490 ("compute.wgsl", "CSMain", ShaderStage::Compute),
491 ];
492 let validator = create_test_validator(ShadingLanguage::Wgsl);
493 for (file_name, entry_point, shader_stage) in stages {
494 let file_path = Path::new("./test/wgsl/stages/").join(file_name);
495 let shader_content = std::fs::read_to_string(&file_path).unwrap();
496 match validator.validate_shader(
497 &shader_content,
498 &file_path,
499 &ShaderParams {
500 compilation: ShaderCompilationParams {
501 entry_point: Some(entry_point.into()),
502 shader_stage: Some(shader_stage),
503 ..Default::default()
504 },
505 ..Default::default()
506 },
507 &mut default_include_callback,
508 ) {
509 Ok(result) => {
510 println!(
511 "Diagnostic should be empty for stage {:?}: {:#?}",
512 shader_stage, result
513 );
514 assert!(result.is_empty())
515 }
516 Err(err) => panic!("{}", err),
517 };
518 }
519 }
520
521 #[test]
522 fn wgsl_ok() {
523 let validator = create_test_validator(ShadingLanguage::Wgsl);
524 let file_path = Path::new("./test/wgsl/ok.wgsl");
525 let shader_content = std::fs::read_to_string(file_path).unwrap();
526 match validator.validate_shader(
527 &shader_content,
528 file_path,
529 &ShaderParams::default(),
530 &mut default_include_callback,
531 ) {
532 Ok(result) => {
533 println!("Diagnostic should be empty: {:#?}", result);
534 assert!(result.is_empty())
535 }
536 Err(err) => panic!("{}", err),
537 };
538 }
539}