From 4a6999bed213b99a743261b954e9e1cddb3d29ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Fri, 10 Dec 2021 17:51:08 +0100 Subject: [PATCH 1/4] loop over shader until everything has been processed --- .../bevy_render/src/render_resource/shader.rs | 136 +++++++++++++----- 1 file changed, 98 insertions(+), 38 deletions(-) diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index 6dd84961111e5..b3a11516014eb 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -362,46 +362,59 @@ impl ShaderProcessor { let shader_defs = HashSet::::from_iter(shader_defs.iter().cloned()); let mut scopes = vec![true]; - let mut final_string = String::new(); - for line in shader_str.split('\n') { - if let Some(cap) = self.ifdef_regex.captures(line) { - let def = cap.get(1).unwrap(); - scopes.push(*scopes.last().unwrap() && shader_defs.contains(def.as_str())); - } else if let Some(cap) = self.ifndef_regex.captures(line) { - let def = cap.get(1).unwrap(); - scopes.push(*scopes.last().unwrap() && !shader_defs.contains(def.as_str())); - } else if self.else_regex.is_match(line) { - let mut is_parent_scope_truthy = true; - if scopes.len() > 1 { - is_parent_scope_truthy = scopes[scopes.len() - 2]; + let mut processing = shader_str.to_string(); + let final_string = loop { + let mut processed = String::new(); + let mut changed = false; + for line in processing.split('\n') { + if let Some(cap) = self.ifdef_regex.captures(line) { + let def = cap.get(1).unwrap(); + scopes.push(*scopes.last().unwrap() && shader_defs.contains(def.as_str())); + changed = true; + } else if let Some(cap) = self.ifndef_regex.captures(line) { + let def = cap.get(1).unwrap(); + scopes.push(*scopes.last().unwrap() && !shader_defs.contains(def.as_str())); + changed = true; + } else if self.else_regex.is_match(line) { + let mut is_parent_scope_truthy = true; + if scopes.len() > 1 { + is_parent_scope_truthy = scopes[scopes.len() - 2]; + } + if let Some(last) = scopes.last_mut() { + *last = is_parent_scope_truthy && !*last; + } + changed = true; + } else if self.endif_regex.is_match(line) { + scopes.pop(); + if scopes.is_empty() { + return Err(ProcessShaderError::TooManyEndIfs); + } + changed = true; + } else if let Some(cap) = SHADER_IMPORT_PROCESSOR + .import_asset_path_regex + .captures(line) + { + let import = ShaderImport::AssetPath(cap.get(1).unwrap().as_str().to_string()); + apply_import(import_handles, shaders, &import, shader, &mut processed)?; + changed = true; + } else if let Some(cap) = SHADER_IMPORT_PROCESSOR + .import_custom_path_regex + .captures(line) + { + let import = ShaderImport::Custom(cap.get(1).unwrap().as_str().to_string()); + apply_import(import_handles, shaders, &import, shader, &mut processed)?; + changed = true; + } else if *scopes.last().unwrap() { + processed.push_str(line); + processed.push('\n'); } - if let Some(last) = scopes.last_mut() { - *last = is_parent_scope_truthy && !*last; - } - } else if self.endif_regex.is_match(line) { - scopes.pop(); - if scopes.is_empty() { - return Err(ProcessShaderError::TooManyEndIfs); - } - } else if let Some(cap) = SHADER_IMPORT_PROCESSOR - .import_asset_path_regex - .captures(line) - { - let import = ShaderImport::AssetPath(cap.get(1).unwrap().as_str().to_string()); - apply_import(import_handles, shaders, &import, shader, &mut final_string)?; - } else if let Some(cap) = SHADER_IMPORT_PROCESSOR - .import_custom_path_regex - .captures(line) - { - let import = ShaderImport::Custom(cap.get(1).unwrap().as_str().to_string()); - apply_import(import_handles, shaders, &import, shader, &mut final_string)?; - } else if *scopes.last().unwrap() { - final_string.push_str(line); - final_string.push('\n'); } - } - - final_string.pop(); + processed.pop(); + if !changed { + break processed; + } + processing = processed.clone(); + }; if scopes.len() != 1 { return Err(ProcessShaderError::NotEnoughEndIfs); @@ -1094,4 +1107,51 @@ fn vertex( .unwrap(); assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); } + + #[test] + fn process_import_ifdef() { + #[rustfmt::skip] + const FOO: &str = r" +#ifdef IMPORT_MISSING +fn in_import_missing() { } +#endif +#ifdef IMPORT_PRESENT +fn in_import_present() { } +#endif +"; + #[rustfmt::skip] + const INPUT: &str = r" +#import FOO +#ifdef MAIN_MISSING +fn in_main_missing() { } +#endif +#ifdef MAIN_PRESENT +fn in_main_present() { } +#endif +"; + #[rustfmt::skip] + const EXPECTED: &str = r" + +fn in_import_present() { } +fn in_main_present() { } +"; + let processor = ShaderProcessor::default(); + let mut shaders = HashMap::default(); + let mut import_handles = HashMap::default(); + let foo_handle = Handle::::default(); + shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO)); + import_handles.insert( + ShaderImport::Custom("FOO".to_string()), + foo_handle.clone_weak(), + ); + let result = processor + .process( + &Shader::from_wgsl(INPUT), + &["MAIN_PRESENT".to_string(), "IMPORT_PRESENT".to_string()], + &shaders, + &import_handles, + ) + .unwrap(); + assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); + } } From a3c632ba9c9044429e335364e92e8ae8e04129df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= <8672791+mockersf@users.noreply.github.com> Date: Wed, 22 Dec 2021 00:17:58 +0100 Subject: [PATCH 2/4] nested imports test --- .../bevy_render/src/render_resource/shader.rs | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index b3a11516014eb..4e4f6d3e4eda4 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -468,7 +468,8 @@ fn apply_import( #[cfg(test)] mod tests { - use bevy_asset::Handle; + use bevy_asset::{Handle, HandleUntyped}; + use bevy_reflect::TypeUuid; use bevy_utils::HashMap; use naga::ShaderStage; @@ -1154,4 +1155,59 @@ fn in_main_present() { } .unwrap(); assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); } + + #[test] + fn process_import_in_import() { + #[rustfmt::skip] + const BAR: &str = r" +#ifdef DEEP +fn inner_import() { } +#endif +"; + const FOO: &str = r" +#import BAR +fn import() { } +"; + #[rustfmt::skip] + const INPUT: &str = r" +#import FOO +fn in_main() { } +"; + #[rustfmt::skip] + const EXPECTED: &str = r" + + +fn inner_import() { } +fn import() { } +fn in_main() { } +"; + let processor = ShaderProcessor::default(); + let mut shaders = HashMap::default(); + let mut import_handles = HashMap::default(); + { + let bar_handle = Handle::::default(); + shaders.insert(bar_handle.clone_weak(), Shader::from_wgsl(BAR)); + import_handles.insert( + ShaderImport::Custom("BAR".to_string()), + bar_handle.clone_weak(), + ); + } + { + let foo_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1).typed(); + shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO)); + import_handles.insert( + ShaderImport::Custom("FOO".to_string()), + foo_handle.clone_weak(), + ); + } + let result = processor + .process( + &Shader::from_wgsl(INPUT), + &["DEEP".to_string()], + &shaders, + &import_handles, + ) + .unwrap(); + assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED); + } } From c51116f2f48ed92e21234e815749ca066ae09db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= <8672791+mockersf@users.noreply.github.com> Date: Wed, 22 Dec 2021 00:21:21 +0100 Subject: [PATCH 3/4] remove useless clone --- crates/bevy_render/src/render_resource/shader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index 4e4f6d3e4eda4..e5bcc892777c7 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -413,7 +413,7 @@ impl ShaderProcessor { if !changed { break processed; } - processing = processed.clone(); + processing = processed; }; if scopes.len() != 1 { From 71447f94948862d3402f584922278efe1130a117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= <8672791+mockersf@users.noreply.github.com> Date: Wed, 22 Dec 2021 00:39:21 +0100 Subject: [PATCH 4/4] Cart doesn't like loops --- .../bevy_render/src/render_resource/shader.rs | 167 +++++++++--------- 1 file changed, 86 insertions(+), 81 deletions(-) diff --git a/crates/bevy_render/src/render_resource/shader.rs b/crates/bevy_render/src/render_resource/shader.rs index e5bcc892777c7..b0adfe8b39723 100644 --- a/crates/bevy_render/src/render_resource/shader.rs +++ b/crates/bevy_render/src/render_resource/shader.rs @@ -360,61 +360,61 @@ impl ShaderProcessor { } }; - let shader_defs = HashSet::::from_iter(shader_defs.iter().cloned()); + let shader_defs_unique = HashSet::::from_iter(shader_defs.iter().cloned()); let mut scopes = vec![true]; - let mut processing = shader_str.to_string(); - let final_string = loop { - let mut processed = String::new(); - let mut changed = false; - for line in processing.split('\n') { - if let Some(cap) = self.ifdef_regex.captures(line) { - let def = cap.get(1).unwrap(); - scopes.push(*scopes.last().unwrap() && shader_defs.contains(def.as_str())); - changed = true; - } else if let Some(cap) = self.ifndef_regex.captures(line) { - let def = cap.get(1).unwrap(); - scopes.push(*scopes.last().unwrap() && !shader_defs.contains(def.as_str())); - changed = true; - } else if self.else_regex.is_match(line) { - let mut is_parent_scope_truthy = true; - if scopes.len() > 1 { - is_parent_scope_truthy = scopes[scopes.len() - 2]; - } - if let Some(last) = scopes.last_mut() { - *last = is_parent_scope_truthy && !*last; - } - changed = true; - } else if self.endif_regex.is_match(line) { - scopes.pop(); - if scopes.is_empty() { - return Err(ProcessShaderError::TooManyEndIfs); - } - changed = true; - } else if let Some(cap) = SHADER_IMPORT_PROCESSOR - .import_asset_path_regex - .captures(line) - { - let import = ShaderImport::AssetPath(cap.get(1).unwrap().as_str().to_string()); - apply_import(import_handles, shaders, &import, shader, &mut processed)?; - changed = true; - } else if let Some(cap) = SHADER_IMPORT_PROCESSOR - .import_custom_path_regex - .captures(line) - { - let import = ShaderImport::Custom(cap.get(1).unwrap().as_str().to_string()); - apply_import(import_handles, shaders, &import, shader, &mut processed)?; - changed = true; - } else if *scopes.last().unwrap() { - processed.push_str(line); - processed.push('\n'); + let mut final_string = String::new(); + for line in shader_str.split('\n') { + if let Some(cap) = self.ifdef_regex.captures(line) { + let def = cap.get(1).unwrap(); + scopes.push(*scopes.last().unwrap() && shader_defs_unique.contains(def.as_str())); + } else if let Some(cap) = self.ifndef_regex.captures(line) { + let def = cap.get(1).unwrap(); + scopes.push(*scopes.last().unwrap() && !shader_defs_unique.contains(def.as_str())); + } else if self.else_regex.is_match(line) { + let mut is_parent_scope_truthy = true; + if scopes.len() > 1 { + is_parent_scope_truthy = scopes[scopes.len() - 2]; } + if let Some(last) = scopes.last_mut() { + *last = is_parent_scope_truthy && !*last; + } + } else if self.endif_regex.is_match(line) { + scopes.pop(); + if scopes.is_empty() { + return Err(ProcessShaderError::TooManyEndIfs); + } + } else if let Some(cap) = SHADER_IMPORT_PROCESSOR + .import_asset_path_regex + .captures(line) + { + let import = ShaderImport::AssetPath(cap.get(1).unwrap().as_str().to_string()); + self.apply_import( + import_handles, + shaders, + &import, + shader, + shader_defs, + &mut final_string, + )?; + } else if let Some(cap) = SHADER_IMPORT_PROCESSOR + .import_custom_path_regex + .captures(line) + { + let import = ShaderImport::Custom(cap.get(1).unwrap().as_str().to_string()); + self.apply_import( + import_handles, + shaders, + &import, + shader, + shader_defs, + &mut final_string, + )?; + } else if *scopes.last().unwrap() { + final_string.push_str(line); + final_string.push('\n'); } - processed.pop(); - if !changed { - break processed; - } - processing = processed; - }; + } + final_string.pop(); if scopes.len() != 1 { return Err(ProcessShaderError::NotEnoughEndIfs); @@ -430,40 +430,45 @@ impl ShaderProcessor { } } } -} -fn apply_import( - import_handles: &HashMap>, - shaders: &HashMap, Shader>, - import: &ShaderImport, - shader: &Shader, - final_string: &mut String, -) -> Result<(), ProcessShaderError> { - let imported_shader = import_handles - .get(import) - .and_then(|handle| shaders.get(handle)) - .ok_or_else(|| ProcessShaderError::UnresolvedImport(import.clone()))?; - match &shader.source { - Source::Wgsl(_) => { - if let Source::Wgsl(import_source) = &imported_shader.source { - final_string.push_str(import_source); - } else { - return Err(ProcessShaderError::MismatchedImportFormat(import.clone())); + fn apply_import( + &self, + import_handles: &HashMap>, + shaders: &HashMap, Shader>, + import: &ShaderImport, + shader: &Shader, + shader_defs: &[String], + final_string: &mut String, + ) -> Result<(), ProcessShaderError> { + let imported_shader = import_handles + .get(import) + .and_then(|handle| shaders.get(handle)) + .ok_or_else(|| ProcessShaderError::UnresolvedImport(import.clone()))?; + let imported_processed = + self.process(imported_shader, shader_defs, shaders, import_handles)?; + + match &shader.source { + Source::Wgsl(_) => { + if let ProcessedShader::Wgsl(import_source) = &imported_processed { + final_string.push_str(import_source); + } else { + return Err(ProcessShaderError::MismatchedImportFormat(import.clone())); + } } - } - Source::Glsl(_, _) => { - if let Source::Glsl(import_source, _) = &imported_shader.source { - final_string.push_str(import_source); - } else { - return Err(ProcessShaderError::MismatchedImportFormat(import.clone())); + Source::Glsl(_, _) => { + if let ProcessedShader::Glsl(import_source, _) = &imported_processed { + final_string.push_str(import_source); + } else { + return Err(ProcessShaderError::MismatchedImportFormat(import.clone())); + } + } + Source::SpirV(_) => { + return Err(ProcessShaderError::ShaderFormatDoesNotSupportImports); } } - Source::SpirV(_) => { - return Err(ProcessShaderError::ShaderFormatDoesNotSupportImports); - } - } - Ok(()) + Ok(()) + } } #[cfg(test)]