From 6c619397d533ac0c4931f1c3a4a33ba006a64a27 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 9 Apr 2025 14:32:10 -0700 Subject: [PATCH] Unify `RenderMaterialInstances` and `RenderMeshMaterialIds`, and fix an associated race condition. (#18734) Currently, `RenderMaterialInstances` and `RenderMeshMaterialIds` are very similar render-world resources: the former maps main world meshes to typed material asset IDs, and the latter maps main world meshes to untyped material asset IDs. This is needlessly-complex and wasteful, so this patch unifies the two in favor of a single untyped `RenderMaterialInstances` resource. This patch also fixes a subtle issue that could cause mesh materials to be incorrect if a `MeshMaterial3d` was removed and replaced with a `MeshMaterial3d` material in the same frame. The problematic pattern looks like: 1. `extract_mesh_materials` runs and, seeing the `Changed>` condition, adds an entry mapping the mesh to the new material to the untyped `RenderMeshMaterialIds`. 2. `extract_mesh_materials` runs and, seeing that the entity is present in `RemovedComponents>`, removes the entry from `RenderMeshMaterialIds`. 3. The material slot is now empty, and the mesh will show up as whatever material happens to be in slot 0 in the material data slab. This commit fixes the issue by splitting out `extract_mesh_materials` into *three* phases: *extraction*, *early sweeping*, and *late sweeping*, which run in that order: 1. The *extraction* system, which runs for each material, updates `RenderMaterialInstances` records whenever `MeshMaterial3d` components change, and updates a change tick so that the following system will know not to remove it. 2. The *early sweeping* system, which runs for each material, processes entities present in `RemovedComponents` and removes each such entity's record from `RenderMeshInstances` only if the extraction system didn't update it this frame. This system runs after *all* extraction systems have completed, fixing the race condition. 3. The *late sweeping* system, which runs only once regardless of the number of materials in the scene, processes entities present in `RemovedComponents` and, as in the early sweeping phase, removes each such entity's record from `RenderMeshInstances` only if the extraction system didn't update it this frame. At the end, the late sweeping system updates the change tick. Because this pattern happens relatively frequently, I think this PR should land for 0.16. --- crates/bevy_pbr/src/material.rs | 185 ++++++++++++++---- .../bevy_pbr/src/meshlet/instance_manager.rs | 28 +-- .../src/meshlet/material_pipeline_prepare.rs | 22 ++- crates/bevy_pbr/src/prepass/mod.rs | 20 +- crates/bevy_pbr/src/render/light.rs | 21 +- crates/bevy_pbr/src/render/mesh.rs | 39 +--- 6 files changed, 209 insertions(+), 106 deletions(-) diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 19afa24b9d..bc8e201ceb 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -306,7 +306,7 @@ where .init_resource::>() .init_resource::>() .init_resource::>() - .init_resource::>() + .init_resource::() .add_render_command::>() .add_render_command::>() .add_render_command::>() @@ -316,7 +316,13 @@ where .add_systems( ExtractSchedule, ( - extract_mesh_materials::.before(ExtractMeshesSet), + ( + extract_mesh_materials::, + early_sweep_material_instances::, + ) + .chain() + .before(late_sweep_material_instances) + .before(ExtractMeshesSet), extract_entities_needs_specialization::.after(extract_cameras), ), ) @@ -404,6 +410,13 @@ where } } +/// A dummy [`AssetId`] that we use as a placeholder whenever a mesh doesn't +/// have a material. +/// +/// See the comments in [`RenderMaterialInstances::mesh_material`] for more +/// information. +static DUMMY_MESH_MATERIAL: AssetId = AssetId::::invalid(); + /// A key uniquely identifying a specialized [`MaterialPipeline`]. pub struct MaterialPipelineKey { pub mesh_key: MeshPipelineKey, @@ -542,7 +555,7 @@ pub struct SetMaterialBindGroup(PhantomData); impl RenderCommand

for SetMaterialBindGroup { type Param = ( SRes>>, - SRes>, + SRes, SRes>, ); type ViewQuery = (); @@ -564,10 +577,13 @@ impl RenderCommand

for SetMaterial let material_instances = material_instances.into_inner(); let material_bind_group_allocator = material_bind_group_allocator.into_inner(); - let Some(material_asset_id) = material_instances.get(&item.main_entity()) else { + let Some(material_instance) = material_instances.instances.get(&item.main_entity()) else { return RenderCommandResult::Skip; }; - let Some(material) = materials.get(*material_asset_id) else { + let Ok(material_asset_id) = material_instance.asset_id.try_typed::() else { + return RenderCommandResult::Skip; + }; + let Some(material) = materials.get(material_asset_id) else { return RenderCommandResult::Skip; }; let Some(material_bind_group) = material_bind_group_allocator.get(material.binding.group) @@ -582,16 +598,45 @@ impl RenderCommand

for SetMaterial } } -/// Stores all extracted instances of a [`Material`] in the render world. -#[derive(Resource, Deref, DerefMut)] -pub struct RenderMaterialInstances(pub MainEntityHashMap>); +/// Stores all extracted instances of all [`Material`]s in the render world. +#[derive(Resource, Default)] +pub struct RenderMaterialInstances { + /// Maps from each entity in the main world to the + /// [`RenderMaterialInstance`] associated with it. + pub instances: MainEntityHashMap, + /// A monotonically-increasing counter, which we use to sweep + /// [`RenderMaterialInstances::instances`] when the entities and/or required + /// components are removed. + current_change_tick: Tick, +} -impl Default for RenderMaterialInstances { - fn default() -> Self { - Self(Default::default()) +impl RenderMaterialInstances { + /// Returns the mesh material ID for the entity with the given mesh, or a + /// dummy mesh material ID if the mesh has no material ID. + /// + /// Meshes almost always have materials, but in very specific circumstances + /// involving custom pipelines they won't. (See the + /// `specialized_mesh_pipelines` example.) + pub(crate) fn mesh_material(&self, entity: MainEntity) -> UntypedAssetId { + match self.instances.get(&entity) { + Some(render_instance) => render_instance.asset_id, + None => DUMMY_MESH_MATERIAL.into(), + } } } +/// The material associated with a single mesh instance in the main world. +/// +/// Note that this uses an [`UntypedAssetId`] and isn't generic over the +/// material type, for simplicity. +pub struct RenderMaterialInstance { + /// The material asset. + pub(crate) asset_id: UntypedAssetId, + /// The [`RenderMaterialInstances::current_change_tick`] at which this + /// material instance was last modified. + last_change_tick: Tick, +} + pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey { match alpha_mode { // Premultiplied and Add share the same pipeline key @@ -648,7 +693,7 @@ pub const fn screen_space_specular_transmission_pipeline_key( /// /// As [`crate::render::mesh::collect_meshes_for_gpu_building`] only considers /// meshes that were newly extracted, and it writes information from the -/// [`RenderMeshMaterialIds`] into the +/// [`RenderMaterialInstances`] into the /// [`crate::render::mesh::MeshInputUniform`], we must tell /// [`crate::render::mesh::extract_meshes_for_gpu_building`] to re-extract a /// mesh if its material changed. Otherwise, the material binding information in @@ -669,42 +714,94 @@ fn mark_meshes_as_changed_if_their_materials_changed( } } -/// Fills the [`RenderMaterialInstances`] and [`RenderMeshMaterialIds`] -/// resources from the meshes in the scene. +/// Fills the [`RenderMaterialInstances`] resources from the meshes in the +/// scene. fn extract_mesh_materials( - mut material_instances: ResMut>, - mut material_ids: ResMut, + mut material_instances: ResMut, changed_meshes_query: Extract< Query< (Entity, &ViewVisibility, &MeshMaterial3d), Or<(Changed, Changed>)>, >, >, - mut removed_visibilities_query: Extract>, - mut removed_materials_query: Extract>>, ) { + let last_change_tick = material_instances.current_change_tick; + for (entity, view_visibility, material) in &changed_meshes_query { if view_visibility.get() { - material_instances.insert(entity.into(), material.id()); - material_ids.insert(entity.into(), material.id().into()); + material_instances.instances.insert( + entity.into(), + RenderMaterialInstance { + asset_id: material.id().untyped(), + last_change_tick, + }, + ); } else { - material_instances.remove(&MainEntity::from(entity)); - material_ids.remove(entity.into()); + material_instances + .instances + .remove(&MainEntity::from(entity)); + } + } +} + +/// Removes mesh materials from [`RenderMaterialInstances`] when their +/// [`MeshMaterial3d`] components are removed. +/// +/// This is tricky because we have to deal with the case in which a material of +/// type A was removed and replaced with a material of type B in the same frame +/// (which is actually somewhat common of an operation). In this case, even +/// though an entry will be present in `RemovedComponents>`, +/// we must not remove the entry in `RenderMaterialInstances` which corresponds +/// to material B. To handle this case, we use change ticks to avoid removing +/// the entry if it was updated this frame. +/// +/// This is the first of two sweep phases. Because this phase runs once per +/// material type, we need a second phase in order to guarantee that we only +/// bump [`RenderMaterialInstances::current_change_tick`] once. +fn early_sweep_material_instances( + mut material_instances: ResMut, + mut removed_materials_query: Extract>>, +) where + M: Material, +{ + let last_change_tick = material_instances.current_change_tick; + + for entity in removed_materials_query.read() { + if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) { + // Only sweep the entry if it wasn't updated this frame. + if occupied_entry.get().last_change_tick != last_change_tick { + occupied_entry.remove(); + } + } + } +} + +/// Removes mesh materials from [`RenderMaterialInstances`] when their +/// [`ViewVisibility`] components are removed. +/// +/// This runs after all invocations of [`early_sweep_material_instances`] and is +/// responsible for bumping [`RenderMaterialInstances::current_change_tick`] in +/// preparation for a new frame. +fn late_sweep_material_instances( + mut material_instances: ResMut, + mut removed_visibilities_query: Extract>, +) { + let last_change_tick = material_instances.current_change_tick; + + for entity in removed_visibilities_query.read() { + if let Entry::Occupied(occupied_entry) = material_instances.instances.entry(entity.into()) { + // Only sweep the entry if it wasn't updated this frame. It's + // possible that a `ViewVisibility` component was removed and + // re-added in the same frame. + if occupied_entry.get().last_change_tick != last_change_tick { + occupied_entry.remove(); + } } } - for entity in removed_visibilities_query - .read() - .chain(removed_materials_query.read()) - { - // Only queue a mesh for removal if we didn't pick it up above. - // It's possible that a necessary component was removed and re-added in - // the same frame. - if !changed_meshes_query.contains(entity) { - material_instances.remove(&MainEntity::from(entity)); - material_ids.remove(entity.into()); - } - } + material_instances + .current_change_tick + .set(last_change_tick.get() + 1); } pub fn extract_entities_needs_specialization( @@ -852,7 +949,7 @@ pub fn specialize_material_meshes( render_meshes: Res>, render_materials: Res>>, render_mesh_instances: Res, - render_material_instances: Res>, + render_material_instances: Res, render_lightmaps: Res, render_visibility_ranges: Res, ( @@ -907,7 +1004,11 @@ pub fn specialize_material_meshes( .or_default(); for (_, visible_entity) in visible_entities.iter::() { - let Some(material_asset_id) = render_material_instances.get(visible_entity) else { + let Some(material_instance) = render_material_instances.instances.get(visible_entity) + else { + continue; + }; + let Ok(material_asset_id) = material_instance.asset_id.try_typed::() else { continue; }; let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap(); @@ -928,7 +1029,7 @@ pub fn specialize_material_meshes( let Some(mesh) = render_meshes.get(mesh_instance.mesh_asset_id) else { continue; }; - let Some(material) = render_materials.get(*material_asset_id) else { + let Some(material) = render_materials.get(material_asset_id) else { continue; }; let Some(material_bind_group) = @@ -1004,7 +1105,7 @@ pub fn specialize_material_meshes( pub fn queue_material_meshes( render_materials: Res>>, render_mesh_instances: Res, - render_material_instances: Res>, + render_material_instances: Res, mesh_allocator: Res, gpu_preprocessing_support: Res, mut opaque_render_phases: ResMut>, @@ -1054,14 +1155,18 @@ pub fn queue_material_meshes( continue; } - let Some(material_asset_id) = render_material_instances.get(visible_entity) else { + let Some(material_instance) = render_material_instances.instances.get(visible_entity) + else { + continue; + }; + let Ok(material_asset_id) = material_instance.asset_id.try_typed::() else { continue; }; let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity) else { continue; }; - let Some(material) = render_materials.get(*material_asset_id) else { + let Some(material) = render_materials.get(material_asset_id) else { continue; }; diff --git a/crates/bevy_pbr/src/meshlet/instance_manager.rs b/crates/bevy_pbr/src/meshlet/instance_manager.rs index 40a2d93277..8fbc12e95c 100644 --- a/crates/bevy_pbr/src/meshlet/instance_manager.rs +++ b/crates/bevy_pbr/src/meshlet/instance_manager.rs @@ -2,7 +2,7 @@ use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh, MeshletMesh3d use crate::{ Material, MaterialBindingId, MeshFlags, MeshTransforms, MeshUniform, NotShadowCaster, NotShadowReceiver, PreviousGlobalTransform, RenderMaterialBindings, RenderMaterialInstances, - RenderMeshMaterialIds, StandardMaterial, + StandardMaterial, }; use bevy_asset::{AssetEvent, AssetId, AssetServer, Assets, UntypedAssetId}; use bevy_ecs::{ @@ -90,7 +90,7 @@ impl InstanceManager { transform: &GlobalTransform, previous_transform: Option<&PreviousGlobalTransform>, render_layers: Option<&RenderLayers>, - mesh_material_ids: &RenderMeshMaterialIds, + mesh_material_ids: &RenderMaterialInstances, render_material_bindings: &RenderMaterialBindings, not_shadow_receiver: bool, not_shadow_caster: bool, @@ -193,7 +193,7 @@ pub fn extract_meshlet_mesh_entities( mut instance_manager: ResMut, // TODO: Replace main_world and system_state when Extract>> is possible mut main_world: ResMut, - mesh_material_ids: Res, + mesh_material_ids: Res, render_material_bindings: Res, mut system_state: Local< Option< @@ -275,20 +275,22 @@ pub fn extract_meshlet_mesh_entities( /// and note that the material is used by at least one entity in the scene. pub fn queue_material_meshlet_meshes( mut instance_manager: ResMut, - render_material_instances: Res>, + render_material_instances: Res, ) { let instance_manager = instance_manager.deref_mut(); for (i, (instance, _, _)) in instance_manager.instances.iter().enumerate() { - if let Some(material_asset_id) = render_material_instances.get(instance) { - if let Some(material_id) = instance_manager - .material_id_lookup - .get(&material_asset_id.untyped()) - { - instance_manager - .material_ids_present_in_scene - .insert(*material_id); - instance_manager.instance_material_ids.get_mut()[i] = *material_id; + if let Some(material_instance) = render_material_instances.instances.get(instance) { + if let Ok(material_asset_id) = material_instance.asset_id.try_typed::() { + if let Some(material_id) = instance_manager + .material_id_lookup + .get(&material_asset_id.untyped()) + { + instance_manager + .material_ids_present_in_scene + .insert(*material_id); + instance_manager.instance_material_ids.get_mut()[i] = *material_id; + } } } } diff --git a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs index 9d56e71ede..957efd12d0 100644 --- a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs +++ b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs @@ -37,7 +37,7 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass( material_pipeline: Res>, mesh_pipeline: Res, render_materials: Res>>, - render_material_instances: Res>, + render_material_instances: Res, material_bind_group_allocator: Res>, asset_server: Res, mut mesh_vertex_buffer_layouts: ResMut, @@ -148,8 +148,13 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass( view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); - for material_id in render_material_instances.values().collect::>() { - let Some(material) = render_materials.get(*material_id) else { + for material_id in render_material_instances + .instances + .values() + .flat_map(|instance| instance.asset_id.try_typed::().ok()) + .collect::>() + { + let Some(material) = render_materials.get(material_id) else { continue; }; let Some(material_bind_group) = @@ -256,7 +261,7 @@ pub fn prepare_material_meshlet_meshes_prepass( pipeline_cache: Res, prepass_pipeline: Res>, render_materials: Res>>, - render_material_instances: Res>, + render_material_instances: Res, mut mesh_vertex_buffer_layouts: ResMut, material_bind_group_allocator: Res>, asset_server: Res, @@ -293,8 +298,13 @@ pub fn prepare_material_meshlet_meshes_prepass( view_key |= MeshPipelineKey::from_primitive_topology(PrimitiveTopology::TriangleList); - for material_id in render_material_instances.values().collect::>() { - let Some(material) = render_materials.get(*material_id) else { + for material_id in render_material_instances + .instances + .values() + .flat_map(|instance| instance.asset_id.try_typed::().ok()) + .collect::>() + { + let Some(material) = render_materials.get(material_id) else { continue; }; let Some(material_bind_group) = diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index d287c37285..d7aa117341 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -872,7 +872,7 @@ pub fn specialize_prepass_material_meshes( render_meshes: Res>, render_materials: Res>>, render_mesh_instances: Res, - render_material_instances: Res>, + render_material_instances: Res, render_lightmaps: Res, render_visibility_ranges: Res, material_bind_group_allocator: Res>, @@ -938,7 +938,11 @@ pub fn specialize_prepass_material_meshes( .or_default(); for (_, visible_entity) in visible_entities.iter::() { - let Some(material_asset_id) = render_material_instances.get(visible_entity) else { + let Some(material_instance) = render_material_instances.instances.get(visible_entity) + else { + continue; + }; + let Ok(material_asset_id) = material_instance.asset_id.try_typed::() else { continue; }; let entity_tick = entity_specialization_ticks.get(visible_entity).unwrap(); @@ -956,7 +960,7 @@ pub fn specialize_prepass_material_meshes( else { continue; }; - let Some(material) = render_materials.get(*material_asset_id) else { + let Some(material) = render_materials.get(material_asset_id) else { continue; }; let Some(material_bind_group) = @@ -1061,7 +1065,7 @@ pub fn specialize_prepass_material_meshes( pub fn queue_prepass_material_meshes( render_mesh_instances: Res, render_materials: Res>>, - render_material_instances: Res>, + render_material_instances: Res, mesh_allocator: Res, gpu_preprocessing_support: Res, mut opaque_prepass_render_phases: ResMut>, @@ -1121,14 +1125,18 @@ pub fn queue_prepass_material_meshes( continue; } - let Some(material_asset_id) = render_material_instances.get(visible_entity) else { + let Some(material_instance) = render_material_instances.instances.get(visible_entity) + else { + continue; + }; + let Ok(material_asset_id) = material_instance.asset_id.try_typed::() else { continue; }; let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*visible_entity) else { continue; }; - let Some(material) = render_materials.get(*material_asset_id) else { + let Some(material) = render_materials.get(material_asset_id) else { continue; }; let (vertex_slab, index_slab) = mesh_allocator.mesh_slabs(&mesh_instance.mesh_asset_id); diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index eb549bd248..bbd0faa144 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1729,7 +1729,7 @@ pub fn specialize_shadows( Res>, Res, Res>>, - Res>, + Res, Res>, ), shadow_render_phases: Res>, @@ -1809,7 +1809,12 @@ pub fn specialize_shadows( .or_default(); for (_, visible_entity) in visible_entities.iter().copied() { - let Some(material_asset_id) = render_material_instances.get(&visible_entity) else { + let Some(material_instances) = + render_material_instances.instances.get(&visible_entity) + else { + continue; + }; + let Ok(material_asset_id) = material_instances.asset_id.try_typed::() else { continue; }; let entity_tick = entity_specialization_ticks.get(&visible_entity).unwrap(); @@ -1823,7 +1828,7 @@ pub fn specialize_shadows( if !needs_specialization { continue; } - let Some(material) = render_materials.get(*material_asset_id) else { + let Some(material) = render_materials.get(material_asset_id) else { continue; }; let Some(mesh_instance) = @@ -1906,7 +1911,7 @@ pub fn queue_shadows( shadow_draw_functions: Res>, render_mesh_instances: Res, render_materials: Res>>, - render_material_instances: Res>, + render_material_instances: Res, mut shadow_render_phases: ResMut>, gpu_preprocessing_support: Res, mesh_allocator: Res, @@ -1989,10 +1994,14 @@ pub fn queue_shadows( continue; } - let Some(material_asset_id) = render_material_instances.get(&main_entity) else { + let Some(material_instance) = render_material_instances.instances.get(&main_entity) + else { continue; }; - let Some(material) = render_materials.get(*material_asset_id) else { + let Ok(material_asset_id) = material_instance.asset_id.try_typed::() else { + continue; + }; + let Some(material) = render_materials.get(material_asset_id) else { continue; }; diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index e00153d97b..0136418e0f 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1,6 +1,6 @@ use crate::material_bind_groups::{MaterialBindGroupIndex, MaterialBindGroupSlot}; use allocator::MeshAllocator; -use bevy_asset::{load_internal_asset, AssetId, UntypedAssetId}; +use bevy_asset::{load_internal_asset, AssetId}; use bevy_core_pipeline::{ core_3d::{AlphaMask3d, Opaque3d, Transmissive3d, Transparent3d, CORE_3D_DEPTH_FORMAT}, deferred::{AlphaMask3dDeferred, Opaque3dDeferred}, @@ -193,7 +193,7 @@ impl Plugin for MeshRenderPlugin { .init_resource::() .init_resource::() .init_resource::() - .init_resource::() + .init_resource::() .configure_sets( ExtractSchedule, ExtractMeshesSet.after(view::extract_visibility_ranges), @@ -895,37 +895,6 @@ pub struct RenderMeshInstancesCpu(MainEntityHashMap); #[derive(Default, Deref, DerefMut)] pub struct RenderMeshInstancesGpu(MainEntityHashMap); -/// Maps each mesh instance to the material ID, and allocated binding ID, -/// associated with that mesh instance. -#[derive(Resource, Default)] -pub struct RenderMeshMaterialIds { - /// Maps the mesh instance to the material ID. - mesh_to_material: MainEntityHashMap, -} - -impl RenderMeshMaterialIds { - /// Returns the mesh material ID for the entity with the given mesh, or a - /// dummy mesh material ID if the mesh has no material ID. - /// - /// Meshes almost always have materials, but in very specific circumstances - /// involving custom pipelines they won't. (See the - /// `specialized_mesh_pipelines` example.) - pub(crate) fn mesh_material(&self, entity: MainEntity) -> UntypedAssetId { - self.mesh_to_material - .get(&entity) - .cloned() - .unwrap_or(AssetId::::invalid().into()) - } - - pub(crate) fn insert(&mut self, mesh_entity: MainEntity, material_id: UntypedAssetId) { - self.mesh_to_material.insert(mesh_entity, material_id); - } - - pub(crate) fn remove(&mut self, main_entity: MainEntity) { - self.mesh_to_material.remove(&main_entity); - } -} - impl RenderMeshInstances { /// Creates a new [`RenderMeshInstances`] instance. fn new(use_gpu_instance_buffer_builder: bool) -> RenderMeshInstances { @@ -1128,7 +1097,7 @@ impl RenderMeshInstanceGpuBuilder { current_input_buffer: &mut InstanceInputUniformBuffer, previous_input_buffer: &mut InstanceInputUniformBuffer, mesh_allocator: &MeshAllocator, - mesh_material_ids: &RenderMeshMaterialIds, + mesh_material_ids: &RenderMaterialInstances, render_material_bindings: &RenderMaterialBindings, render_lightmaps: &RenderLightmaps, skin_uniforms: &SkinUniforms, @@ -1673,7 +1642,7 @@ pub fn collect_meshes_for_gpu_building( mut mesh_culling_data_buffer: ResMut, mut render_mesh_instance_queues: ResMut, mesh_allocator: Res, - mesh_material_ids: Res, + mesh_material_ids: Res, render_material_bindings: Res, render_lightmaps: Res, skin_uniforms: Res,