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