Skip to content

Commit 37bbbf7

Browse files
authored
Use SmallVec instead of HashMap in MaterialProperties (#19846)
# Objective - MaterialProperties uses HashMap for some data that is generally going to be really small. This is likely using more memory than necessary ## Solution - Use a SmallVec instead - I used the size a StandardMaterial would need for all the backing arrays ## Testing - Tested the 3d_scene to confirm it still works ## Notes I'm not sure if it made a measurable difference since I'm not sure how to measure this. It's a bit hard to create an artificial workflow where this would be the main bottleneck. This is very in the realm of microoptimization.
1 parent fb2bbb0 commit 37bbbf7

File tree

2 files changed

+29
-31
lines changed

2 files changed

+29
-31
lines changed

crates/bevy_pbr/src/material.rs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,8 +1300,11 @@ pub struct MaterialProperties {
13001300
pub reads_view_transmission_texture: bool,
13011301
pub render_phase_type: RenderPhaseType,
13021302
pub material_layout: Option<BindGroupLayout>,
1303-
pub draw_functions: HashMap<InternedDrawFunctionLabel, DrawFunctionId>,
1304-
pub shaders: HashMap<InternedShaderLabel, Handle<Shader>>,
1303+
/// Backing array is a size of 4 because the `StandardMaterial` needs 4 draw functions by default
1304+
pub draw_functions: SmallVec<[(InternedDrawFunctionLabel, DrawFunctionId); 4]>,
1305+
/// Backing array is a size of 3 because the `StandardMaterial` has 3 custom shaders (`frag`, `prepass_frag`, `deferred_frag`) which is the
1306+
/// most common use case
1307+
pub shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 3]>,
13051308
/// Whether this material *actually* uses bindless resources, taking the
13061309
/// platform support (or lack thereof) of bindless resources into account.
13071310
pub bindless: bool,
@@ -1320,27 +1323,31 @@ pub struct MaterialProperties {
13201323

13211324
impl MaterialProperties {
13221325
pub fn get_shader(&self, label: impl ShaderLabel) -> Option<Handle<Shader>> {
1323-
self.shaders.get(&label.intern()).cloned()
1326+
self.shaders
1327+
.iter()
1328+
.find(|(inner_label, _)| inner_label == &label.intern())
1329+
.map(|(_, shader)| shader)
1330+
.cloned()
13241331
}
13251332

1326-
pub fn add_shader(
1327-
&mut self,
1328-
label: impl ShaderLabel,
1329-
shader: Handle<Shader>,
1330-
) -> Option<Handle<Shader>> {
1331-
self.shaders.insert(label.intern(), shader)
1333+
pub fn add_shader(&mut self, label: impl ShaderLabel, shader: Handle<Shader>) {
1334+
self.shaders.push((label.intern(), shader));
13321335
}
13331336

13341337
pub fn get_draw_function(&self, label: impl DrawFunctionLabel) -> Option<DrawFunctionId> {
1335-
self.draw_functions.get(&label.intern()).copied()
1338+
self.draw_functions
1339+
.iter()
1340+
.find(|(inner_label, _)| inner_label == &label.intern())
1341+
.map(|(_, shader)| shader)
1342+
.cloned()
13361343
}
13371344

13381345
pub fn add_draw_function(
13391346
&mut self,
13401347
label: impl DrawFunctionLabel,
13411348
draw_function: DrawFunctionId,
1342-
) -> Option<DrawFunctionId> {
1343-
self.draw_functions.insert(label.intern(), draw_function)
1349+
) {
1350+
self.draw_functions.push((label.intern(), draw_function));
13441351
}
13451352
}
13461353

@@ -1472,27 +1479,27 @@ where
14721479
_ => None,
14731480
};
14741481

1475-
let mut draw_functions = HashMap::new();
1476-
draw_functions.insert(MaterialDrawFunction.intern(), draw_function_id);
1482+
let mut draw_functions = SmallVec::new();
1483+
draw_functions.push((MaterialDrawFunction.intern(), draw_function_id));
14771484
if let Some(prepass_draw_function_id) = prepass_draw_function_id {
1478-
draw_functions.insert(PrepassDrawFunction.intern(), prepass_draw_function_id);
1485+
draw_functions.push((PrepassDrawFunction.intern(), prepass_draw_function_id));
14791486
}
14801487
if let Some(deferred_draw_function_id) = deferred_draw_function_id {
1481-
draw_functions.insert(DeferredDrawFunction.intern(), deferred_draw_function_id);
1488+
draw_functions.push((DeferredDrawFunction.intern(), deferred_draw_function_id));
14821489
}
14831490
if let Some(shadow_draw_function_id) = shadow_draw_function_id {
1484-
draw_functions.insert(ShadowsDrawFunction.intern(), shadow_draw_function_id);
1491+
draw_functions.push((ShadowsDrawFunction.intern(), shadow_draw_function_id));
14851492
}
14861493

1487-
let mut shaders = HashMap::new();
1494+
let mut shaders = SmallVec::new();
14881495
let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
14891496
let mayber_shader = match shader_ref {
14901497
ShaderRef::Default => None,
14911498
ShaderRef::Handle(handle) => Some(handle),
14921499
ShaderRef::Path(path) => Some(asset_server.load(path)),
14931500
};
14941501
if let Some(shader) = mayber_shader {
1495-
shaders.insert(label, shader);
1502+
shaders.push((label, shader));
14961503
}
14971504
};
14981505
add_shader(MaterialVertexShader.intern(), M::vertex_shader());

crates/bevy_pbr/src/prepass/mod.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -553,27 +553,18 @@ impl PrepassPipelineInternal {
553553
|| emulate_unclipped_depth
554554
|| (mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
555555
&& material_properties
556-
.shaders
557-
.get(&PrepassFragmentShader.intern())
556+
.get_shader(PrepassFragmentShader)
558557
.is_some());
559558

560559
let fragment = fragment_required.then(|| {
561560
// Use the fragment shader from the material
562561
let frag_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
563-
match material_properties
564-
.shaders
565-
.get(&DeferredFragmentShader.intern())
566-
.cloned()
567-
{
562+
match material_properties.get_shader(DeferredFragmentShader) {
568563
Some(frag_shader_handle) => frag_shader_handle,
569564
None => self.default_prepass_shader.clone(),
570565
}
571566
} else {
572-
match material_properties
573-
.shaders
574-
.get(&PrepassFragmentShader.intern())
575-
.cloned()
576-
{
567+
match material_properties.get_shader(PrepassFragmentShader) {
577568
Some(frag_shader_handle) => frag_shader_handle,
578569
None => self.default_prepass_shader.clone(),
579570
}

0 commit comments

Comments
 (0)