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