shader_sense/validator/
validator.rs

1//! Validator trait implemented for all languages.
2use std::path::Path;
3
4#[cfg(not(target_os = "wasi"))]
5use crate::validator::dxc::Dxc;
6use crate::{
7    shader::{ShaderParams, ShaderStage, ShadingLanguage},
8    shader_error::{ShaderDiagnosticList, ShaderError},
9    validator::{glslang::Glslang, naga::Naga},
10};
11
12/// Default include callback for [`Validator::validate_shader`]
13pub fn default_include_callback(path: &Path) -> Option<String> {
14    Some(std::fs::read_to_string(path).unwrap())
15}
16
17/// Trait that all validator must implement to validate files.
18pub trait ValidatorImpl {
19    fn validate_shader(
20        &self,
21        shader_content: &str,
22        file_path: &Path,
23        params: &ShaderParams,
24        include_callback: &mut dyn FnMut(&Path) -> Option<String>,
25    ) -> Result<ShaderDiagnosticList, ShaderError>;
26
27    fn support(&self, shader_stage: ShaderStage) -> bool;
28
29    fn get_file_name(&self, path: &Path) -> String {
30        String::from(path.file_name().unwrap().to_string_lossy())
31    }
32}
33
34/// Validator main entry point. Create this struct in order to validate shader
35///
36/// Run
37/// ```
38/// use shader_sense::validator::validator::Validator;
39/// use shader_sense::shader::ShaderParams;
40/// use std::path::Path;
41/// let shader_path = Path::new("./test/hlsl/ok.hlsl");
42/// let shader_content = std::fs::read_to_string(shader_path).unwrap();
43/// let validator = Validator::hlsl();
44/// validator.validate_shader(
45///     &shader_content,
46///     shader_path,
47///     &ShaderParams::default(),
48///     &mut |path: &Path| {
49///         Some(std::fs::read_to_string(path).unwrap())
50///     }
51/// ).unwrap();
52/// ```
53pub struct Validator {
54    imp: Box<dyn ValidatorImpl>,
55}
56impl Validator {
57    /// Create a validator for Glsl.
58    /// It will use glslang directly.
59    pub fn glsl() -> Self {
60        Self {
61            imp: Box::new(Glslang::glsl()),
62        }
63    }
64    /// Create a validator for Hlsl.
65    /// It will use DXC if it is available and fallback on glslang if its not supported.
66    /// Note that glslang support for HLSL is not as advanced as dxc.
67    pub fn hlsl() -> Self {
68        Self {
69            #[cfg(not(target_os = "wasi"))]
70            imp: match Dxc::new(Dxc::find_dxc_library()) {
71                Ok(dxc) => Box::new(dxc),
72                Err(_) => Box::new(Glslang::hlsl()), // Failed to instantiate dxc. Fallback to glslang.
73            },
74            #[cfg(target_os = "wasi")]
75            imp: Box::new(Glslang::hlsl()),
76        }
77    }
78    /// Create a validator for Wgsl.
79    /// It will use naga directly.
80    pub fn wgsl() -> Self {
81        Self {
82            imp: Box::new(Naga::new()),
83        }
84    }
85    /// Create a validator from the given [`ShadingLanguage`]
86    pub fn from_shading_language(shading_language: ShadingLanguage) -> Self {
87        match shading_language {
88            ShadingLanguage::Wgsl => Self::wgsl(),
89            ShadingLanguage::Hlsl => Self::hlsl(),
90            ShadingLanguage::Glsl => Self::glsl(),
91        }
92    }
93    /// Validate a shader and return the diagnostic list, or an error if the process failed.
94    /// If diagnostic list is empty, no error were found.
95    /// You can handle how your file will be loaded.
96    /// The include callback is being passed the already processed absolute canonicalized path of the include.
97    pub fn validate_shader(
98        &self,
99        shader_content: &str,
100        file_path: &Path,
101        params: &ShaderParams,
102        include_callback: &mut dyn FnMut(&Path) -> Option<String>, // TODO: Check if we cant pass a ref instead of a copy here.
103    ) -> Result<ShaderDiagnosticList, ShaderError> {
104        self.imp
105            .validate_shader(shader_content, file_path, params, include_callback)
106    }
107}