can define a value from inside a shader (#7518)
# Objective - Fixes #7494 - It is now possible to define a ShaderDef from inside a shader. This can be useful to centralise a value, or making sure an import is only interpreted once ## Solution - Support `#define <SHADERDEF_NAME> <optional value>`
This commit is contained in:
		
							parent
							
								
									19368441f3
								
							
						
					
					
						commit
						b056475360
					
				| @ -316,6 +316,11 @@ pub enum ProcessShaderError { | |||||||
|         expected: String, |         expected: String, | ||||||
|         value: String, |         value: String, | ||||||
|     }, |     }, | ||||||
|  |     #[error("Invalid shader def definition for '{shader_def_name}': {value}")] | ||||||
|  |     InvalidShaderDefDefinitionValue { | ||||||
|  |         shader_def_name: String, | ||||||
|  |         value: String, | ||||||
|  |     }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub struct ShaderImportProcessor { | pub struct ShaderImportProcessor { | ||||||
| @ -388,6 +393,7 @@ pub struct ShaderProcessor { | |||||||
|     else_ifdef_regex: Regex, |     else_ifdef_regex: Regex, | ||||||
|     else_regex: Regex, |     else_regex: Regex, | ||||||
|     endif_regex: Regex, |     endif_regex: Regex, | ||||||
|  |     define_regex: Regex, | ||||||
|     def_regex: Regex, |     def_regex: Regex, | ||||||
|     def_regex_delimited: Regex, |     def_regex_delimited: Regex, | ||||||
| } | } | ||||||
| @ -397,10 +403,11 @@ impl Default for ShaderProcessor { | |||||||
|         Self { |         Self { | ||||||
|             ifdef_regex: Regex::new(r"^\s*#\s*ifdef\s*([\w|\d|_]+)").unwrap(), |             ifdef_regex: Regex::new(r"^\s*#\s*ifdef\s*([\w|\d|_]+)").unwrap(), | ||||||
|             ifndef_regex: Regex::new(r"^\s*#\s*ifndef\s*([\w|\d|_]+)").unwrap(), |             ifndef_regex: Regex::new(r"^\s*#\s*ifndef\s*([\w|\d|_]+)").unwrap(), | ||||||
|             ifop_regex: Regex::new(r"^\s*#\s*if\s*([\w|\d|_]+)\s*([^\s]*)\s*([\w|\d]+)").unwrap(), |             ifop_regex: Regex::new(r"^\s*#\s*if\s*([\w|\d|_]+)\s*([^\s]*)\s*([-\w|\d]+)").unwrap(), | ||||||
|             else_ifdef_regex: Regex::new(r"^\s*#\s*else\s+ifdef\s*([\w|\d|_]+)").unwrap(), |             else_ifdef_regex: Regex::new(r"^\s*#\s*else\s+ifdef\s*([\w|\d|_]+)").unwrap(), | ||||||
|             else_regex: Regex::new(r"^\s*#\s*else").unwrap(), |             else_regex: Regex::new(r"^\s*#\s*else").unwrap(), | ||||||
|             endif_regex: Regex::new(r"^\s*#\s*endif").unwrap(), |             endif_regex: Regex::new(r"^\s*#\s*endif").unwrap(), | ||||||
|  |             define_regex: Regex::new(r"^\s*#\s*define\s*([\w|\d|_]+)\s*([-\w|\d]+)?").unwrap(), | ||||||
|             def_regex: Regex::new(r"#\s*([\w|\d|_]+)").unwrap(), |             def_regex: Regex::new(r"#\s*([\w|\d|_]+)").unwrap(), | ||||||
|             def_regex_delimited: Regex::new(r"#\s*\{([\w|\d|_]+)\}").unwrap(), |             def_regex_delimited: Regex::new(r"#\s*\{([\w|\d|_]+)\}").unwrap(), | ||||||
|         } |         } | ||||||
| @ -449,24 +456,34 @@ impl ShaderProcessor { | |||||||
|         shader_defs: &[ShaderDefVal], |         shader_defs: &[ShaderDefVal], | ||||||
|         shaders: &HashMap<Handle<Shader>, Shader>, |         shaders: &HashMap<Handle<Shader>, Shader>, | ||||||
|         import_handles: &HashMap<ShaderImport, Handle<Shader>>, |         import_handles: &HashMap<ShaderImport, Handle<Shader>>, | ||||||
|  |     ) -> Result<ProcessedShader, ProcessShaderError> { | ||||||
|  |         let mut shader_defs_unique = | ||||||
|  |             HashMap::<String, ShaderDefVal>::from_iter(shader_defs.iter().map(|v| match v { | ||||||
|  |                 ShaderDefVal::Bool(k, _) | ShaderDefVal::Int(k, _) | ShaderDefVal::UInt(k, _) => { | ||||||
|  |                     (k.clone(), v.clone()) | ||||||
|  |                 } | ||||||
|  |             })); | ||||||
|  |         self.process_inner(shader, &mut shader_defs_unique, shaders, import_handles) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn process_inner( | ||||||
|  |         &self, | ||||||
|  |         shader: &Shader, | ||||||
|  |         shader_defs_unique: &mut HashMap<String, ShaderDefVal>, | ||||||
|  |         shaders: &HashMap<Handle<Shader>, Shader>, | ||||||
|  |         import_handles: &HashMap<ShaderImport, Handle<Shader>>, | ||||||
|     ) -> Result<ProcessedShader, ProcessShaderError> { |     ) -> Result<ProcessedShader, ProcessShaderError> { | ||||||
|         let shader_str = match &shader.source { |         let shader_str = match &shader.source { | ||||||
|             Source::Wgsl(source) => source.deref(), |             Source::Wgsl(source) => source.deref(), | ||||||
|             Source::Glsl(source, _stage) => source.deref(), |             Source::Glsl(source, _stage) => source.deref(), | ||||||
|             Source::SpirV(source) => { |             Source::SpirV(source) => { | ||||||
|                 if shader_defs.is_empty() { |                 if shader_defs_unique.is_empty() { | ||||||
|                     return Ok(ProcessedShader::SpirV(source.clone())); |                     return Ok(ProcessedShader::SpirV(source.clone())); | ||||||
|                 } |                 } | ||||||
|                 return Err(ProcessShaderError::ShaderFormatDoesNotSupportShaderDefs); |                 return Err(ProcessShaderError::ShaderFormatDoesNotSupportShaderDefs); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let shader_defs_unique = |  | ||||||
|             HashMap::<String, ShaderDefVal>::from_iter(shader_defs.iter().map(|v| match v { |  | ||||||
|                 ShaderDefVal::Bool(k, _) | ShaderDefVal::Int(k, _) | ShaderDefVal::UInt(k, _) => { |  | ||||||
|                     (k.clone(), v.clone()) |  | ||||||
|                 } |  | ||||||
|             })); |  | ||||||
|         let mut scopes = vec![Scope::new(true)]; |         let mut scopes = vec![Scope::new(true)]; | ||||||
|         let mut final_string = String::new(); |         let mut final_string = String::new(); | ||||||
|         for line in shader_str.lines() { |         for line in shader_str.lines() { | ||||||
| @ -544,6 +561,26 @@ impl ShaderProcessor { | |||||||
|                 let current_valid = scopes.last().unwrap().is_accepting_lines(); |                 let current_valid = scopes.last().unwrap().is_accepting_lines(); | ||||||
| 
 | 
 | ||||||
|                 scopes.push(Scope::new(current_valid && new_scope)); |                 scopes.push(Scope::new(current_valid && new_scope)); | ||||||
|  |             } else if let Some(cap) = self.define_regex.captures(line) { | ||||||
|  |                 let def = cap.get(1).unwrap(); | ||||||
|  |                 let name = def.as_str().to_string(); | ||||||
|  | 
 | ||||||
|  |                 if let Some(val) = cap.get(2) { | ||||||
|  |                     if let Ok(val) = val.as_str().parse::<u32>() { | ||||||
|  |                         shader_defs_unique.insert(name.clone(), ShaderDefVal::UInt(name, val)); | ||||||
|  |                     } else if let Ok(val) = val.as_str().parse::<i32>() { | ||||||
|  |                         shader_defs_unique.insert(name.clone(), ShaderDefVal::Int(name, val)); | ||||||
|  |                     } else if let Ok(val) = val.as_str().parse::<bool>() { | ||||||
|  |                         shader_defs_unique.insert(name.clone(), ShaderDefVal::Bool(name, val)); | ||||||
|  |                     } else { | ||||||
|  |                         return Err(ProcessShaderError::InvalidShaderDefDefinitionValue { | ||||||
|  |                             shader_def_name: name, | ||||||
|  |                             value: val.as_str().to_string(), | ||||||
|  |                         }); | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     shader_defs_unique.insert(name.clone(), ShaderDefVal::Bool(name, true)); | ||||||
|  |                 } | ||||||
|             } else if let Some(cap) = self.else_ifdef_regex.captures(line) { |             } else if let Some(cap) = self.else_ifdef_regex.captures(line) { | ||||||
|                 // When should we accept the code in an
 |                 // When should we accept the code in an
 | ||||||
|                 //
 |                 //
 | ||||||
| @ -627,7 +664,7 @@ impl ShaderProcessor { | |||||||
|                         shaders, |                         shaders, | ||||||
|                         &import, |                         &import, | ||||||
|                         shader, |                         shader, | ||||||
|                         shader_defs, |                         shader_defs_unique, | ||||||
|                         &mut final_string, |                         &mut final_string, | ||||||
|                     )?; |                     )?; | ||||||
|                 } else if let Some(cap) = SHADER_IMPORT_PROCESSOR |                 } else if let Some(cap) = SHADER_IMPORT_PROCESSOR | ||||||
| @ -640,7 +677,7 @@ impl ShaderProcessor { | |||||||
|                         shaders, |                         shaders, | ||||||
|                         &import, |                         &import, | ||||||
|                         shader, |                         shader, | ||||||
|                         shader_defs, |                         shader_defs_unique, | ||||||
|                         &mut final_string, |                         &mut final_string, | ||||||
|                     )?; |                     )?; | ||||||
|                 } else if SHADER_IMPORT_PROCESSOR |                 } else if SHADER_IMPORT_PROCESSOR | ||||||
| @ -695,7 +732,7 @@ impl ShaderProcessor { | |||||||
|         shaders: &HashMap<Handle<Shader>, Shader>, |         shaders: &HashMap<Handle<Shader>, Shader>, | ||||||
|         import: &ShaderImport, |         import: &ShaderImport, | ||||||
|         shader: &Shader, |         shader: &Shader, | ||||||
|         shader_defs: &[ShaderDefVal], |         shader_defs_unique: &mut HashMap<String, ShaderDefVal>, | ||||||
|         final_string: &mut String, |         final_string: &mut String, | ||||||
|     ) -> Result<(), ProcessShaderError> { |     ) -> Result<(), ProcessShaderError> { | ||||||
|         let imported_shader = import_handles |         let imported_shader = import_handles | ||||||
| @ -703,7 +740,7 @@ impl ShaderProcessor { | |||||||
|             .and_then(|handle| shaders.get(handle)) |             .and_then(|handle| shaders.get(handle)) | ||||||
|             .ok_or_else(|| ProcessShaderError::UnresolvedImport(import.clone()))?; |             .ok_or_else(|| ProcessShaderError::UnresolvedImport(import.clone()))?; | ||||||
|         let imported_processed = |         let imported_processed = | ||||||
|             self.process(imported_shader, shader_defs, shaders, import_handles)?; |             self.process_inner(imported_shader, shader_defs_unique, shaders, import_handles)?; | ||||||
| 
 | 
 | ||||||
|         match &shader.source { |         match &shader.source { | ||||||
|             Source::Wgsl(_) => { |             Source::Wgsl(_) => { | ||||||
| @ -2441,4 +2478,114 @@ fn vertex( | |||||||
|             .unwrap(); |             .unwrap(); | ||||||
|         assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED_REPLACED); |         assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED_REPLACED); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn process_shader_define_in_shader() { | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const WGSL: &str = r" | ||||||
|  | #ifdef NOW_DEFINED | ||||||
|  | defined at start | ||||||
|  | #endif | ||||||
|  | #define NOW_DEFINED | ||||||
|  | #ifdef NOW_DEFINED | ||||||
|  | defined at end | ||||||
|  | #endif | ||||||
|  | ";
 | ||||||
|  | 
 | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const EXPECTED: &str = r" | ||||||
|  | defined at end | ||||||
|  | ";
 | ||||||
|  |         let processor = ShaderProcessor::default(); | ||||||
|  |         let result = processor | ||||||
|  |             .process( | ||||||
|  |                 &Shader::from_wgsl(WGSL), | ||||||
|  |                 &[], | ||||||
|  |                 &HashMap::default(), | ||||||
|  |                 &HashMap::default(), | ||||||
|  |             ) | ||||||
|  |             .unwrap(); | ||||||
|  |         assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn process_shader_define_in_shader_with_value() { | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const WGSL: &str = r" | ||||||
|  | #define DEFUINT 1 | ||||||
|  | #define DEFINT -1 | ||||||
|  | #define DEFBOOL false | ||||||
|  | #if DEFUINT == 1 | ||||||
|  | uint: #DEFUINT | ||||||
|  | #endif | ||||||
|  | #if DEFINT == -1 | ||||||
|  | int: #DEFINT | ||||||
|  | #endif | ||||||
|  | #if DEFBOOL == false | ||||||
|  | bool: #DEFBOOL | ||||||
|  | #endif | ||||||
|  | ";
 | ||||||
|  | 
 | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const EXPECTED: &str = r" | ||||||
|  | uint: 1 | ||||||
|  | int: -1 | ||||||
|  | bool: false | ||||||
|  | ";
 | ||||||
|  |         let processor = ShaderProcessor::default(); | ||||||
|  |         let result = processor | ||||||
|  |             .process( | ||||||
|  |                 &Shader::from_wgsl(WGSL), | ||||||
|  |                 &[], | ||||||
|  |                 &HashMap::default(), | ||||||
|  |                 &HashMap::default(), | ||||||
|  |             ) | ||||||
|  |             .unwrap(); | ||||||
|  |         assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn process_shader_define_across_imports() { | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const FOO: &str = r" | ||||||
|  | #define IMPORTED | ||||||
|  | ";
 | ||||||
|  |         const BAR: &str = r" | ||||||
|  | #IMPORTED | ||||||
|  | ";
 | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const INPUT: &str = r" | ||||||
|  | #import FOO | ||||||
|  | #import BAR | ||||||
|  | ";
 | ||||||
|  |         #[rustfmt::skip] | ||||||
|  |         const EXPECTED: &str = r" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | true | ||||||
|  | ";
 | ||||||
|  |         let processor = ShaderProcessor::default(); | ||||||
|  |         let mut shaders = HashMap::default(); | ||||||
|  |         let mut import_handles = HashMap::default(); | ||||||
|  |         { | ||||||
|  |             let foo_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 0).typed(); | ||||||
|  |             shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO)); | ||||||
|  |             import_handles.insert( | ||||||
|  |                 ShaderImport::Custom("FOO".to_string()), | ||||||
|  |                 foo_handle.clone_weak(), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         { | ||||||
|  |             let bar_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1).typed(); | ||||||
|  |             shaders.insert(bar_handle.clone_weak(), Shader::from_wgsl(BAR)); | ||||||
|  |             import_handles.insert( | ||||||
|  |                 ShaderImport::Custom("BAR".to_string()), | ||||||
|  |                 bar_handle.clone_weak(), | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  |         let result = processor | ||||||
|  |             .process(&Shader::from_wgsl(INPUT), &[], &shaders, &import_handles) | ||||||
|  |             .unwrap(); | ||||||
|  |         assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 François
						François