Retain RenderMaterialInstances
and RenderMeshMaterialIds
from frame to frame. (#16985)
This commit makes Bevy use change detection to only update `RenderMaterialInstances` and `RenderMeshMaterialIds` when meshes have been added, changed, or removed. `extract_mesh_materials`, the system that extracts these, now follows the pattern that `extract_meshes_for_gpu_building` established. This improves frame time of `many_cubes` from 3.9ms to approximately 3.1ms, which slightly surpasses the performance of Bevy 0.14. (Resubmitted from #16878 to clean up history.)  --------- Co-authored-by: Charlotte McElwain <charlotte.c.mcelwain@gmail.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
93e5e6cb95
commit
72ddac140a
@ -439,7 +439,8 @@ impl Plugin for PbrPlugin {
|
||||
prepare_clusters.in_set(RenderSet::PrepareResources),
|
||||
),
|
||||
)
|
||||
.init_resource::<LightMeta>();
|
||||
.init_resource::<LightMeta>()
|
||||
.init_resource::<RenderMaterialBindings>();
|
||||
|
||||
render_app.world_mut().add_observer(add_light_view_entities);
|
||||
render_app
|
||||
|
@ -818,7 +818,9 @@ pub fn check_dir_light_mesh_visibility(
|
||||
for entities in defer_queue.iter_mut() {
|
||||
let mut iter = query.iter_many_mut(world, entities.iter());
|
||||
while let Some(mut view_visibility) = iter.fetch_next() {
|
||||
view_visibility.set();
|
||||
if !**view_visibility {
|
||||
view_visibility.set();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -940,12 +942,16 @@ pub fn check_point_light_mesh_visibility(
|
||||
if has_no_frustum_culling
|
||||
|| frustum.intersects_obb(aabb, &model_to_world, true, true)
|
||||
{
|
||||
view_visibility.set();
|
||||
if !**view_visibility {
|
||||
view_visibility.set();
|
||||
}
|
||||
visible_entities.push(entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view_visibility.set();
|
||||
if !**view_visibility {
|
||||
view_visibility.set();
|
||||
}
|
||||
for visible_entities in cubemap_visible_entities_local_queue.iter_mut()
|
||||
{
|
||||
visible_entities.push(entity);
|
||||
@ -1025,11 +1031,15 @@ pub fn check_point_light_mesh_visibility(
|
||||
if has_no_frustum_culling
|
||||
|| frustum.intersects_obb(aabb, &model_to_world, true, true)
|
||||
{
|
||||
view_visibility.set();
|
||||
if !**view_visibility {
|
||||
view_visibility.set();
|
||||
}
|
||||
spot_visible_entities_local_queue.push(entity);
|
||||
}
|
||||
} else {
|
||||
view_visibility.set();
|
||||
if !**view_visibility {
|
||||
view_visibility.set();
|
||||
}
|
||||
spot_visible_entities_local_queue.push(entity);
|
||||
}
|
||||
},
|
||||
|
@ -6,7 +6,7 @@ use crate::meshlet::{
|
||||
InstanceManager,
|
||||
};
|
||||
use crate::*;
|
||||
use bevy_asset::{Asset, AssetId, AssetServer};
|
||||
use bevy_asset::{Asset, AssetId, AssetServer, UntypedAssetId};
|
||||
use bevy_core_pipeline::{
|
||||
core_3d::{
|
||||
AlphaMask3d, Camera3d, Opaque3d, Opaque3dBatchSetKey, Opaque3dBinKey,
|
||||
@ -33,17 +33,18 @@ use bevy_render::{
|
||||
batching::gpu_preprocessing::GpuPreprocessingSupport,
|
||||
camera::TemporalJitter,
|
||||
extract_resource::ExtractResource,
|
||||
mesh::{Mesh3d, MeshVertexBufferLayoutRef, RenderMesh},
|
||||
mesh::{self, Mesh3d, MeshVertexBufferLayoutRef, RenderMesh},
|
||||
render_asset::{PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets},
|
||||
render_phase::*,
|
||||
render_resource::*,
|
||||
renderer::RenderDevice,
|
||||
sync_world::MainEntity,
|
||||
view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility},
|
||||
Extract,
|
||||
};
|
||||
use bevy_render::{mesh::allocator::MeshAllocator, sync_world::MainEntityHashMap};
|
||||
use bevy_render::{texture::FallbackImage, view::RenderVisibleEntities};
|
||||
use bevy_utils::hashbrown::hash_map::Entry;
|
||||
use bevy_utils::HashMap;
|
||||
use core::{hash::Hash, marker::PhantomData};
|
||||
use tracing::error;
|
||||
|
||||
@ -270,7 +271,13 @@ where
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_asset::<M>()
|
||||
.register_type::<MeshMaterial3d<M>>()
|
||||
.add_plugins(RenderAssetPlugin::<PreparedMaterial<M>>::default());
|
||||
.add_plugins(RenderAssetPlugin::<PreparedMaterial<M>>::default())
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
mark_meshes_as_changed_if_their_materials_changed::<M>
|
||||
.ambiguous_with_all()
|
||||
.after(mesh::mark_3d_meshes_as_changed_if_their_assets_changed),
|
||||
);
|
||||
|
||||
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app
|
||||
@ -282,7 +289,10 @@ where
|
||||
.add_render_command::<Opaque3d, DrawMaterial<M>>()
|
||||
.add_render_command::<AlphaMask3d, DrawMaterial<M>>()
|
||||
.init_resource::<SpecializedMeshPipelines<MaterialPipeline<M>>>()
|
||||
.add_systems(ExtractSchedule, extract_mesh_materials::<M>)
|
||||
.add_systems(
|
||||
ExtractSchedule,
|
||||
extract_mesh_materials::<M>.before(ExtractMeshesSet),
|
||||
)
|
||||
.add_systems(
|
||||
Render,
|
||||
queue_material_meshes::<M>
|
||||
@ -581,26 +591,64 @@ pub const fn screen_space_specular_transmission_pipeline_key(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_mesh_materials<M: Material>(
|
||||
/// A system that ensures that
|
||||
/// [`crate::render::mesh::extract_meshes_for_gpu_building`] re-extracts meshes
|
||||
/// whose materials changed.
|
||||
///
|
||||
/// 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
|
||||
/// [`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
|
||||
/// the [`crate::render::mesh::MeshInputUniform`] might not be updated properly.
|
||||
/// The easiest way to ensure that
|
||||
/// [`crate::render::mesh::extract_meshes_for_gpu_building`] re-extracts a mesh
|
||||
/// is to mark its [`Mesh3d`] as changed, so that's what this system does.
|
||||
fn mark_meshes_as_changed_if_their_materials_changed<M>(
|
||||
mut changed_meshes_query: Query<&mut Mesh3d, Changed<MeshMaterial3d<M>>>,
|
||||
) where
|
||||
M: Material,
|
||||
{
|
||||
for mut mesh in &mut changed_meshes_query {
|
||||
mesh.set_changed();
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills the [`RenderMaterialInstances`] and [`RenderMeshMaterialIds`]
|
||||
/// resources from the meshes in the scene.
|
||||
fn extract_mesh_materials<M: Material>(
|
||||
mut material_instances: ResMut<RenderMaterialInstances<M>>,
|
||||
mut material_ids: ResMut<RenderMeshMaterialIds>,
|
||||
mut material_bind_group_allocator: ResMut<MaterialBindGroupAllocator<M>>,
|
||||
query: Extract<Query<(Entity, &ViewVisibility, &MeshMaterial3d<M>)>>,
|
||||
changed_meshes_query: Extract<
|
||||
Query<
|
||||
(Entity, &ViewVisibility, &MeshMaterial3d<M>),
|
||||
Or<(Changed<ViewVisibility>, Changed<MeshMaterial3d<M>>)>,
|
||||
>,
|
||||
>,
|
||||
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
|
||||
mut removed_materials_query: Extract<RemovedComponents<MeshMaterial3d<M>>>,
|
||||
) {
|
||||
material_instances.clear();
|
||||
|
||||
for (entity, view_visibility, material) in &query {
|
||||
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());
|
||||
} else {
|
||||
material_instances.remove(&MainEntity::from(entity));
|
||||
material_ids.remove(entity.into());
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a slot for this material in the bind group.
|
||||
let material_id = material.id().untyped();
|
||||
material_ids
|
||||
.mesh_to_material
|
||||
.insert(entity.into(), material_id);
|
||||
if let Entry::Vacant(entry) = material_ids.material_to_binding.entry(material_id) {
|
||||
entry.insert(material_bind_group_allocator.allocate());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1019,6 +1067,14 @@ pub struct MaterialProperties {
|
||||
pub reads_view_transmission_texture: bool,
|
||||
}
|
||||
|
||||
/// A resource that maps each untyped material ID to its binding.
|
||||
///
|
||||
/// This duplicates information in `RenderAssets<M>`, but it doesn't have the
|
||||
/// `M` type parameter, so it can be used in untyped contexts like
|
||||
/// [`crate::render::mesh::collect_meshes_for_gpu_building`].
|
||||
#[derive(Resource, Default, Deref, DerefMut)]
|
||||
pub struct RenderMaterialBindings(HashMap<UntypedAssetId, MaterialBindingId>);
|
||||
|
||||
/// Data prepared for a [`Material`] instance.
|
||||
pub struct PreparedMaterial<M: Material> {
|
||||
pub binding: MaterialBindingId,
|
||||
@ -1033,8 +1089,8 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
|
||||
SRes<RenderDevice>,
|
||||
SRes<MaterialPipeline<M>>,
|
||||
SRes<DefaultOpaqueRendererMethod>,
|
||||
SRes<RenderMeshMaterialIds>,
|
||||
SResMut<MaterialBindGroupAllocator<M>>,
|
||||
SResMut<RenderMaterialBindings>,
|
||||
M::Param,
|
||||
);
|
||||
|
||||
@ -1045,19 +1101,15 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
|
||||
render_device,
|
||||
pipeline,
|
||||
default_opaque_render_method,
|
||||
mesh_material_ids,
|
||||
ref mut bind_group_allocator,
|
||||
ref mut render_material_bindings,
|
||||
ref mut material_param,
|
||||
): &mut SystemParamItem<Self::Param>,
|
||||
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
|
||||
// Fetch the material binding ID, so that we can write it in to the
|
||||
// `PreparedMaterial`.
|
||||
let Some(material_binding_id) = mesh_material_ids
|
||||
.material_to_binding
|
||||
.get(&material_id.untyped())
|
||||
else {
|
||||
return Err(PrepareAssetError::RetryNextUpdate(material));
|
||||
};
|
||||
// Allocate a material binding ID if needed.
|
||||
let material_binding_id = *render_material_bindings
|
||||
.entry(material_id.into())
|
||||
.or_insert_with(|| bind_group_allocator.allocate());
|
||||
|
||||
let method = match material.opaque_render_method() {
|
||||
OpaqueRendererMethod::Forward => OpaqueRendererMethod::Forward,
|
||||
@ -1077,10 +1129,10 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
|
||||
false,
|
||||
) {
|
||||
Ok(unprepared) => {
|
||||
bind_group_allocator.init(render_device, *material_binding_id, unprepared);
|
||||
bind_group_allocator.init(render_device, material_binding_id, unprepared);
|
||||
|
||||
Ok(PreparedMaterial {
|
||||
binding: *material_binding_id,
|
||||
binding: material_binding_id,
|
||||
properties: MaterialProperties {
|
||||
alpha_mode: material.alpha_mode(),
|
||||
depth_bias: material.depth_bias(),
|
||||
@ -1110,13 +1162,13 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
|
||||
Ok(prepared_bind_group) => {
|
||||
// Store the resulting bind group directly in the slot.
|
||||
bind_group_allocator.init_custom(
|
||||
*material_binding_id,
|
||||
material_binding_id,
|
||||
prepared_bind_group.bind_group,
|
||||
prepared_bind_group.data,
|
||||
);
|
||||
|
||||
Ok(PreparedMaterial {
|
||||
binding: *material_binding_id,
|
||||
binding: material_binding_id,
|
||||
properties: MaterialProperties {
|
||||
alpha_mode: material.alpha_mode(),
|
||||
depth_bias: material.depth_bias(),
|
||||
@ -1142,21 +1194,21 @@ impl<M: Material> RenderAsset for PreparedMaterial<M> {
|
||||
}
|
||||
|
||||
fn unload_asset(
|
||||
asset_id: AssetId<Self::SourceAsset>,
|
||||
(_, _, _, mesh_material_ids, ref mut bind_group_allocator, _): &mut SystemParamItem<
|
||||
Self::Param,
|
||||
>,
|
||||
source_asset: AssetId<Self::SourceAsset>,
|
||||
(
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
ref mut bind_group_allocator,
|
||||
ref mut render_material_bindings,
|
||||
_,
|
||||
): &mut SystemParamItem<Self::Param>,
|
||||
) {
|
||||
// Mark this material's slot in the binding array as free.
|
||||
|
||||
let Some(material_binding_id) = mesh_material_ids
|
||||
.material_to_binding
|
||||
.get(&asset_id.untyped())
|
||||
let Some(material_binding_id) = render_material_bindings.remove(&source_asset.untyped())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
bind_group_allocator.free(*material_binding_id);
|
||||
bind_group_allocator.free(material_binding_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh, MeshletMesh3d};
|
||||
use crate::{
|
||||
Material, MeshFlags, MeshTransforms, MeshUniform, NotShadowCaster, NotShadowReceiver,
|
||||
PreviousGlobalTransform, RenderMaterialInstances, RenderMeshMaterialIds,
|
||||
PreviousGlobalTransform, RenderMaterialBindings, RenderMaterialInstances,
|
||||
RenderMeshMaterialIds,
|
||||
};
|
||||
use bevy_asset::{AssetEvent, AssetServer, Assets, UntypedAssetId};
|
||||
use bevy_ecs::{
|
||||
@ -90,6 +91,7 @@ impl InstanceManager {
|
||||
previous_transform: Option<&PreviousGlobalTransform>,
|
||||
render_layers: Option<&RenderLayers>,
|
||||
mesh_material_ids: &RenderMeshMaterialIds,
|
||||
render_material_bindings: &RenderMaterialBindings,
|
||||
not_shadow_receiver: bool,
|
||||
not_shadow_caster: bool,
|
||||
) {
|
||||
@ -110,15 +112,11 @@ impl InstanceManager {
|
||||
flags: flags.bits(),
|
||||
};
|
||||
|
||||
let Some(mesh_material_asset_id) = mesh_material_ids.mesh_to_material.get(&instance) else {
|
||||
return;
|
||||
};
|
||||
let Some(mesh_material_binding_id) = mesh_material_ids
|
||||
.material_to_binding
|
||||
.get(mesh_material_asset_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let mesh_material = mesh_material_ids.mesh_material(instance);
|
||||
let mesh_material_binding_id = render_material_bindings
|
||||
.get(&mesh_material)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let mesh_uniform = MeshUniform::new(
|
||||
&transforms,
|
||||
@ -190,6 +188,7 @@ pub fn extract_meshlet_mesh_entities(
|
||||
// TODO: Replace main_world and system_state when Extract<ResMut<Assets<MeshletMesh>>> is possible
|
||||
mut main_world: ResMut<MainWorld>,
|
||||
mesh_material_ids: Res<RenderMeshMaterialIds>,
|
||||
render_material_bindings: Res<RenderMaterialBindings>,
|
||||
mut system_state: Local<
|
||||
Option<
|
||||
SystemState<(
|
||||
@ -259,6 +258,7 @@ pub fn extract_meshlet_mesh_entities(
|
||||
previous_transform,
|
||||
render_layers,
|
||||
&mesh_material_ids,
|
||||
&render_material_bindings,
|
||||
not_shadow_receiver,
|
||||
not_shadow_caster,
|
||||
);
|
||||
|
@ -38,8 +38,8 @@ use bevy_render::{
|
||||
renderer::{RenderAdapter, RenderDevice, RenderQueue},
|
||||
texture::DefaultImageSampler,
|
||||
view::{
|
||||
NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges, ViewTarget, ViewUniformOffset,
|
||||
ViewVisibility, VisibilityRange,
|
||||
self, NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges, ViewTarget,
|
||||
ViewUniformOffset, ViewVisibility, VisibilityRange,
|
||||
},
|
||||
Extract,
|
||||
};
|
||||
@ -160,6 +160,10 @@ impl Plugin for MeshRenderPlugin {
|
||||
.init_resource::<MorphIndices>()
|
||||
.init_resource::<MeshCullingDataBuffer>()
|
||||
.init_resource::<RenderMeshMaterialIds>()
|
||||
.configure_sets(
|
||||
ExtractSchedule,
|
||||
ExtractMeshesSet.after(view::extract_visibility_ranges),
|
||||
)
|
||||
.add_systems(
|
||||
ExtractSchedule,
|
||||
(
|
||||
@ -172,7 +176,7 @@ impl Plugin for MeshRenderPlugin {
|
||||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
set_mesh_motion_vector_flags.in_set(RenderSet::PrepareAssets),
|
||||
set_mesh_motion_vector_flags.in_set(RenderSet::PrepareMeshes),
|
||||
prepare_skins.in_set(RenderSet::PrepareResources),
|
||||
prepare_morphs.in_set(RenderSet::PrepareResources),
|
||||
prepare_mesh_bind_group.in_set(RenderSet::PrepareBindGroups),
|
||||
@ -220,9 +224,7 @@ impl Plugin for MeshRenderPlugin {
|
||||
gpu_preprocessing::delete_old_work_item_buffers::<MeshPipeline>
|
||||
.in_set(RenderSet::PrepareResources),
|
||||
collect_meshes_for_gpu_building
|
||||
.in_set(RenderSet::PrepareAssets)
|
||||
.after(allocator::allocate_and_free_meshes)
|
||||
.after(extract_skins)
|
||||
.in_set(RenderSet::PrepareMeshes)
|
||||
// This must be before
|
||||
// `set_mesh_motion_vector_flags` so it doesn't
|
||||
// overwrite those flags.
|
||||
@ -696,10 +698,7 @@ pub struct RenderMeshInstancesGpu(MainEntityHashMap<RenderMeshInstanceGpu>);
|
||||
#[derive(Resource, Default)]
|
||||
pub struct RenderMeshMaterialIds {
|
||||
/// Maps the mesh instance to the material ID.
|
||||
pub(crate) mesh_to_material: MainEntityHashMap<UntypedAssetId>,
|
||||
/// Maps the material ID to the binding ID, which describes the location of
|
||||
/// that material bind group data in memory.
|
||||
pub(crate) material_to_binding: HashMap<UntypedAssetId, MaterialBindingId>,
|
||||
mesh_to_material: MainEntityHashMap<UntypedAssetId>,
|
||||
}
|
||||
|
||||
impl RenderMeshMaterialIds {
|
||||
@ -709,15 +708,19 @@ impl RenderMeshMaterialIds {
|
||||
/// Meshes almost always have materials, but in very specific circumstances
|
||||
/// involving custom pipelines they won't. (See the
|
||||
/// `specialized_mesh_pipelines` example.)
|
||||
fn mesh_material_binding_id(&self, entity: MainEntity) -> MaterialBindingId {
|
||||
pub(crate) fn mesh_material(&self, entity: MainEntity) -> UntypedAssetId {
|
||||
self.mesh_to_material
|
||||
.get(&entity)
|
||||
.and_then(|mesh_material_asset_id| {
|
||||
self.material_to_binding
|
||||
.get(mesh_material_asset_id)
|
||||
.cloned()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.cloned()
|
||||
.unwrap_or(AssetId::<StandardMaterial>::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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -920,6 +923,7 @@ impl RenderMeshInstanceGpuBuilder {
|
||||
previous_input_buffer: &mut InstanceInputUniformBuffer<MeshInputUniform>,
|
||||
mesh_allocator: &MeshAllocator,
|
||||
mesh_material_ids: &RenderMeshMaterialIds,
|
||||
render_material_bindings: &RenderMaterialBindings,
|
||||
render_lightmaps: &RenderLightmaps,
|
||||
skin_indices: &SkinIndices,
|
||||
) -> u32 {
|
||||
@ -951,7 +955,11 @@ impl RenderMeshInstanceGpuBuilder {
|
||||
};
|
||||
|
||||
// Look up the material index.
|
||||
let mesh_material_binding_id = mesh_material_ids.mesh_material_binding_id(entity);
|
||||
let mesh_material = mesh_material_ids.mesh_material(entity);
|
||||
let mesh_material_binding_id = render_material_bindings
|
||||
.get(&mesh_material)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
self.shared.material_bindings_index = mesh_material_binding_id;
|
||||
|
||||
let lightmap_slot = match render_lightmaps.render_lightmaps.get(&entity) {
|
||||
@ -1394,6 +1402,7 @@ pub fn collect_meshes_for_gpu_building(
|
||||
mut render_mesh_instance_queues: ResMut<RenderMeshInstanceGpuQueues>,
|
||||
mesh_allocator: Res<MeshAllocator>,
|
||||
mesh_material_ids: Res<RenderMeshMaterialIds>,
|
||||
render_material_bindings: Res<RenderMaterialBindings>,
|
||||
render_lightmaps: Res<RenderLightmaps>,
|
||||
skin_indices: Res<SkinIndices>,
|
||||
) {
|
||||
@ -1432,6 +1441,7 @@ pub fn collect_meshes_for_gpu_building(
|
||||
previous_input_buffer,
|
||||
&mesh_allocator,
|
||||
&mesh_material_ids,
|
||||
&render_material_bindings,
|
||||
&render_lightmaps,
|
||||
&skin_indices,
|
||||
);
|
||||
@ -1458,6 +1468,7 @@ pub fn collect_meshes_for_gpu_building(
|
||||
previous_input_buffer,
|
||||
&mesh_allocator,
|
||||
&mesh_material_ids,
|
||||
&render_material_bindings,
|
||||
&render_lightmaps,
|
||||
&skin_indices,
|
||||
);
|
||||
|
@ -126,6 +126,8 @@ pub enum RenderSet {
|
||||
ExtractCommands,
|
||||
/// Prepare assets that have been created/modified/removed this frame.
|
||||
PrepareAssets,
|
||||
/// Prepares extracted meshes.
|
||||
PrepareMeshes,
|
||||
/// Create any additional views such as those used for shadow mapping.
|
||||
ManageViews,
|
||||
/// Queue drawable entities as phase items in render phases ready for
|
||||
@ -174,6 +176,7 @@ impl Render {
|
||||
schedule.configure_sets(
|
||||
(
|
||||
ExtractCommands,
|
||||
PrepareMeshes,
|
||||
ManageViews,
|
||||
Queue,
|
||||
PhaseSort,
|
||||
@ -185,7 +188,7 @@ impl Render {
|
||||
.chain(),
|
||||
);
|
||||
|
||||
schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
|
||||
schedule.configure_sets((ExtractCommands, PrepareAssets, PrepareMeshes, Prepare).chain());
|
||||
schedule.configure_sets(
|
||||
QueueMeshes
|
||||
.in_set(Queue)
|
||||
|
@ -21,7 +21,7 @@ use bevy_ecs::{
|
||||
SystemParamItem,
|
||||
},
|
||||
};
|
||||
pub use components::{Mesh2d, Mesh3d};
|
||||
pub use components::{mark_3d_meshes_as_changed_if_their_assets_changed, Mesh2d, Mesh3d};
|
||||
use wgpu::IndexFormat;
|
||||
|
||||
/// Adds the [`Mesh`] as an asset and makes sure that they are extracted and prepared for the GPU.
|
||||
@ -40,7 +40,7 @@ impl Plugin for MeshPlugin {
|
||||
.add_plugins(MeshAllocatorPlugin)
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
components::mark_3d_meshes_as_changed_if_their_assets_changed
|
||||
mark_3d_meshes_as_changed_if_their_assets_changed
|
||||
.ambiguous_with(VisibilitySystems::CalculateBounds),
|
||||
);
|
||||
|
||||
|
@ -28,15 +28,15 @@ use bevy_render::{
|
||||
ViewBinnedRenderPhases, ViewSortedRenderPhases,
|
||||
},
|
||||
render_resource::{
|
||||
AsBindGroup, AsBindGroupError, BindGroup, BindGroupId, BindGroupLayout, PipelineCache,
|
||||
RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline,
|
||||
AsBindGroup, AsBindGroupError, BindGroup, BindGroupId, BindGroupLayout, BindingResources,
|
||||
PipelineCache, RenderPipelineDescriptor, Shader, ShaderRef, SpecializedMeshPipeline,
|
||||
SpecializedMeshPipelineError, SpecializedMeshPipelines,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
sync_world::{MainEntity, MainEntityHashMap},
|
||||
view::{ExtractedView, Msaa, RenderVisibleEntities, ViewVisibility},
|
||||
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
};
|
||||
use bevy_render::{render_resource::BindingResources, sync_world::MainEntityHashMap};
|
||||
use core::{hash::Hash, marker::PhantomData};
|
||||
use derive_more::derive::From;
|
||||
use tracing::error;
|
||||
@ -281,15 +281,56 @@ impl<M: Material2d> Default for RenderMaterial2dInstances<M> {
|
||||
|
||||
pub fn extract_mesh_materials_2d<M: Material2d>(
|
||||
mut material_instances: ResMut<RenderMaterial2dInstances<M>>,
|
||||
query: Extract<Query<(Entity, &ViewVisibility, &MeshMaterial2d<M>), With<Mesh2d>>>,
|
||||
changed_meshes_query: Extract<
|
||||
Query<
|
||||
(Entity, &ViewVisibility, &MeshMaterial2d<M>),
|
||||
Or<(Changed<ViewVisibility>, Changed<MeshMaterial2d<M>>)>,
|
||||
>,
|
||||
>,
|
||||
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
|
||||
mut removed_materials_query: Extract<RemovedComponents<MeshMaterial2d<M>>>,
|
||||
) {
|
||||
material_instances.clear();
|
||||
|
||||
for (entity, view_visibility, material) in &query {
|
||||
for (entity, view_visibility, material) in &changed_meshes_query {
|
||||
if view_visibility.get() {
|
||||
material_instances.insert(entity.into(), material.id());
|
||||
add_mesh_instance(entity, material, &mut material_instances);
|
||||
} else {
|
||||
remove_mesh_instance(entity, &mut material_instances);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
remove_mesh_instance(entity, &mut material_instances);
|
||||
}
|
||||
}
|
||||
|
||||
// Adds or updates a mesh instance in the [`RenderMaterial2dInstances`]
|
||||
// array.
|
||||
fn add_mesh_instance<M>(
|
||||
entity: Entity,
|
||||
material: &MeshMaterial2d<M>,
|
||||
material_instances: &mut RenderMaterial2dInstances<M>,
|
||||
) where
|
||||
M: Material2d,
|
||||
{
|
||||
material_instances.insert(entity.into(), material.id());
|
||||
}
|
||||
|
||||
// Removes a mesh instance from the [`RenderMaterial2dInstances`] array.
|
||||
fn remove_mesh_instance<M>(
|
||||
entity: Entity,
|
||||
material_instances: &mut RenderMaterial2dInstances<M>,
|
||||
) where
|
||||
M: Material2d,
|
||||
{
|
||||
material_instances.remove(&MainEntity::from(entity));
|
||||
}
|
||||
}
|
||||
|
||||
/// Render pipeline data for a given [`Material2d`]
|
||||
|
Loading…
Reference in New Issue
Block a user