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