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
This commit is contained in:
Robert Swain 2021-11-16 03:19:08 +00:00
parent 213839f503
commit 14ce281904

View file

@ -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<f32>;
};
[[stage(vertex)]]
fn vertex(
[[location(0)]] vertex_position: vec3<f32>,
[[location(1)]] vertex_uv: vec2<f32>
) -> VertexOutput {
var out: VertexOutput;
out.uv = vertex_uv;
out.position = view.view_proj * vec4<f32>(vertex_position, 1.0);
return out;
}
";
const WGSL_NESTED_IFDEF: &str = r"
[[block]]
struct View {
view_proj: mat4x4<f32>;
world_position: vec3<f32>;
};
[[group(0), binding(0)]]
var<uniform> view: View;
# ifdef TEXTURE
# ifdef ATTRIBUTE
[[group(1), binding(0)]]
var sprite_texture: texture_2d<f32>;
# endif
# endif
struct VertexOutput {
[[location(0)]] uv: vec2<f32>;
[[builtin(position)]] position: vec4<f32>;
};
[[stage(vertex)]]
fn vertex(
[[location(0)]] vertex_position: vec3<f32>,
@ -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<f32>;
world_position: vec3<f32>;
};
[[group(0), binding(0)]]
var<uniform> view: View;
struct VertexOutput {
[[location(0)]] uv: vec2<f32>;
[[builtin(position)]] position: vec4<f32>;
};
[[stage(vertex)]]
fn vertex(
[[location(0)]] vertex_position: vec3<f32>,
[[location(1)]] vertex_uv: vec2<f32>
) -> VertexOutput {
var out: VertexOutput;
out.uv = vertex_uv;
out.position = view.view_proj * vec4<f32>(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<f32>;
world_position: vec3<f32>;
};
[[group(0), binding(0)]]
var<uniform> view: View;
struct VertexOutput {
[[location(0)]] uv: vec2<f32>;
[[builtin(position)]] position: vec4<f32>;
};
[[stage(vertex)]]
fn vertex(
[[location(0)]] vertex_position: vec3<f32>,
[[location(1)]] vertex_uv: vec2<f32>
) -> VertexOutput {
var out: VertexOutput;
out.uv = vertex_uv;
out.position = view.view_proj * vec4<f32>(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<f32>;
world_position: vec3<f32>;
};
[[group(0), binding(0)]]
var<uniform> view: View;
struct VertexOutput {
[[location(0)]] uv: vec2<f32>;
[[builtin(position)]] position: vec4<f32>;
};
[[stage(vertex)]]
fn vertex(
[[location(0)]] vertex_position: vec3<f32>,
[[location(1)]] vertex_uv: vec2<f32>
) -> VertexOutput {
var out: VertexOutput;
out.uv = vertex_uv;
out.position = view.view_proj * vec4<f32>(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<f32>;
world_position: vec3<f32>;
};
[[group(0), binding(0)]]
var<uniform> view: View;
[[group(1), binding(0)]]
var sprite_texture: texture_2d<f32>;
struct VertexOutput {
[[location(0)]] uv: vec2<f32>;
[[builtin(position)]] position: vec4<f32>;
};
[[stage(vertex)]]
fn vertex(
[[location(0)]] vertex_position: vec3<f32>,
[[location(1)]] vertex_uv: vec2<f32>
) -> VertexOutput {
var out: VertexOutput;
out.uv = vertex_uv;
out.position = view.view_proj * vec4<f32>(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);
}
}