From 14ce28190417c483acf7957056d79de07a4b4491 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Tue, 16 Nov 2021 03:19:08 +0000 Subject: [PATCH] bevy_render2: Support nested shader defs (#3113) # Objective Fix nested shader defs. For example, in: ```rust #ifdef A #ifdef B some code here #endif #endif ``` ...before this PR, if `A` *is not* defined, and `B` *is* defined, then `some code here` will be output. ## Solution - Combine the logic of whether the parent and child scope guards are defined and use that as the resulting child scope guard boolean value --- .../src/render_resource/shader.rs | 183 +++++++++++++++++- 1 file changed, 181 insertions(+), 2 deletions(-) diff --git a/pipelined/bevy_render2/src/render_resource/shader.rs b/pipelined/bevy_render2/src/render_resource/shader.rs index 170c78eb8f..90f80e190a 100644 --- a/pipelined/bevy_render2/src/render_resource/shader.rs +++ b/pipelined/bevy_render2/src/render_resource/shader.rs @@ -268,10 +268,10 @@ impl ShaderProcessor { for line in shader.split('\n') { if let Some(cap) = self.ifdef_regex.captures(line) { let def = cap.get(1).unwrap(); - scopes.push(shader_defs.contains(def.as_str())); + 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(!shader_defs.contains(def.as_str())); + scopes.push(*scopes.last().unwrap() && !shader_defs.contains(def.as_str())); } else if self.endif_regex.is_match(line) { scopes.pop(); if scopes.is_empty() { @@ -315,6 +315,38 @@ struct VertexOutput { [[builtin(position)]] position: vec4; }; +[[stage(vertex)]] +fn vertex( + [[location(0)]] vertex_position: vec3, + [[location(1)]] vertex_uv: vec2 +) -> VertexOutput { + var out: VertexOutput; + out.uv = vertex_uv; + out.position = view.view_proj * vec4(vertex_position, 1.0); + return out; +} +"; + const WGSL_NESTED_IFDEF: &str = r" +[[block]] +struct View { + view_proj: mat4x4; + world_position: vec3; +}; +[[group(0), binding(0)]] +var view: View; + +# ifdef TEXTURE +# ifdef ATTRIBUTE +[[group(1), binding(0)]] +var sprite_texture: texture_2d; +# endif +# endif + +struct VertexOutput { + [[location(0)]] uv: vec2; + [[builtin(position)]] position: vec4; +}; + [[stage(vertex)]] fn vertex( [[location(0)]] vertex_position: vec3, @@ -432,4 +464,151 @@ fn foo() { } let result = processor.process_str(INPUT, &[]).unwrap(); assert_eq!(result, INPUT); } + + #[test] + fn process_nested_shader_def_outer_defined_inner_not() { + #[rustfmt::skip] + const EXPECTED: &str = r" +[[block]] +struct View { + view_proj: mat4x4; + world_position: vec3; +}; +[[group(0), binding(0)]] +var view: View; + + +struct VertexOutput { + [[location(0)]] uv: vec2; + [[builtin(position)]] position: vec4; +}; + +[[stage(vertex)]] +fn vertex( + [[location(0)]] vertex_position: vec3, + [[location(1)]] vertex_uv: vec2 +) -> VertexOutput { + var out: VertexOutput; + out.uv = vertex_uv; + out.position = view.view_proj * vec4(vertex_position, 1.0); + return out; +} +"; + let processor = ShaderProcessor::default(); + let result = processor + .process_str(WGSL_NESTED_IFDEF, &["TEXTURE".to_string()]) + .unwrap(); + assert_eq!(result, EXPECTED); + } + + #[test] + fn process_nested_shader_def_neither_defined() { + #[rustfmt::skip] + const EXPECTED: &str = r" +[[block]] +struct View { + view_proj: mat4x4; + world_position: vec3; +}; +[[group(0), binding(0)]] +var view: View; + + +struct VertexOutput { + [[location(0)]] uv: vec2; + [[builtin(position)]] position: vec4; +}; + +[[stage(vertex)]] +fn vertex( + [[location(0)]] vertex_position: vec3, + [[location(1)]] vertex_uv: vec2 +) -> VertexOutput { + var out: VertexOutput; + out.uv = vertex_uv; + out.position = view.view_proj * vec4(vertex_position, 1.0); + return out; +} +"; + let processor = ShaderProcessor::default(); + let result = processor.process_str(WGSL_NESTED_IFDEF, &[]).unwrap(); + assert_eq!(result, EXPECTED); + } + + #[test] + fn process_nested_shader_def_inner_defined_outer_not() { + #[rustfmt::skip] + const EXPECTED: &str = r" +[[block]] +struct View { + view_proj: mat4x4; + world_position: vec3; +}; +[[group(0), binding(0)]] +var view: View; + + +struct VertexOutput { + [[location(0)]] uv: vec2; + [[builtin(position)]] position: vec4; +}; + +[[stage(vertex)]] +fn vertex( + [[location(0)]] vertex_position: vec3, + [[location(1)]] vertex_uv: vec2 +) -> VertexOutput { + var out: VertexOutput; + out.uv = vertex_uv; + out.position = view.view_proj * vec4(vertex_position, 1.0); + return out; +} +"; + let processor = ShaderProcessor::default(); + let result = processor + .process_str(WGSL_NESTED_IFDEF, &["ATTRIBUTE".to_string()]) + .unwrap(); + assert_eq!(result, EXPECTED); + } + + #[test] + fn process_nested_shader_def_both_defined() { + #[rustfmt::skip] + const EXPECTED: &str = r" +[[block]] +struct View { + view_proj: mat4x4; + world_position: vec3; +}; +[[group(0), binding(0)]] +var view: View; + +[[group(1), binding(0)]] +var sprite_texture: texture_2d; + +struct VertexOutput { + [[location(0)]] uv: vec2; + [[builtin(position)]] position: vec4; +}; + +[[stage(vertex)]] +fn vertex( + [[location(0)]] vertex_position: vec3, + [[location(1)]] vertex_uv: vec2 +) -> VertexOutput { + var out: VertexOutput; + out.uv = vertex_uv; + out.position = view.view_proj * vec4(vertex_position, 1.0); + return out; +} +"; + let processor = ShaderProcessor::default(); + let result = processor + .process_str( + WGSL_NESTED_IFDEF, + &["TEXTURE".to_string(), "ATTRIBUTE".to_string()], + ) + .unwrap(); + assert_eq!(result, EXPECTED); + } }