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.
This commit is contained in:
IceSentry 2025-06-28 14:43:56 -04:00 committed by GitHub
parent fb2bbb043c
commit 37bbbf753d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 31 deletions

View File

@ -1300,8 +1300,11 @@ pub struct MaterialProperties {
pub reads_view_transmission_texture: bool,
pub render_phase_type: RenderPhaseType,
pub material_layout: Option<BindGroupLayout>,
pub draw_functions: HashMap<InternedDrawFunctionLabel, DrawFunctionId>,
pub shaders: HashMap<InternedShaderLabel, Handle<Shader>>,
/// Backing array is a size of 4 because the `StandardMaterial` needs 4 draw functions by default
pub draw_functions: SmallVec<[(InternedDrawFunctionLabel, DrawFunctionId); 4]>,
/// Backing array is a size of 3 because the `StandardMaterial` has 3 custom shaders (`frag`, `prepass_frag`, `deferred_frag`) which is the
/// most common use case
pub shaders: SmallVec<[(InternedShaderLabel, Handle<Shader>); 3]>,
/// Whether this material *actually* uses bindless resources, taking the
/// platform support (or lack thereof) of bindless resources into account.
pub bindless: bool,
@ -1320,27 +1323,31 @@ pub struct MaterialProperties {
impl MaterialProperties {
pub fn get_shader(&self, label: impl ShaderLabel) -> Option<Handle<Shader>> {
self.shaders.get(&label.intern()).cloned()
self.shaders
.iter()
.find(|(inner_label, _)| inner_label == &label.intern())
.map(|(_, shader)| shader)
.cloned()
}
pub fn add_shader(
&mut self,
label: impl ShaderLabel,
shader: Handle<Shader>,
) -> Option<Handle<Shader>> {
self.shaders.insert(label.intern(), shader)
pub fn add_shader(&mut self, label: impl ShaderLabel, shader: Handle<Shader>) {
self.shaders.push((label.intern(), shader));
}
pub fn get_draw_function(&self, label: impl DrawFunctionLabel) -> Option<DrawFunctionId> {
self.draw_functions.get(&label.intern()).copied()
self.draw_functions
.iter()
.find(|(inner_label, _)| inner_label == &label.intern())
.map(|(_, shader)| shader)
.cloned()
}
pub fn add_draw_function(
&mut self,
label: impl DrawFunctionLabel,
draw_function: DrawFunctionId,
) -> Option<DrawFunctionId> {
self.draw_functions.insert(label.intern(), draw_function)
) {
self.draw_functions.push((label.intern(), draw_function));
}
}
@ -1472,19 +1479,19 @@ where
_ => None,
};
let mut draw_functions = HashMap::new();
draw_functions.insert(MaterialDrawFunction.intern(), draw_function_id);
let mut draw_functions = SmallVec::new();
draw_functions.push((MaterialDrawFunction.intern(), draw_function_id));
if let Some(prepass_draw_function_id) = prepass_draw_function_id {
draw_functions.insert(PrepassDrawFunction.intern(), prepass_draw_function_id);
draw_functions.push((PrepassDrawFunction.intern(), prepass_draw_function_id));
}
if let Some(deferred_draw_function_id) = deferred_draw_function_id {
draw_functions.insert(DeferredDrawFunction.intern(), deferred_draw_function_id);
draw_functions.push((DeferredDrawFunction.intern(), deferred_draw_function_id));
}
if let Some(shadow_draw_function_id) = shadow_draw_function_id {
draw_functions.insert(ShadowsDrawFunction.intern(), shadow_draw_function_id);
draw_functions.push((ShadowsDrawFunction.intern(), shadow_draw_function_id));
}
let mut shaders = HashMap::new();
let mut shaders = SmallVec::new();
let mut add_shader = |label: InternedShaderLabel, shader_ref: ShaderRef| {
let mayber_shader = match shader_ref {
ShaderRef::Default => None,
@ -1492,7 +1499,7 @@ where
ShaderRef::Path(path) => Some(asset_server.load(path)),
};
if let Some(shader) = mayber_shader {
shaders.insert(label, shader);
shaders.push((label, shader));
}
};
add_shader(MaterialVertexShader.intern(), M::vertex_shader());

View File

@ -553,27 +553,18 @@ impl PrepassPipelineInternal {
|| emulate_unclipped_depth
|| (mesh_key.contains(MeshPipelineKey::MAY_DISCARD)
&& material_properties
.shaders
.get(&PrepassFragmentShader.intern())
.get_shader(PrepassFragmentShader)
.is_some());
let fragment = fragment_required.then(|| {
// Use the fragment shader from the material
let frag_shader_handle = if mesh_key.contains(MeshPipelineKey::DEFERRED_PREPASS) {
match material_properties
.shaders
.get(&DeferredFragmentShader.intern())
.cloned()
{
match material_properties.get_shader(DeferredFragmentShader) {
Some(frag_shader_handle) => frag_shader_handle,
None => self.default_prepass_shader.clone(),
}
} else {
match material_properties
.shaders
.get(&PrepassFragmentShader.intern())
.cloned()
{
match material_properties.get_shader(PrepassFragmentShader) {
Some(frag_shader_handle) => frag_shader_handle,
None => self.default_prepass_shader.clone(),
}