Skip to content

Commit b056475

Browse files
committed
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>`
1 parent 1936844 commit b056475

File tree

1 file changed

+159
-12
lines changed
  • crates/bevy_render/src/render_resource

1 file changed

+159
-12
lines changed

crates/bevy_render/src/render_resource/shader.rs

Lines changed: 159 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,11 @@ pub enum ProcessShaderError {
316316
expected: String,
317317
value: String,
318318
},
319+
#[error("Invalid shader def definition for '{shader_def_name}': {value}")]
320+
InvalidShaderDefDefinitionValue {
321+
shader_def_name: String,
322+
value: String,
323+
},
319324
}
320325

321326
pub struct ShaderImportProcessor {
@@ -388,6 +393,7 @@ pub struct ShaderProcessor {
388393
else_ifdef_regex: Regex,
389394
else_regex: Regex,
390395
endif_regex: Regex,
396+
define_regex: Regex,
391397
def_regex: Regex,
392398
def_regex_delimited: Regex,
393399
}
@@ -397,10 +403,11 @@ impl Default for ShaderProcessor {
397403
Self {
398404
ifdef_regex: Regex::new(r"^\s*#\s*ifdef\s*([\w|\d|_]+)").unwrap(),
399405
ifndef_regex: Regex::new(r"^\s*#\s*ifndef\s*([\w|\d|_]+)").unwrap(),
400-
ifop_regex: Regex::new(r"^\s*#\s*if\s*([\w|\d|_]+)\s*([^\s]*)\s*([\w|\d]+)").unwrap(),
406+
ifop_regex: Regex::new(r"^\s*#\s*if\s*([\w|\d|_]+)\s*([^\s]*)\s*([-\w|\d]+)").unwrap(),
401407
else_ifdef_regex: Regex::new(r"^\s*#\s*else\s+ifdef\s*([\w|\d|_]+)").unwrap(),
402408
else_regex: Regex::new(r"^\s*#\s*else").unwrap(),
403409
endif_regex: Regex::new(r"^\s*#\s*endif").unwrap(),
410+
define_regex: Regex::new(r"^\s*#\s*define\s*([\w|\d|_]+)\s*([-\w|\d]+)?").unwrap(),
404411
def_regex: Regex::new(r"#\s*([\w|\d|_]+)").unwrap(),
405412
def_regex_delimited: Regex::new(r"#\s*\{([\w|\d|_]+)\}").unwrap(),
406413
}
@@ -449,24 +456,34 @@ impl ShaderProcessor {
449456
shader_defs: &[ShaderDefVal],
450457
shaders: &HashMap<Handle<Shader>, Shader>,
451458
import_handles: &HashMap<ShaderImport, Handle<Shader>>,
459+
) -> Result<ProcessedShader, ProcessShaderError> {
460+
let mut shader_defs_unique =
461+
HashMap::<String, ShaderDefVal>::from_iter(shader_defs.iter().map(|v| match v {
462+
ShaderDefVal::Bool(k, _) | ShaderDefVal::Int(k, _) | ShaderDefVal::UInt(k, _) => {
463+
(k.clone(), v.clone())
464+
}
465+
}));
466+
self.process_inner(shader, &mut shader_defs_unique, shaders, import_handles)
467+
}
468+
469+
fn process_inner(
470+
&self,
471+
shader: &Shader,
472+
shader_defs_unique: &mut HashMap<String, ShaderDefVal>,
473+
shaders: &HashMap<Handle<Shader>, Shader>,
474+
import_handles: &HashMap<ShaderImport, Handle<Shader>>,
452475
) -> Result<ProcessedShader, ProcessShaderError> {
453476
let shader_str = match &shader.source {
454477
Source::Wgsl(source) => source.deref(),
455478
Source::Glsl(source, _stage) => source.deref(),
456479
Source::SpirV(source) => {
457-
if shader_defs.is_empty() {
480+
if shader_defs_unique.is_empty() {
458481
return Ok(ProcessedShader::SpirV(source.clone()));
459482
}
460483
return Err(ProcessShaderError::ShaderFormatDoesNotSupportShaderDefs);
461484
}
462485
};
463486

464-
let shader_defs_unique =
465-
HashMap::<String, ShaderDefVal>::from_iter(shader_defs.iter().map(|v| match v {
466-
ShaderDefVal::Bool(k, _) | ShaderDefVal::Int(k, _) | ShaderDefVal::UInt(k, _) => {
467-
(k.clone(), v.clone())
468-
}
469-
}));
470487
let mut scopes = vec![Scope::new(true)];
471488
let mut final_string = String::new();
472489
for line in shader_str.lines() {
@@ -544,6 +561,26 @@ impl ShaderProcessor {
544561
let current_valid = scopes.last().unwrap().is_accepting_lines();
545562

546563
scopes.push(Scope::new(current_valid && new_scope));
564+
} else if let Some(cap) = self.define_regex.captures(line) {
565+
let def = cap.get(1).unwrap();
566+
let name = def.as_str().to_string();
567+
568+
if let Some(val) = cap.get(2) {
569+
if let Ok(val) = val.as_str().parse::<u32>() {
570+
shader_defs_unique.insert(name.clone(), ShaderDefVal::UInt(name, val));
571+
} else if let Ok(val) = val.as_str().parse::<i32>() {
572+
shader_defs_unique.insert(name.clone(), ShaderDefVal::Int(name, val));
573+
} else if let Ok(val) = val.as_str().parse::<bool>() {
574+
shader_defs_unique.insert(name.clone(), ShaderDefVal::Bool(name, val));
575+
} else {
576+
return Err(ProcessShaderError::InvalidShaderDefDefinitionValue {
577+
shader_def_name: name,
578+
value: val.as_str().to_string(),
579+
});
580+
}
581+
} else {
582+
shader_defs_unique.insert(name.clone(), ShaderDefVal::Bool(name, true));
583+
}
547584
} else if let Some(cap) = self.else_ifdef_regex.captures(line) {
548585
// When should we accept the code in an
549586
//
@@ -627,7 +664,7 @@ impl ShaderProcessor {
627664
shaders,
628665
&import,
629666
shader,
630-
shader_defs,
667+
shader_defs_unique,
631668
&mut final_string,
632669
)?;
633670
} else if let Some(cap) = SHADER_IMPORT_PROCESSOR
@@ -640,7 +677,7 @@ impl ShaderProcessor {
640677
shaders,
641678
&import,
642679
shader,
643-
shader_defs,
680+
shader_defs_unique,
644681
&mut final_string,
645682
)?;
646683
} else if SHADER_IMPORT_PROCESSOR
@@ -695,15 +732,15 @@ impl ShaderProcessor {
695732
shaders: &HashMap<Handle<Shader>, Shader>,
696733
import: &ShaderImport,
697734
shader: &Shader,
698-
shader_defs: &[ShaderDefVal],
735+
shader_defs_unique: &mut HashMap<String, ShaderDefVal>,
699736
final_string: &mut String,
700737
) -> Result<(), ProcessShaderError> {
701738
let imported_shader = import_handles
702739
.get(import)
703740
.and_then(|handle| shaders.get(handle))
704741
.ok_or_else(|| ProcessShaderError::UnresolvedImport(import.clone()))?;
705742
let imported_processed =
706-
self.process(imported_shader, shader_defs, shaders, import_handles)?;
743+
self.process_inner(imported_shader, shader_defs_unique, shaders, import_handles)?;
707744

708745
match &shader.source {
709746
Source::Wgsl(_) => {
@@ -2441,4 +2478,114 @@ fn vertex(
24412478
.unwrap();
24422479
assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED_REPLACED);
24432480
}
2481+
2482+
#[test]
2483+
fn process_shader_define_in_shader() {
2484+
#[rustfmt::skip]
2485+
const WGSL: &str = r"
2486+
#ifdef NOW_DEFINED
2487+
defined at start
2488+
#endif
2489+
#define NOW_DEFINED
2490+
#ifdef NOW_DEFINED
2491+
defined at end
2492+
#endif
2493+
";
2494+
2495+
#[rustfmt::skip]
2496+
const EXPECTED: &str = r"
2497+
defined at end
2498+
";
2499+
let processor = ShaderProcessor::default();
2500+
let result = processor
2501+
.process(
2502+
&Shader::from_wgsl(WGSL),
2503+
&[],
2504+
&HashMap::default(),
2505+
&HashMap::default(),
2506+
)
2507+
.unwrap();
2508+
assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED);
2509+
}
2510+
2511+
#[test]
2512+
fn process_shader_define_in_shader_with_value() {
2513+
#[rustfmt::skip]
2514+
const WGSL: &str = r"
2515+
#define DEFUINT 1
2516+
#define DEFINT -1
2517+
#define DEFBOOL false
2518+
#if DEFUINT == 1
2519+
uint: #DEFUINT
2520+
#endif
2521+
#if DEFINT == -1
2522+
int: #DEFINT
2523+
#endif
2524+
#if DEFBOOL == false
2525+
bool: #DEFBOOL
2526+
#endif
2527+
";
2528+
2529+
#[rustfmt::skip]
2530+
const EXPECTED: &str = r"
2531+
uint: 1
2532+
int: -1
2533+
bool: false
2534+
";
2535+
let processor = ShaderProcessor::default();
2536+
let result = processor
2537+
.process(
2538+
&Shader::from_wgsl(WGSL),
2539+
&[],
2540+
&HashMap::default(),
2541+
&HashMap::default(),
2542+
)
2543+
.unwrap();
2544+
assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED);
2545+
}
2546+
2547+
#[test]
2548+
fn process_shader_define_across_imports() {
2549+
#[rustfmt::skip]
2550+
const FOO: &str = r"
2551+
#define IMPORTED
2552+
";
2553+
const BAR: &str = r"
2554+
#IMPORTED
2555+
";
2556+
#[rustfmt::skip]
2557+
const INPUT: &str = r"
2558+
#import FOO
2559+
#import BAR
2560+
";
2561+
#[rustfmt::skip]
2562+
const EXPECTED: &str = r"
2563+
2564+
2565+
true
2566+
";
2567+
let processor = ShaderProcessor::default();
2568+
let mut shaders = HashMap::default();
2569+
let mut import_handles = HashMap::default();
2570+
{
2571+
let foo_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 0).typed();
2572+
shaders.insert(foo_handle.clone_weak(), Shader::from_wgsl(FOO));
2573+
import_handles.insert(
2574+
ShaderImport::Custom("FOO".to_string()),
2575+
foo_handle.clone_weak(),
2576+
);
2577+
}
2578+
{
2579+
let bar_handle = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1).typed();
2580+
shaders.insert(bar_handle.clone_weak(), Shader::from_wgsl(BAR));
2581+
import_handles.insert(
2582+
ShaderImport::Custom("BAR".to_string()),
2583+
bar_handle.clone_weak(),
2584+
);
2585+
}
2586+
let result = processor
2587+
.process(&Shader::from_wgsl(INPUT), &[], &shaders, &import_handles)
2588+
.unwrap();
2589+
assert_eq!(result.get_wgsl_source().unwrap(), EXPECTED);
2590+
}
24442591
}

0 commit comments

Comments
 (0)