1use super::validator::ValidatorImpl;
2use crate::{
3 include::IncludeHandler,
4 position::{ShaderFileRange, ShaderPosition},
5 shader::{GlslSpirvVersion, GlslTargetClient, ShaderParams, ShaderStage},
6 shader_error::{ShaderDiagnostic, ShaderDiagnosticList, ShaderDiagnosticSeverity, ShaderError},
7};
8use glslang::{
9 error::GlslangError,
10 include::{IncludeResult, IncludeType},
11 Compiler, CompilerOptions, ShaderInput, ShaderSource,
12};
13use std::{
14 collections::HashMap,
15 path::{Path, PathBuf},
16};
17
18impl Into<glslang::ShaderStage> for ShaderStage {
19 fn into(self) -> glslang::ShaderStage {
20 match self {
21 ShaderStage::Vertex => glslang::ShaderStage::Vertex,
22 ShaderStage::Fragment => glslang::ShaderStage::Fragment,
23 ShaderStage::Compute => glslang::ShaderStage::Compute,
24 ShaderStage::TesselationControl => glslang::ShaderStage::TesselationControl,
25 ShaderStage::TesselationEvaluation => glslang::ShaderStage::TesselationEvaluation,
26 ShaderStage::Mesh => glslang::ShaderStage::Mesh,
27 ShaderStage::Task => glslang::ShaderStage::Task,
28 ShaderStage::Geometry => glslang::ShaderStage::Geometry,
29 ShaderStage::RayGeneration => glslang::ShaderStage::RayGeneration,
30 ShaderStage::ClosestHit => glslang::ShaderStage::ClosestHit,
31 ShaderStage::AnyHit => glslang::ShaderStage::AnyHit,
32 ShaderStage::Callable => glslang::ShaderStage::Callable,
33 ShaderStage::Miss => glslang::ShaderStage::Miss,
34 ShaderStage::Intersect => glslang::ShaderStage::Intersect,
35 }
36 }
37}
38
39pub struct Glslang {
40 hlsl: bool,
41 compiler: &'static Compiler,
42
43 diagnostic_regex: regex::Regex,
45 internal_diagnostic_regex: regex::Regex,
46}
47
48impl Glslang {
49 #[allow(dead_code)] pub fn hlsl() -> Self {
51 Self::new(true)
52 }
53 pub fn glsl() -> Self {
54 Self::new(false)
55 }
56 fn new(hlsl: bool) -> Self {
57 let compiler = Compiler::acquire().expect("Failed to create glslang compiler");
58 Self {
59 hlsl: hlsl,
60 compiler,
61 diagnostic_regex: regex::Regex::new(r"(?m)^(.*?:(?: \d+:\d+:)?)").unwrap(),
62 internal_diagnostic_regex: regex::Regex::new(
63 r"(?s)^(.*?):(?: ((?:[a-zA-Z]:)?[\d\w\.\/\\\-]+):(\d+):(\d+):)?(.+)",
64 )
65 .unwrap(),
66 }
67 }
68}
69
70struct GlslangIncludeHandler<'a> {
71 include_handler: IncludeHandler,
72 include_callback: &'a mut dyn FnMut(&Path) -> Option<String>,
73}
74
75impl<'a> GlslangIncludeHandler<'a> {
76 pub fn new(
77 file_path: &'a Path,
78 includes: Vec<String>,
79 path_remapping: HashMap<PathBuf, PathBuf>,
80 include_callback: &'a mut dyn FnMut(&Path) -> Option<String>,
81 ) -> Self {
82 Self {
83 include_handler: IncludeHandler::main(file_path, includes, path_remapping),
84 include_callback: include_callback,
85 }
86 }
87}
88
89impl glslang::include::IncludeHandler for GlslangIncludeHandler<'_> {
90 fn include(
91 &mut self,
92 _ty: IncludeType, header_name: &str,
94 _includer_name: &str,
95 include_depth: usize,
96 ) -> Option<IncludeResult> {
97 if include_depth > IncludeHandler::DEPTH_LIMIT {
99 None
100 } else {
101 match self
102 .include_handler
103 .search_in_includes(Path::new(header_name), self.include_callback)
104 {
105 Some((content, path)) => {
106 self.include_handler.push_directory_stack(&path);
107 Some(IncludeResult {
108 name: String::from(header_name),
109 data: content,
110 })
111 }
112 None => None,
113 }
114 }
115 }
116}
117
118impl Glslang {
119 fn parse_errors(
120 &self,
121 errors: &String,
122 file_path: &Path,
123 params: &ShaderParams,
124 offset_first_line: bool,
125 ) -> Result<ShaderDiagnosticList, ShaderError> {
126 let mut shader_error_list = ShaderDiagnosticList::empty();
127
128 let mut starts = Vec::new();
129 for capture in self.diagnostic_regex.captures_iter(errors.as_str()) {
130 if let Some(pos) = capture.get(0) {
131 starts.push(pos.start());
132 }
133 }
134 starts.push(errors.len());
135 let mut include_handler = IncludeHandler::main(
136 file_path,
137 params.context.includes.clone(),
138 params.context.path_remapping.clone(),
139 );
140 let mut include_cache: HashMap<String, PathBuf> = HashMap::new();
142 for start in 0..starts.len() - 1 {
143 let begin = starts[start];
144 let end = starts[start + 1];
145 let block = &errors[begin..end];
146 if block.contains("compilation errors. No code generated.") {
147 continue; }
149 if let Some(capture) = self.internal_diagnostic_regex.captures(block) {
150 let level = capture.get(1).map_or("", |m| m.as_str());
151 let relative_path = capture.get(2).map_or("", |m| m.as_str());
152 let line = capture.get(3).map_or("", |m| m.as_str());
153 let pos = capture.get(4).map_or("", |m| m.as_str());
154 let msg = capture.get(5).map_or("", |m| m.as_str());
155 let file_path: PathBuf = match relative_path.parse::<u32>() {
156 Ok(_) => file_path.into(), Err(_) => {
158 if relative_path.is_empty() {
159 file_path.into()
160 } else {
161 include_cache
162 .entry(relative_path.into())
163 .or_insert_with(|| {
164 include_handler
165 .search_path_in_includes(Path::new(&relative_path))
166 .unwrap_or(file_path.into())
167 })
168 .clone()
169 }
170 }
171 };
172 let line = {
173 let offset = 1 + offset_first_line as u32;
176 let line = line.parse::<u32>().unwrap_or(offset);
177 if line < offset {
178 0
179 } else {
180 line - offset
181 }
182 };
183 let pos = pos.parse::<u32>().unwrap_or(0);
184 shader_error_list.push(ShaderDiagnostic {
185 severity: match level {
186 "ERROR" => ShaderDiagnosticSeverity::Error,
187 "WARNING" => ShaderDiagnosticSeverity::Warning,
188 "NOTE" => ShaderDiagnosticSeverity::Information,
189 "HINT" => ShaderDiagnosticSeverity::Hint,
190 _ => ShaderDiagnosticSeverity::Error,
191 },
192 error: String::from(msg),
193 range: ShaderFileRange::new(
194 file_path.clone(),
195 ShaderPosition::new(line, pos),
196 ShaderPosition::new(line, pos),
197 ),
198 });
199 } else {
200 return Err(ShaderError::InternalErr(format!(
201 "Failed to parse regex: {}",
202 block
203 )));
204 }
205 }
206
207 if shader_error_list.is_empty() {
208 return Err(ShaderError::InternalErr(format!(
209 "Failed to parse errors: {}",
210 errors
211 )));
212 }
213 return Ok(shader_error_list);
214 }
215
216 fn from_glslang_error(
217 &self,
218 err: GlslangError,
219 file_path: &Path,
220 params: &ShaderParams,
221 offset_first_line: bool,
222 ) -> Result<ShaderDiagnosticList, ShaderError> {
223 match err {
224 GlslangError::PreprocessError(error) => {
225 self.parse_errors(&error, file_path, ¶ms, offset_first_line)
226 }
227 GlslangError::ParseError(error) => {
228 self.parse_errors(&error, file_path, ¶ms, offset_first_line)
229 }
230 GlslangError::LinkError(error) => {
231 self.parse_errors(&error, file_path, ¶ms, offset_first_line)
232 }
233 GlslangError::ShaderStageNotFound(stage) => Err(ShaderError::InternalErr(format!(
234 "Shader stage not found: {:#?}",
235 stage
236 ))),
237 GlslangError::InvalidProfile(target, value, profile) => {
238 Err(ShaderError::InternalErr(format!(
239 "Invalid profile {} for target {:#?}: {:#?}",
240 value, target, profile
241 )))
242 }
243 GlslangError::VersionUnsupported(value, profile) => Err(ShaderError::InternalErr(
244 format!("Unsupported profile {}: {:#?}", value, profile),
245 )),
246 err => Err(ShaderError::InternalErr(format!(
247 "Internal error: {:#?}",
248 err
249 ))),
250 }
251 }
252}
253impl ValidatorImpl for Glslang {
254 fn validate_shader(
255 &self,
256 content: &str,
257 file_path: &Path,
258 params: &ShaderParams,
259 include_callback: &mut dyn FnMut(&Path) -> Option<String>,
260 ) -> Result<ShaderDiagnosticList, ShaderError> {
261 let file_name = self.get_file_name(file_path);
262
263 let (shader_stage, shader_source, offset_first_line) =
264 if let Some(variant_stage) = params.compilation.shader_stage {
265 (variant_stage, content.into(), false)
266 } else if let Some(shader_stage) = ShaderStage::from_file_name(&file_name) {
267 (shader_stage, content.into(), false)
268 } else {
269 let default_stage = ShaderStage::Fragment;
272 if self.hlsl {
273 (default_stage, content.into(), false)
275 } else {
276 if content.contains("#version ") {
279 (default_stage, content.into(), false)
281 } else {
282 let version_header = String::from("#version 450\n");
285 (default_stage, version_header + content, true)
286 }
287 }
288 };
289
290 let source = ShaderSource::try_from(shader_source).expect("Failed to read from source");
291
292 let defines_copy = params.context.defines.clone();
293 let defines: Vec<(&str, Option<&str>)> = defines_copy
294 .iter()
295 .map(|v| (&v.0 as &str, Some(&v.1 as &str)))
296 .collect();
297 let mut include_handler = GlslangIncludeHandler::new(
298 file_path,
299 params.context.includes.clone(),
300 params.context.path_remapping.clone(),
301 include_callback,
302 );
303
304 let lang_version = match params.compilation.glsl.spirv {
305 GlslSpirvVersion::SPIRV1_0 => glslang::SpirvVersion::SPIRV1_0,
306 GlslSpirvVersion::SPIRV1_1 => glslang::SpirvVersion::SPIRV1_1,
307 GlslSpirvVersion::SPIRV1_2 => glslang::SpirvVersion::SPIRV1_2,
308 GlslSpirvVersion::SPIRV1_3 => glslang::SpirvVersion::SPIRV1_3,
309 GlslSpirvVersion::SPIRV1_4 => glslang::SpirvVersion::SPIRV1_4,
310 GlslSpirvVersion::SPIRV1_5 => glslang::SpirvVersion::SPIRV1_5,
311 GlslSpirvVersion::SPIRV1_6 => glslang::SpirvVersion::SPIRV1_6,
312 };
313 let input = match ShaderInput::new(
314 &source,
315 shader_stage.into(),
316 &CompilerOptions {
317 source_language: if self.hlsl {
318 glslang::SourceLanguage::HLSL
319 } else {
320 glslang::SourceLanguage::GLSL
321 },
322 target: if self.hlsl {
324 glslang::Target::None(Some(lang_version))
325 } else {
326 if params.compilation.glsl.client.is_opengl() {
327 glslang::Target::OpenGL {
328 version: glslang::OpenGlVersion::OpenGL4_5,
329 spirv_version: None, }
331 } else {
332 let client_version = match params.compilation.glsl.client {
333 GlslTargetClient::Vulkan1_0 => glslang::VulkanVersion::Vulkan1_0,
334 GlslTargetClient::Vulkan1_1 => glslang::VulkanVersion::Vulkan1_1,
335 GlslTargetClient::Vulkan1_2 => glslang::VulkanVersion::Vulkan1_2,
336 GlslTargetClient::Vulkan1_3 => glslang::VulkanVersion::Vulkan1_3,
337 _ => unreachable!(),
338 };
339 glslang::Target::Vulkan {
340 version: client_version,
341 spirv_version: lang_version,
342 }
343 }
344 },
345 messages: glslang::ShaderMessage::CASCADING_ERRORS
346 | glslang::ShaderMessage::DEBUG_INFO
347 | glslang::ShaderMessage::DISPLAY_ERROR_COLUMN
348 | if self.hlsl && params.compilation.hlsl.enable16bit_types {
349 glslang::ShaderMessage::HLSL_ENABLE_16BIT_TYPES
350 } else {
351 glslang::ShaderMessage::DEFAULT
352 },
353 ..Default::default()
354 },
355 Some(&defines),
356 Some(&mut include_handler),
357 )
358 .map_err(|e| self.from_glslang_error(e, file_path, ¶ms, offset_first_line))
359 {
360 Ok(value) => value,
361 Err(error) => match error {
362 Err(error) => return Err(error),
363 Ok(diag) => return Ok(diag),
364 },
365 };
366 let _shader = match glslang::Shader::new(&self.compiler, input)
367 .map_err(|e| self.from_glslang_error(e, file_path, ¶ms, offset_first_line))
368 {
369 Ok(value) => value,
370 Err(error) => match error {
371 Err(error) => return Err(error),
372 Ok(diag) => return Ok(diag),
373 },
374 };
375 Ok(ShaderDiagnosticList::empty()) }
392 fn support(&self, shader_stage: ShaderStage) -> bool {
393 if self.hlsl {
394 match shader_stage {
395 ShaderStage::Vertex
396 | ShaderStage::Fragment
397 | ShaderStage::Compute
398 | ShaderStage::Geometry
399 | ShaderStage::TesselationControl
400 | ShaderStage::TesselationEvaluation => true,
401 _ => false,
402 }
403 } else {
404 true }
406 }
407}