diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index c621ebc7fd215..e40bad7239c2c 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -38,9 +38,11 @@ pub use camera_2d::*; pub use main_opaque_pass_2d_node::*; pub use main_transparent_pass_2d_node::*; +use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode}; use bevy_app::{App, Plugin}; use bevy_ecs::{entity::EntityHashSet, prelude::*}; use bevy_math::FloatOrd; +use bevy_render::sync_world::MainEntity; use bevy_render::{ camera::{Camera, ExtractedCamera}, extract_component::ExtractComponentPlugin, @@ -61,8 +63,6 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode}; - use self::graph::{Core2d, Node2d}; pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float; @@ -129,7 +129,7 @@ pub struct Opaque2d { pub key: Opaque2dBinKey, /// An entity from which data will be fetched, including the mesh if /// applicable. - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), /// The ranges of instances. pub batch_range: Range, /// An extra index, which is either a dynamic offset or an index in the @@ -156,7 +156,11 @@ pub struct Opaque2dBinKey { impl PhaseItem for Opaque2d { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -188,7 +192,7 @@ impl BinnedPhaseItem for Opaque2d { fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { @@ -214,7 +218,7 @@ pub struct AlphaMask2d { pub key: AlphaMask2dBinKey, /// An entity from which data will be fetched, including the mesh if /// applicable. - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), /// The ranges of instances. pub batch_range: Range, /// An extra index, which is either a dynamic offset or an index in the @@ -241,7 +245,12 @@ pub struct AlphaMask2dBinKey { impl PhaseItem for AlphaMask2d { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + #[inline] + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -273,7 +282,7 @@ impl BinnedPhaseItem for AlphaMask2d { fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { @@ -296,7 +305,7 @@ impl CachedRenderPipelinePhaseItem for AlphaMask2d { /// Transparent 2D [`SortedPhaseItem`]s. pub struct Transparent2d { pub sort_key: FloatOrd, - pub entity: Entity, + pub entity: (Entity, MainEntity), pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, pub batch_range: Range, @@ -306,7 +315,12 @@ pub struct Transparent2d { impl PhaseItem for Transparent2d { #[inline] fn entity(&self) -> Entity { - self.entity + self.entity.0 + } + + #[inline] + fn main_entity(&self) -> MainEntity { + self.entity.1 } #[inline] diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 5389e5c9bdf13..559e757d71d51 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -74,6 +74,7 @@ pub use main_transparent_pass_3d_node::*; use bevy_app::{App, Plugin, PostUpdate}; use bevy_ecs::{entity::EntityHashSet, prelude::*}; use bevy_math::FloatOrd; +use bevy_render::sync_world::MainEntity; use bevy_render::{ camera::{Camera, ExtractedCamera}, extract_component::ExtractComponentPlugin, @@ -214,7 +215,7 @@ pub struct Opaque3d { pub key: Opaque3dBinKey, /// An entity from which data will be fetched, including the mesh if /// applicable. - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), /// The ranges of instances. pub batch_range: Range, /// An extra index, which is either a dynamic offset or an index in the @@ -249,7 +250,12 @@ pub struct Opaque3dBinKey { impl PhaseItem for Opaque3d { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + #[inline] + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -282,7 +288,7 @@ impl BinnedPhaseItem for Opaque3d { #[inline] fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { @@ -304,7 +310,7 @@ impl CachedRenderPipelinePhaseItem for Opaque3d { pub struct AlphaMask3d { pub key: OpaqueNoLightmap3dBinKey, - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, } @@ -312,7 +318,11 @@ pub struct AlphaMask3d { impl PhaseItem for AlphaMask3d { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -347,7 +357,7 @@ impl BinnedPhaseItem for AlphaMask3d { #[inline] fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { @@ -370,7 +380,7 @@ impl CachedRenderPipelinePhaseItem for AlphaMask3d { pub struct Transmissive3d { pub distance: f32, pub pipeline: CachedRenderPipelineId, - pub entity: Entity, + pub entity: (Entity, MainEntity), pub draw_function: DrawFunctionId, pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, @@ -390,7 +400,12 @@ impl PhaseItem for Transmissive3d { #[inline] fn entity(&self) -> Entity { - self.entity + self.entity.0 + } + + #[inline] + fn main_entity(&self) -> MainEntity { + self.entity.1 } #[inline] @@ -444,7 +459,7 @@ impl CachedRenderPipelinePhaseItem for Transmissive3d { pub struct Transparent3d { pub distance: f32, pub pipeline: CachedRenderPipelineId, - pub entity: Entity, + pub entity: (Entity, MainEntity), pub draw_function: DrawFunctionId, pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, @@ -453,7 +468,11 @@ pub struct Transparent3d { impl PhaseItem for Transparent3d { #[inline] fn entity(&self) -> Entity { - self.entity + self.entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.entity.1 } #[inline] diff --git a/crates/bevy_core_pipeline/src/deferred/mod.rs b/crates/bevy_core_pipeline/src/deferred/mod.rs index ef20bf7ee59d6..6ae4108d80492 100644 --- a/crates/bevy_core_pipeline/src/deferred/mod.rs +++ b/crates/bevy_core_pipeline/src/deferred/mod.rs @@ -3,7 +3,9 @@ pub mod node; use core::ops::Range; +use crate::prepass::OpaqueNoLightmap3dBinKey; use bevy_ecs::prelude::*; +use bevy_render::sync_world::MainEntity; use bevy_render::{ render_phase::{ BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, @@ -12,8 +14,6 @@ use bevy_render::{ render_resource::{CachedRenderPipelineId, TextureFormat}, }; -use crate::prepass::OpaqueNoLightmap3dBinKey; - pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint; pub const DEFERRED_LIGHTING_PASS_ID_FORMAT: TextureFormat = TextureFormat::R8Uint; pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth16Unorm; @@ -26,7 +26,7 @@ pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat: #[derive(PartialEq, Eq, Hash)] pub struct Opaque3dDeferred { pub key: OpaqueNoLightmap3dBinKey, - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, } @@ -34,7 +34,11 @@ pub struct Opaque3dDeferred { impl PhaseItem for Opaque3dDeferred { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -69,7 +73,7 @@ impl BinnedPhaseItem for Opaque3dDeferred { #[inline] fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { @@ -96,7 +100,7 @@ impl CachedRenderPipelinePhaseItem for Opaque3dDeferred { /// Used to render all meshes with a material with an alpha mask. pub struct AlphaMask3dDeferred { pub key: OpaqueNoLightmap3dBinKey, - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, } @@ -104,7 +108,12 @@ pub struct AlphaMask3dDeferred { impl PhaseItem for AlphaMask3dDeferred { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + #[inline] + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -138,7 +147,7 @@ impl BinnedPhaseItem for AlphaMask3dDeferred { fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { diff --git a/crates/bevy_core_pipeline/src/prepass/mod.rs b/crates/bevy_core_pipeline/src/prepass/mod.rs index 4829c76ee7653..f8151a0e163c4 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -29,10 +29,12 @@ pub mod node; use core::ops::Range; +use crate::deferred::{DEFERRED_LIGHTING_PASS_ID_FORMAT, DEFERRED_PREPASS_FORMAT}; use bevy_asset::UntypedAssetId; use bevy_ecs::prelude::*; use bevy_math::Mat4; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::sync_world::MainEntity; use bevy_render::{ render_phase::{ BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem, @@ -45,8 +47,6 @@ use bevy_render::{ texture::ColorAttachment, }; -use crate::deferred::{DEFERRED_LIGHTING_PASS_ID_FORMAT, DEFERRED_PREPASS_FORMAT}; - pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm; pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float; @@ -143,8 +143,7 @@ pub struct Opaque3dPrepass { /// An entity from which Bevy fetches data common to all instances in this /// batch, such as the mesh. - pub representative_entity: Entity, - + pub representative_entity: (Entity, MainEntity), pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, } @@ -171,7 +170,11 @@ pub struct OpaqueNoLightmap3dBinKey { impl PhaseItem for Opaque3dPrepass { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -206,7 +209,7 @@ impl BinnedPhaseItem for Opaque3dPrepass { #[inline] fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { @@ -233,7 +236,7 @@ impl CachedRenderPipelinePhaseItem for Opaque3dPrepass { /// Used to render all meshes with a material with an alpha mask. pub struct AlphaMask3dPrepass { pub key: OpaqueNoLightmap3dBinKey, - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, } @@ -241,7 +244,11 @@ pub struct AlphaMask3dPrepass { impl PhaseItem for AlphaMask3dPrepass { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -276,7 +283,7 @@ impl BinnedPhaseItem for AlphaMask3dPrepass { #[inline] fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index 3cffb4480c173..995b59af2243b 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -14,6 +14,7 @@ use bevy_ecs::{ world::{FromWorld, World}, }; use bevy_math::FloatOrd; +use bevy_render::sync_world::MainEntity; use bevy_render::{ render_asset::{prepare_assets, RenderAssets}, render_phase::{ @@ -283,7 +284,7 @@ fn queue_line_gizmos_2d( pipeline: Res, mut pipelines: ResMut>, pipeline_cache: Res, - line_gizmos: Query<(Entity, &GizmoMeshConfig)>, + line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, mut views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>, @@ -299,7 +300,7 @@ fn queue_line_gizmos_2d( | Mesh2dPipelineKey::from_hdr(view.hdr); let render_layers = render_layers.unwrap_or_default(); - for (entity, config) in &line_gizmos { + for (entity, main_entity, config) in &line_gizmos { if !config.render_layers.intersects(render_layers) { continue; } @@ -319,7 +320,7 @@ fn queue_line_gizmos_2d( ); transparent_phase.add(Transparent2d { - entity, + entity: (entity, *main_entity), draw_function, pipeline, sort_key: FloatOrd(f32::INFINITY), @@ -336,7 +337,7 @@ fn queue_line_joint_gizmos_2d( pipeline: Res, mut pipelines: ResMut>, pipeline_cache: Res, - line_gizmos: Query<(Entity, &GizmoMeshConfig)>, + line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, mut views: Query<(Entity, &ExtractedView, &Msaa, Option<&RenderLayers>)>, @@ -355,7 +356,7 @@ fn queue_line_joint_gizmos_2d( | Mesh2dPipelineKey::from_hdr(view.hdr); let render_layers = render_layers.unwrap_or_default(); - for (entity, config) in &line_gizmos { + for (entity, main_entity, config) in &line_gizmos { if !config.render_layers.intersects(render_layers) { continue; } @@ -377,7 +378,7 @@ fn queue_line_joint_gizmos_2d( }, ); transparent_phase.add(Transparent2d { - entity, + entity: (entity, *main_entity), draw_function, pipeline, sort_key: FloatOrd(f32::INFINITY), diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index 5b9d156d06b70..6d16db4711622 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -18,6 +18,7 @@ use bevy_ecs::{ world::{FromWorld, World}, }; use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup}; +use bevy_render::sync_world::MainEntity; use bevy_render::{ render_asset::{prepare_assets, RenderAssets}, render_phase::{ @@ -278,7 +279,7 @@ fn queue_line_gizmos_3d( pipeline: Res, mut pipelines: ResMut>, pipeline_cache: Res, - line_gizmos: Query<(Entity, &GizmoMeshConfig)>, + line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, mut views: Query<( @@ -329,7 +330,7 @@ fn queue_line_gizmos_3d( view_key |= MeshPipelineKey::DEFERRED_PREPASS; } - for (entity, config) in &line_gizmos { + for (entity, main_entity, config) in &line_gizmos { if !config.render_layers.intersects(render_layers) { continue; } @@ -350,7 +351,7 @@ fn queue_line_gizmos_3d( ); transparent_phase.add(Transparent3d { - entity, + entity: (entity, *main_entity), draw_function, pipeline, distance: 0., @@ -367,7 +368,7 @@ fn queue_line_joint_gizmos_3d( pipeline: Res, mut pipelines: ResMut>, pipeline_cache: Res, - line_gizmos: Query<(Entity, &GizmoMeshConfig)>, + line_gizmos: Query<(Entity, &MainEntity, &GizmoMeshConfig)>, line_gizmo_assets: Res>, mut transparent_render_phases: ResMut>, mut views: Query<( @@ -421,7 +422,7 @@ fn queue_line_joint_gizmos_3d( view_key |= MeshPipelineKey::DEFERRED_PREPASS; } - for (entity, config) in &line_gizmos { + for (entity, main_entity, config) in &line_gizmos { if !config.render_layers.intersects(render_layers) { continue; } @@ -445,7 +446,7 @@ fn queue_line_joint_gizmos_3d( ); transparent_phase.add(Transparent3d { - entity, + entity: (entity, *main_entity), draw_function, pipeline, distance: 0., diff --git a/crates/bevy_pbr/src/bundle.rs b/crates/bevy_pbr/src/bundle.rs index 107c56f719838..bdfdd695f5904 100644 --- a/crates/bevy_pbr/src/bundle.rs +++ b/crates/bevy_pbr/src/bundle.rs @@ -12,6 +12,7 @@ use bevy_ecs::{ reflect::ReflectComponent, }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::sync_world::MainEntity; use bevy_render::{ mesh::Mesh3d, primitives::{CascadesFrusta, CubemapFrusta, Frustum}, @@ -71,6 +72,13 @@ pub struct VisibleMeshEntities { pub entities: Vec, } +#[derive(Component, Clone, Debug, Default, Reflect, Deref, DerefMut)] +#[reflect(Component, Debug, Default)] +pub struct RenderVisibleMeshEntities { + #[reflect(ignore)] + pub entities: Vec<(Entity, MainEntity)>, +} + #[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component, Debug, Default)] pub struct CubemapVisibleEntities { @@ -96,6 +104,31 @@ impl CubemapVisibleEntities { } } +#[derive(Component, Clone, Debug, Default, Reflect)] +#[reflect(Component, Debug, Default)] +pub struct RenderCubemapVisibleEntities { + #[reflect(ignore)] + pub(crate) data: [RenderVisibleMeshEntities; 6], +} + +impl RenderCubemapVisibleEntities { + pub fn get(&self, i: usize) -> &RenderVisibleMeshEntities { + &self.data[i] + } + + pub fn get_mut(&mut self, i: usize) -> &mut RenderVisibleMeshEntities { + &mut self.data[i] + } + + pub fn iter(&self) -> impl DoubleEndedIterator { + self.data.iter() + } + + pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { + self.data.iter_mut() + } +} + #[derive(Component, Clone, Debug, Default, Reflect)] #[reflect(Component)] pub struct CascadesVisibleEntities { @@ -104,6 +137,14 @@ pub struct CascadesVisibleEntities { pub entities: EntityHashMap>, } +#[derive(Component, Clone, Debug, Default, Reflect)] +#[reflect(Component)] +pub struct RenderCascadesVisibleEntities { + /// Map of view entity to the visible entities for each cascade frustum. + #[reflect(ignore)] + pub entities: EntityHashMap>, +} + /// A component bundle for [`PointLight`] entities. #[derive(Debug, Bundle, Default, Clone)] #[deprecated( diff --git a/crates/bevy_pbr/src/lightmap/mod.rs b/crates/bevy_pbr/src/lightmap/mod.rs index f1b03f97af3f8..982f7214a8c81 100644 --- a/crates/bevy_pbr/src/lightmap/mod.rs +++ b/crates/bevy_pbr/src/lightmap/mod.rs @@ -34,13 +34,14 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, AssetId, Handle}; use bevy_ecs::{ component::Component, - entity::{Entity, EntityHashMap}, + entity::Entity, reflect::ReflectComponent, schedule::IntoSystemConfigs, system::{Query, Res, ResMut, Resource}, }; use bevy_math::{uvec2, vec4, Rect, UVec2}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::sync_world::MainEntityHashMap; use bevy_render::{ mesh::{Mesh, RenderMesh}, render_asset::RenderAssets, @@ -110,7 +111,7 @@ pub struct RenderLightmaps { /// /// Entities without lightmaps, or for which the mesh or lightmap isn't /// loaded, won't have entries in this table. - pub(crate) render_lightmaps: EntityHashMap, + pub(crate) render_lightmaps: MainEntityHashMap, /// All active lightmap images in the scene. /// @@ -161,7 +162,7 @@ fn extract_lightmaps( if !view_visibility.get() || images.get(&lightmap.image).is_none() || !render_mesh_instances - .mesh_asset_id(entity) + .mesh_asset_id(entity.into()) .and_then(|mesh_asset_id| meshes.get(mesh_asset_id)) .is_some_and(|mesh| mesh.layout.0.contains(Mesh::ATTRIBUTE_UV_1.id)) { @@ -170,7 +171,7 @@ fn extract_lightmaps( // Store information about the lightmap in the render world. render_lightmaps.render_lightmaps.insert( - entity, + entity.into(), RenderLightmap::new(lightmap.image.id(), lightmap.uv_rect), ); diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 903daab5c0d75..2a06aee19c20f 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -1,3 +1,4 @@ +use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight}; #[cfg(feature = "meshlet")] use crate::meshlet::{ prepare_material_meshlet_meshes_main_opaque_pass, queue_material_meshlet_meshes, @@ -18,12 +19,13 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - entity::EntityHashMap, prelude::*, system::{lifetimeless::SRes, SystemParamItem}, }; use bevy_reflect::std_traits::ReflectDefault; use bevy_reflect::Reflect; +use bevy_render::sync_world::MainEntityHashMap; +use bevy_render::view::RenderVisibleEntities; use bevy_render::{ camera::TemporalJitter, extract_resource::ExtractResource, @@ -32,7 +34,7 @@ use bevy_render::{ render_phase::*, render_resource::*, renderer::RenderDevice, - view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility, VisibleEntities}, + view::{ExtractedView, Msaa, RenderVisibilityRanges, ViewVisibility}, Extract, }; use bevy_utils::tracing::error; @@ -43,8 +45,6 @@ use core::{ sync::atomic::{AtomicU32, Ordering}, }; -use self::{irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight}; - /// Materials are used alongside [`MaterialPlugin`], [`Mesh3d`], and [`MeshMaterial3d`] /// to spawn entities that are rendered with a specific [`Material`] type. They serve as an easy to use high level /// way to render [`Mesh3d`] entities with custom shader logic. @@ -479,7 +479,7 @@ impl RenderCommand

for SetMaterial let materials = materials.into_inner(); let material_instances = material_instances.into_inner(); - let Some(material_asset_id) = material_instances.get(&item.entity()) else { + let Some(material_asset_id) = material_instances.get(&item.main_entity()) else { return RenderCommandResult::Skip; }; let Some(material) = materials.get(*material_asset_id) else { @@ -492,7 +492,7 @@ impl RenderCommand

for SetMaterial /// Stores all extracted instances of a [`Material`] in the render world. #[derive(Resource, Deref, DerefMut)] -pub struct RenderMaterialInstances(pub EntityHashMap>); +pub struct RenderMaterialInstances(pub MainEntityHashMap>); impl Default for RenderMaterialInstances { fn default() -> Self { @@ -562,7 +562,7 @@ fn extract_mesh_materials( ) { for (entity, view_visibility, material) in &query { if view_visibility.get() { - material_instances.insert(entity, material.id()); + material_instances.insert(entity.into(), material.id()); } } } @@ -574,7 +574,7 @@ pub(super) fn extract_default_materials( ) { for (entity, view_visibility) in &query { if view_visibility.get() { - material_instances.insert(entity, AssetId::default()); + material_instances.insert(entity.into(), AssetId::default()); } } } @@ -610,7 +610,7 @@ pub fn queue_material_meshes( views: Query<( Entity, &ExtractedView, - &VisibleEntities, + &RenderVisibleEntities, &Msaa, Option<&Tonemapping>, Option<&DebandDither>, @@ -744,7 +744,7 @@ pub fn queue_material_meshes( } let rangefinder = view.rangefinder3d(); - for visible_entity in visible_entities.iter::>() { + for (render_entity, visible_entity) in visible_entities.iter::>() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; @@ -825,7 +825,7 @@ pub fn queue_material_meshes( let distance = rangefinder.distance_translation(&mesh_instance.translation) + material.properties.depth_bias; transmissive_phase.add(Transmissive3d { - entity: *visible_entity, + entity: (*render_entity, *visible_entity), draw_function: draw_transmissive_pbr, pipeline: pipeline_id, distance, @@ -842,7 +842,7 @@ pub fn queue_material_meshes( }; opaque_phase.add( bin_key, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } @@ -853,7 +853,7 @@ pub fn queue_material_meshes( let distance = rangefinder.distance_translation(&mesh_instance.translation) + material.properties.depth_bias; transmissive_phase.add(Transmissive3d { - entity: *visible_entity, + entity: (*render_entity, *visible_entity), draw_function: draw_transmissive_pbr, pipeline: pipeline_id, distance, @@ -869,7 +869,7 @@ pub fn queue_material_meshes( }; alpha_mask_phase.add( bin_key, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } @@ -878,7 +878,7 @@ pub fn queue_material_meshes( let distance = rangefinder.distance_translation(&mesh_instance.translation) + material.properties.depth_bias; transparent_phase.add(Transparent3d { - entity: *visible_entity, + entity: (*render_entity, *visible_entity), draw_function: draw_transparent_pbr, pipeline: pipeline_id, distance, diff --git a/crates/bevy_pbr/src/meshlet/instance_manager.rs b/crates/bevy_pbr/src/meshlet/instance_manager.rs index 768864fe6b610..eec2f32d35790 100644 --- a/crates/bevy_pbr/src/meshlet/instance_manager.rs +++ b/crates/bevy_pbr/src/meshlet/instance_manager.rs @@ -10,6 +10,7 @@ use bevy_ecs::{ query::Has, system::{Local, Query, Res, ResMut, Resource, SystemState}, }; +use bevy_render::sync_world::MainEntity; use bevy_render::{render_resource::StorageBuffer, view::RenderLayers, MainWorld}; use bevy_transform::components::GlobalTransform; use bevy_utils::{HashMap, HashSet}; @@ -21,8 +22,8 @@ pub struct InstanceManager { /// Amount of clusters in the scene (sum of all meshlet counts across all instances) pub scene_cluster_count: u32, - /// Per-instance [`Entity`], [`RenderLayers`], and [`NotShadowCaster`] - pub instances: Vec<(Entity, RenderLayers, bool)>, + /// Per-instance [`MainEntity`], [`RenderLayers`], and [`NotShadowCaster`] + pub instances: Vec<(MainEntity, RenderLayers, bool)>, /// Per-instance [`MeshUniform`] pub instance_uniforms: StorageBuffer>, /// Per-instance material ID @@ -107,7 +108,7 @@ impl InstanceManager { // Append instance data self.instances.push(( - instance, + instance.into(), render_layers.cloned().unwrap_or(RenderLayers::default()), not_shadow_caster, )); diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 4ac34878df6ce..5bbe90b37feea 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -26,7 +26,7 @@ use bevy_render::{ render_phase::*, render_resource::*, renderer::{RenderDevice, RenderQueue}, - view::{ExtractedView, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities}, + view::{ExtractedView, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, }; use bevy_transform::prelude::GlobalTransform; @@ -39,6 +39,7 @@ use crate::meshlet::{ }; use crate::*; +use bevy_render::view::RenderVisibleEntities; use core::{hash::Hash, marker::PhantomData}; pub const PREPASS_SHADER_HANDLE: Handle = Handle::weak_from_u128(921124473254008983); @@ -697,7 +698,7 @@ pub fn queue_prepass_material_meshes( views: Query< ( Entity, - &VisibleEntities, + &RenderVisibleEntities, &Msaa, Option<&DepthPrepass>, Option<&NormalPrepass>, @@ -767,7 +768,7 @@ pub fn queue_prepass_material_meshes( view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS; } - for visible_entity in visible_entities.iter::>() { + for (render_entity, visible_entity) in visible_entities.iter::>() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; @@ -873,7 +874,7 @@ pub fn queue_prepass_material_meshes( asset_id: mesh_instance.mesh_asset_id.into(), material_bind_group_id: material.get_bind_group_id().0, }, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } else if let Some(opaque_phase) = opaque_phase.as_mut() { @@ -884,7 +885,7 @@ pub fn queue_prepass_material_meshes( asset_id: mesh_instance.mesh_asset_id.into(), material_bind_group_id: material.get_bind_group_id().0, }, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } @@ -900,7 +901,7 @@ pub fn queue_prepass_material_meshes( }; alpha_mask_deferred_phase.as_mut().unwrap().add( bin_key, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() { @@ -912,7 +913,7 @@ pub fn queue_prepass_material_meshes( }; alpha_mask_phase.add( bin_key, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 18a5a0955d6a3..6ddb265885ab4 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1,3 +1,4 @@ +use crate::*; use bevy_asset::UntypedAssetId; use bevy_color::ColorToComponents; use bevy_core_pipeline::core_3d::{Camera3d, CORE_3D_DEPTH_FORMAT}; @@ -8,7 +9,7 @@ use bevy_ecs::{ system::lifetimeless::Read, }; use bevy_math::{ops, Mat4, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; -use bevy_render::sync_world::RenderEntity; +use bevy_render::sync_world::{MainEntity, RenderEntity, TemporaryRenderEntity}; use bevy_render::{ diagnostic::RecordDiagnostics, mesh::RenderMesh, @@ -31,8 +32,6 @@ use bevy_utils::{ }; use core::{hash::Hash, ops::Range}; -use crate::*; - #[derive(Component)] pub struct ExtractedPointLight { pub color: LinearRgba, @@ -269,9 +268,15 @@ pub fn extract_lights( if !view_visibility.get() { continue; } - // TODO: This is very much not ideal. We should be able to re-use the vector memory. - // However, since exclusive access to the main world in extract is ill-advised, we just clone here. - let render_cubemap_visible_entities = cubemap_visible_entities.clone(); + let render_cubemap_visible_entities = RenderCubemapVisibleEntities { + data: cubemap_visible_entities + .iter() + .map(|v| create_render_visible_mesh_entities(&mut commands, &mapper, v)) + .collect::>() + .try_into() + .unwrap(), + }; + let extracted_point_light = ExtractedPointLight { color: point_light.color.into(), // NOTE: Map from luminous power in lumens to luminous intensity in lumens per steradian @@ -319,9 +324,9 @@ pub fn extract_lights( if !view_visibility.get() { continue; } - // TODO: This is very much not ideal. We should be able to re-use the vector memory. - // However, since exclusive access to the main world in extract is ill-advised, we just clone here. - let render_visible_entities = visible_entities.clone(); + let render_visible_entities = + create_render_visible_mesh_entities(&mut commands, &mapper, visible_entities); + let texel_size = 2.0 * ops::tan(spot_light.outer_angle) / directional_light_shadow_map.size as f32; @@ -397,7 +402,12 @@ pub fn extract_lights( } for (e, v) in visible_entities.entities.iter() { if let Ok(entity) = mapper.get(*e) { - cascade_visible_entities.insert(entity.id(), v.clone()); + cascade_visible_entities.insert( + entity.id(), + v.iter() + .map(|v| create_render_visible_mesh_entities(&mut commands, &mapper, v)) + .collect(), + ); } else { break; } @@ -423,13 +433,32 @@ pub fn extract_lights( frusta: extracted_frusta, render_layers: maybe_layers.unwrap_or_default().clone(), }, - CascadesVisibleEntities { + RenderCascadesVisibleEntities { entities: cascade_visible_entities, }, )); } } +fn create_render_visible_mesh_entities( + commands: &mut Commands, + mapper: &Extract>, + visible_entities: &VisibleMeshEntities, +) -> RenderVisibleMeshEntities { + RenderVisibleMeshEntities { + entities: visible_entities + .iter() + .map(|e| { + let render_entity = mapper + .get(*e) + .map(RenderEntity::id) + .unwrap_or_else(|_| commands.spawn(TemporaryRenderEntity).id()); + (render_entity, MainEntity::from(*e)) + }) + .collect(), + } +} + #[derive(Component, Default, Deref, DerefMut)] pub struct LightViewEntities(Vec); @@ -1352,9 +1381,12 @@ pub fn queue_shadows( render_lightmaps: Res, view_lights: Query<(Entity, &ViewLightEntities)>, view_light_entities: Query<&LightEntity>, - point_light_entities: Query<&CubemapVisibleEntities, With>, - directional_light_entities: Query<&CascadesVisibleEntities, With>, - spot_light_entities: Query<&VisibleMeshEntities, With>, + point_light_entities: Query<&RenderCubemapVisibleEntities, With>, + directional_light_entities: Query< + &RenderCascadesVisibleEntities, + With, + >, + spot_light_entities: Query<&RenderVisibleMeshEntities, With>, ) where M::Data: PartialEq + Eq + Hash + Clone, { @@ -1398,8 +1430,8 @@ pub fn queue_shadows( // NOTE: Lights with shadow mapping disabled will have no visible entities // so no meshes will be queued - for entity in visible_entities.iter().copied() { - let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity) + for (entity, main_entity) in visible_entities.iter().copied() { + let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(main_entity) else { continue; }; @@ -1409,7 +1441,7 @@ pub fn queue_shadows( { continue; } - let Some(material_asset_id) = render_material_instances.get(&entity) else { + let Some(material_asset_id) = render_material_instances.get(&main_entity) else { continue; }; let Some(material) = render_materials.get(*material_asset_id) else { @@ -1427,7 +1459,7 @@ pub fn queue_shadows( // we need to include the appropriate flag in the mesh pipeline key // to ensure that the necessary bind group layout entries are // present. - if render_lightmaps.render_lightmaps.contains_key(&entity) { + if render_lightmaps.render_lightmaps.contains_key(&main_entity) { mesh_key |= MeshPipelineKey::LIGHTMAPPED; } @@ -1467,7 +1499,7 @@ pub fn queue_shadows( pipeline: pipeline_id, asset_id: mesh_instance.mesh_asset_id.into(), }, - entity, + (entity, main_entity), BinnedRenderPhaseType::mesh(mesh_instance.should_batch()), ); } @@ -1477,7 +1509,7 @@ pub fn queue_shadows( pub struct Shadow { pub key: ShadowBinKey, - pub representative_entity: Entity, + pub representative_entity: (Entity, MainEntity), pub batch_range: Range, pub extra_index: PhaseItemExtraIndex, } @@ -1498,7 +1530,11 @@ pub struct ShadowBinKey { impl PhaseItem for Shadow { #[inline] fn entity(&self) -> Entity { - self.representative_entity + self.representative_entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.representative_entity.1 } #[inline] @@ -1533,7 +1569,7 @@ impl BinnedPhaseItem for Shadow { #[inline] fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self { diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index f59381b865358..af6621eb259e6 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -10,7 +10,6 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - entity::EntityHashMap, prelude::*, query::ROQueryItem, system::{lifetimeless::*, SystemParamItem, SystemState}, @@ -46,11 +45,6 @@ use bevy_utils::{ Entry, HashMap, Parallel, }; -use bytemuck::{Pod, Zeroable}; -use nonmax::{NonMaxU16, NonMaxU32}; -use smallvec::{smallvec, SmallVec}; -use static_assertions::const_assert_eq; - use crate::{ render::{ morph::{ @@ -61,6 +55,11 @@ use crate::{ }, *, }; +use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; +use bytemuck::{Pod, Zeroable}; +use nonmax::{NonMaxU16, NonMaxU32}; +use smallvec::{smallvec, SmallVec}; +use static_assertions::const_assert_eq; use self::irradiance_volume::IRRADIANCE_VOLUMES_ARE_USABLE; @@ -557,11 +556,11 @@ pub enum RenderMeshInstanceGpuQueue { /// The version of [`RenderMeshInstanceGpuQueue`] that omits the /// [`MeshCullingData`], so that we don't waste space when GPU /// culling is disabled. - CpuCulling(Vec<(Entity, RenderMeshInstanceGpuBuilder)>), + CpuCulling(Vec<(MainEntity, RenderMeshInstanceGpuBuilder)>), /// The version of [`RenderMeshInstanceGpuQueue`] that contains the /// [`MeshCullingData`], used when any view has GPU culling /// enabled. - GpuCulling(Vec<(Entity, RenderMeshInstanceGpuBuilder, MeshCullingData)>), + GpuCulling(Vec<(MainEntity, RenderMeshInstanceGpuBuilder, MeshCullingData)>), } /// The per-thread queues containing mesh instances, populated during the @@ -623,12 +622,12 @@ pub enum RenderMeshInstances { /// Information that the render world keeps about each entity that contains a /// mesh, when using CPU mesh instance data building. #[derive(Default, Deref, DerefMut)] -pub struct RenderMeshInstancesCpu(EntityHashMap); +pub struct RenderMeshInstancesCpu(MainEntityHashMap); /// Information that the render world keeps about each entity that contains a /// mesh, when using GPU mesh instance data building. #[derive(Default, Deref, DerefMut)] -pub struct RenderMeshInstancesGpu(EntityHashMap); +pub struct RenderMeshInstancesGpu(MainEntityHashMap); impl RenderMeshInstances { /// Creates a new [`RenderMeshInstances`] instance. @@ -641,7 +640,7 @@ impl RenderMeshInstances { } /// Returns the ID of the mesh asset attached to the given entity, if any. - pub(crate) fn mesh_asset_id(&self, entity: Entity) -> Option> { + pub(crate) fn mesh_asset_id(&self, entity: MainEntity) -> Option> { match *self { RenderMeshInstances::CpuBuilding(ref instances) => instances.mesh_asset_id(entity), RenderMeshInstances::GpuBuilding(ref instances) => instances.mesh_asset_id(entity), @@ -650,7 +649,7 @@ impl RenderMeshInstances { /// Constructs [`RenderMeshQueueData`] for the given entity, if it has a /// mesh attached. - pub fn render_mesh_queue_data(&self, entity: Entity) -> Option { + pub fn render_mesh_queue_data(&self, entity: MainEntity) -> Option { match *self { RenderMeshInstances::CpuBuilding(ref instances) => { instances.render_mesh_queue_data(entity) @@ -663,7 +662,7 @@ impl RenderMeshInstances { /// Inserts the given flags into the CPU or GPU render mesh instance data /// for the given mesh as appropriate. - fn insert_mesh_instance_flags(&mut self, entity: Entity, flags: RenderMeshInstanceFlags) { + fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) { match *self { RenderMeshInstances::CpuBuilding(ref mut instances) => { instances.insert_mesh_instance_flags(entity, flags); @@ -676,12 +675,12 @@ impl RenderMeshInstances { } impl RenderMeshInstancesCpu { - fn mesh_asset_id(&self, entity: Entity) -> Option> { + fn mesh_asset_id(&self, entity: MainEntity) -> Option> { self.get(&entity) .map(|render_mesh_instance| render_mesh_instance.mesh_asset_id) } - fn render_mesh_queue_data(&self, entity: Entity) -> Option { + fn render_mesh_queue_data(&self, entity: MainEntity) -> Option { self.get(&entity) .map(|render_mesh_instance| RenderMeshQueueData { shared: &render_mesh_instance.shared, @@ -691,7 +690,7 @@ impl RenderMeshInstancesCpu { /// Inserts the given flags into the render mesh instance data for the given /// mesh. - fn insert_mesh_instance_flags(&mut self, entity: Entity, flags: RenderMeshInstanceFlags) { + fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) { if let Some(instance) = self.get_mut(&entity) { instance.flags.insert(flags); } @@ -699,12 +698,12 @@ impl RenderMeshInstancesCpu { } impl RenderMeshInstancesGpu { - fn mesh_asset_id(&self, entity: Entity) -> Option> { + fn mesh_asset_id(&self, entity: MainEntity) -> Option> { self.get(&entity) .map(|render_mesh_instance| render_mesh_instance.mesh_asset_id) } - fn render_mesh_queue_data(&self, entity: Entity) -> Option { + fn render_mesh_queue_data(&self, entity: MainEntity) -> Option { self.get(&entity) .map(|render_mesh_instance| RenderMeshQueueData { shared: &render_mesh_instance.shared, @@ -714,7 +713,7 @@ impl RenderMeshInstancesGpu { /// Inserts the given flags into the render mesh instance data for the given /// mesh. - fn insert_mesh_instance_flags(&mut self, entity: Entity, flags: RenderMeshInstanceFlags) { + fn insert_mesh_instance_flags(&mut self, entity: MainEntity, flags: RenderMeshInstanceFlags) { if let Some(instance) = self.get_mut(&entity) { instance.flags.insert(flags); } @@ -739,7 +738,7 @@ impl RenderMeshInstanceGpuQueue { /// Adds a new mesh to this queue. fn push( &mut self, - entity: Entity, + entity: MainEntity, instance_builder: RenderMeshInstanceGpuBuilder, culling_data_builder: Option, ) { @@ -772,8 +771,8 @@ impl RenderMeshInstanceGpuBuilder { /// [`MeshInputUniform`] tables. fn add_to( self, - entity: Entity, - render_mesh_instances: &mut EntityHashMap, + entity: MainEntity, + render_mesh_instances: &mut MainEntityHashMap, current_input_buffer: &mut RawBufferVec, mesh_allocator: &MeshAllocator, ) -> usize { @@ -907,7 +906,7 @@ pub fn extract_meshes_for_cpu_building( let mut lod_index = None; if visibility_range { - lod_index = render_visibility_ranges.lod_index_for_entity(entity); + lod_index = render_visibility_ranges.lod_index_for_entity(entity.into()); } let mesh_flags = MeshFlags::from_components( @@ -954,7 +953,7 @@ pub fn extract_meshes_for_cpu_building( render_mesh_instances.clear(); for queue in render_mesh_instance_queues.iter_mut() { for (entity, render_mesh_instance) in queue.drain(..) { - render_mesh_instances.insert_unique_unchecked(entity, render_mesh_instance); + render_mesh_instances.insert_unique_unchecked(entity.into(), render_mesh_instance); } } } @@ -1023,7 +1022,7 @@ pub fn extract_meshes_for_gpu_building( let mut lod_index = None; if visibility_range { - lod_index = render_visibility_ranges.lod_index_for_entity(entity); + lod_index = render_visibility_ranges.lod_index_for_entity(entity.into()); } let mesh_flags = MeshFlags::from_components( @@ -1049,7 +1048,7 @@ pub fn extract_meshes_for_gpu_building( .contains(RenderMeshInstanceFlags::HAS_PREVIOUS_TRANSFORM) { render_mesh_instances - .get(&entity) + .get(&MainEntity::from(entity)) .map(|render_mesh_instance| render_mesh_instance.current_uniform_index) } else { None @@ -1063,7 +1062,11 @@ pub fn extract_meshes_for_gpu_building( previous_input_index, }; - queue.push(entity, gpu_mesh_instance_builder, gpu_mesh_culling_data); + queue.push( + entity.into(), + gpu_mesh_instance_builder, + gpu_mesh_culling_data, + ); }, ); } @@ -1285,7 +1288,7 @@ impl GetBatchData for MeshPipeline { fn get_batch_data( (mesh_instances, lightmaps, _, mesh_allocator): &SystemParamItem, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), ) -> Option<(Self::BufferData, Option)> { let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else { error!( @@ -1294,13 +1297,13 @@ impl GetBatchData for MeshPipeline { ); return None; }; - let mesh_instance = mesh_instances.get(&entity)?; + let mesh_instance = mesh_instances.get(&main_entity)?; let first_vertex_index = match mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) { Some(mesh_vertex_slice) => mesh_vertex_slice.range.start, None => 0, }; - let maybe_lightmap = lightmaps.render_lightmaps.get(&entity); + let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity); Some(( MeshUniform::new( @@ -1322,7 +1325,7 @@ impl GetFullBatchData for MeshPipeline { fn get_index_and_compare_data( (mesh_instances, lightmaps, _, _): &SystemParamItem, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), ) -> Option<(NonMaxU32, Option)> { // This should only be called during GPU building. let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else { @@ -1333,8 +1336,8 @@ impl GetFullBatchData for MeshPipeline { return None; }; - let mesh_instance = mesh_instances.get(&entity)?; - let maybe_lightmap = lightmaps.render_lightmaps.get(&entity); + let mesh_instance = mesh_instances.get(&main_entity)?; + let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity); Some(( mesh_instance.current_uniform_index, @@ -1348,7 +1351,7 @@ impl GetFullBatchData for MeshPipeline { fn get_binned_batch_data( (mesh_instances, lightmaps, _, mesh_allocator): &SystemParamItem, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), ) -> Option { let RenderMeshInstances::CpuBuilding(ref mesh_instances) = **mesh_instances else { error!( @@ -1356,13 +1359,13 @@ impl GetFullBatchData for MeshPipeline { ); return None; }; - let mesh_instance = mesh_instances.get(&entity)?; + let mesh_instance = mesh_instances.get(&main_entity)?; let first_vertex_index = match mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) { Some(mesh_vertex_slice) => mesh_vertex_slice.range.start, None => 0, }; - let maybe_lightmap = lightmaps.render_lightmaps.get(&entity); + let maybe_lightmap = lightmaps.render_lightmaps.get(&main_entity); Some(MeshUniform::new( &mesh_instance.transforms, @@ -1373,7 +1376,7 @@ impl GetFullBatchData for MeshPipeline { fn get_binned_index( (mesh_instances, _, _, _): &SystemParamItem, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), ) -> Option { // This should only be called during GPU building. let RenderMeshInstances::GpuBuilding(ref mesh_instances) = **mesh_instances else { @@ -1385,14 +1388,14 @@ impl GetFullBatchData for MeshPipeline { }; mesh_instances - .get(&entity) + .get(&main_entity) .map(|entity| entity.current_uniform_index) } fn get_batch_indirect_parameters_index( (mesh_instances, _, meshes, mesh_allocator): &SystemParamItem, indirect_parameters_buffer: &mut IndirectParametersBuffer, - entity: Entity, + entity: (Entity, MainEntity), instance_index: u32, ) -> Option { get_batch_indirect_parameters_index( @@ -1414,7 +1417,7 @@ fn get_batch_indirect_parameters_index( meshes: &RenderAssets, mesh_allocator: &MeshAllocator, indirect_parameters_buffer: &mut IndirectParametersBuffer, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), instance_index: u32, ) -> Option { // This should only be called during GPU building. @@ -1426,7 +1429,7 @@ fn get_batch_indirect_parameters_index( return None; }; - let mesh_instance = mesh_instances.get(&entity)?; + let mesh_instance = mesh_instances.get(&main_entity)?; let mesh = meshes.get(mesh_instance.mesh_asset_id)?; let vertex_buffer_slice = mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id)?; @@ -2259,7 +2262,7 @@ impl RenderCommand

for SetMeshBindGroup { let skin_indices = skin_indices.into_inner(); let morph_indices = morph_indices.into_inner(); - let entity = &item.entity(); + let entity = &item.main_entity(); let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(*entity) else { return RenderCommandResult::Success; @@ -2380,7 +2383,7 @@ impl RenderCommand

for DrawMesh { let indirect_parameters_buffer = indirect_parameters_buffer.into_inner(); let mesh_allocator = mesh_allocator.into_inner(); - let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.entity()) else { + let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.main_entity()) else { return RenderCommandResult::Skip; }; let Some(gpu_mesh) = meshes.get(mesh_asset_id) else { diff --git a/crates/bevy_pbr/src/render/morph.rs b/crates/bevy_pbr/src/render/morph.rs index c2696652a7a2f..4b1ed68ce87a3 100644 --- a/crates/bevy_pbr/src/render/morph.rs +++ b/crates/bevy_pbr/src/render/morph.rs @@ -1,6 +1,7 @@ use core::{iter, mem}; -use bevy_ecs::{entity::EntityHashMap, prelude::*}; +use bevy_ecs::prelude::*; +use bevy_render::sync_world::MainEntityHashMap; use bevy_render::{ batching::NoAutomaticBatching, mesh::morph::{MeshMorphWeights, MAX_MORPH_WEIGHTS}, @@ -25,11 +26,11 @@ pub struct MorphIndex { pub struct MorphIndices { /// Maps each entity with a morphed mesh to the appropriate offset within /// [`MorphUniforms::current_buffer`]. - pub current: EntityHashMap, + pub current: MainEntityHashMap, /// Maps each entity with a morphed mesh to the appropriate offset within /// [`MorphUniforms::prev_buffer`]. - pub prev: EntityHashMap, + pub prev: MainEntityHashMap, } /// The GPU buffers containing morph weights for all meshes with morph targets. @@ -131,7 +132,9 @@ pub fn extract_morphs( add_to_alignment::(&mut uniform.current_buffer); let index = (start * size_of::()) as u32; - morph_indices.current.insert(entity, MorphIndex { index }); + morph_indices + .current + .insert(entity.into(), MorphIndex { index }); } } diff --git a/crates/bevy_pbr/src/render/skin.rs b/crates/bevy_pbr/src/render/skin.rs index 1719789d55420..b6f35fc0bf49d 100644 --- a/crates/bevy_pbr/src/render/skin.rs +++ b/crates/bevy_pbr/src/render/skin.rs @@ -1,8 +1,9 @@ use core::mem::{self, size_of}; use bevy_asset::Assets; -use bevy_ecs::{entity::EntityHashMap, prelude::*}; +use bevy_ecs::prelude::*; use bevy_math::Mat4; +use bevy_render::sync_world::MainEntityHashMap; use bevy_render::{ batching::NoAutomaticBatching, mesh::skinning::{SkinnedMesh, SkinnedMeshInverseBindposes}, @@ -39,11 +40,11 @@ impl SkinIndex { pub struct SkinIndices { /// Maps each skinned mesh to the applicable offset within /// [`SkinUniforms::current_buffer`]. - pub current: EntityHashMap, + pub current: MainEntityHashMap, /// Maps each skinned mesh to the applicable offset within /// [`SkinUniforms::prev_buffer`]. - pub prev: EntityHashMap, + pub prev: MainEntityHashMap, } /// The GPU buffers containing joint matrices for all skinned meshes. @@ -168,7 +169,9 @@ pub fn extract_skins( buffer.push(Mat4::ZERO); } - skin_indices.current.insert(entity, SkinIndex::new(start)); + skin_indices + .current + .insert(entity.into(), SkinIndex::new(start)); } // Pad out the buffer to ensure that there's enough space for bindings diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index bd515c0840625..d6b490f8c40b4 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -433,10 +433,10 @@ pub fn batch_and_prepare_sorted_render_phase( for current_index in 0..phase.items.len() { // Get the index of the input data, and comparison metadata, for // this entity. - let current_batch_input_index = GFBD::get_index_and_compare_data( - &system_param_item, - phase.items[current_index].entity(), - ); + let item = &phase.items[current_index]; + let entity = (item.entity(), item.main_entity()); + let current_batch_input_index = + GFBD::get_index_and_compare_data(&system_param_item, entity); // Unpack that index and metadata. Note that it's possible for index // and/or metadata to not be present, which signifies that this @@ -464,7 +464,8 @@ pub fn batch_and_prepare_sorted_render_phase( }); // Make space in the data buffer for this instance. - let current_entity = phase.items[current_index].entity(); + let item = &phase.items[current_index]; + let entity = (item.entity(), item.main_entity()); let output_index = data_buffer.add() as u32; // If we can't batch, break the existing batch and make a new one. @@ -479,7 +480,7 @@ pub fn batch_and_prepare_sorted_render_phase( GFBD::get_batch_indirect_parameters_index( &system_param_item, &mut indirect_parameters_buffer, - current_entity, + entity, output_index, ) } else { @@ -551,8 +552,10 @@ pub fn batch_and_prepare_binned_render_phase( for key in &phase.batchable_mesh_keys { let mut batch: Option = None; - for &entity in &phase.batchable_mesh_values[key] { - let Some(input_index) = GFBD::get_binned_index(&system_param_item, entity) else { + for &(entity, main_entity) in &phase.batchable_mesh_values[key] { + let Some(input_index) = + GFBD::get_binned_index(&system_param_item, (entity, main_entity)) + else { continue; }; let output_index = data_buffer.add() as u32; @@ -573,7 +576,7 @@ pub fn batch_and_prepare_binned_render_phase( let indirect_parameters_index = GFBD::get_batch_indirect_parameters_index( &system_param_item, &mut indirect_parameters_buffer, - entity, + (entity, main_entity), output_index, ); work_item_buffer.buffer.push(PreprocessWorkItem { @@ -581,7 +584,7 @@ pub fn batch_and_prepare_binned_render_phase( output_index: indirect_parameters_index.unwrap_or_default().into(), }); batch = Some(BinnedRenderPhaseBatch { - representative_entity: entity, + representative_entity: (entity, main_entity), instance_range: output_index..output_index + 1, extra_index: PhaseItemExtraIndex::maybe_indirect_parameters_index( indirect_parameters_index, @@ -595,7 +598,7 @@ pub fn batch_and_prepare_binned_render_phase( output_index, }); batch = Some(BinnedRenderPhaseBatch { - representative_entity: entity, + representative_entity: (entity, main_entity), instance_range: output_index..output_index + 1, extra_index: PhaseItemExtraIndex::NONE, }); @@ -611,8 +614,10 @@ pub fn batch_and_prepare_binned_render_phase( // Prepare unbatchables. for key in &phase.unbatchable_mesh_keys { let unbatchables = phase.unbatchable_mesh_values.get_mut(key).unwrap(); - for &entity in &unbatchables.entities { - let Some(input_index) = GFBD::get_binned_index(&system_param_item, entity) else { + for &(entity, main_entity) in &unbatchables.entities { + let Some(input_index) = + GFBD::get_binned_index(&system_param_item, (entity, main_entity)) + else { continue; }; let output_index = data_buffer.add() as u32; @@ -621,7 +626,7 @@ pub fn batch_and_prepare_binned_render_phase( let indirect_parameters_index = GFBD::get_batch_indirect_parameters_index( &system_param_item, &mut indirect_parameters_buffer, - entity, + (entity, main_entity), output_index, ) .unwrap_or_default(); diff --git a/crates/bevy_render/src/batching/mod.rs b/crates/bevy_render/src/batching/mod.rs index 6287911e06249..d4b7e76fcbb15 100644 --- a/crates/bevy_render/src/batching/mod.rs +++ b/crates/bevy_render/src/batching/mod.rs @@ -6,6 +6,8 @@ use bevy_ecs::{ use bytemuck::Pod; use nonmax::NonMaxU32; +use self::gpu_preprocessing::IndirectParametersBuffer; +use crate::sync_world::MainEntity; use crate::{ render_phase::{ BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, SortedPhaseItem, @@ -14,8 +16,6 @@ use crate::{ render_resource::{CachedRenderPipelineId, GpuArrayBufferable}, }; -use self::gpu_preprocessing::IndirectParametersBuffer; - pub mod gpu_preprocessing; pub mod no_gpu_preprocessing; @@ -88,7 +88,7 @@ pub trait GetBatchData { /// [`GetFullBatchData::get_index_and_compare_data`] instead. fn get_batch_data( param: &SystemParamItem, - query_item: Entity, + query_item: (Entity, MainEntity), ) -> Option<(Self::BufferData, Option)>; } @@ -109,7 +109,7 @@ pub trait GetFullBatchData: GetBatchData { /// [`GetFullBatchData::get_index_and_compare_data`] instead. fn get_binned_batch_data( param: &SystemParamItem, - query_item: Entity, + query_item: (Entity, MainEntity), ) -> Option; /// Returns the index of the [`GetFullBatchData::BufferInputData`] that the @@ -121,7 +121,7 @@ pub trait GetFullBatchData: GetBatchData { /// function will never be called. fn get_index_and_compare_data( param: &SystemParamItem, - query_item: Entity, + query_item: (Entity, MainEntity), ) -> Option<(NonMaxU32, Option)>; /// Returns the index of the [`GetFullBatchData::BufferInputData`] that the @@ -133,7 +133,7 @@ pub trait GetFullBatchData: GetBatchData { /// function will never be called. fn get_binned_index( param: &SystemParamItem, - query_item: Entity, + query_item: (Entity, MainEntity), ) -> Option; /// Pushes [`gpu_preprocessing::IndirectParameters`] necessary to draw this @@ -145,7 +145,7 @@ pub trait GetFullBatchData: GetBatchData { fn get_batch_indirect_parameters_index( param: &SystemParamItem, indirect_parameters_buffer: &mut IndirectParametersBuffer, - entity: Entity, + entity: (Entity, MainEntity), instance_index: u32, ) -> Option; } diff --git a/crates/bevy_render/src/batching/no_gpu_preprocessing.rs b/crates/bevy_render/src/batching/no_gpu_preprocessing.rs index 51176cb42b240..63fced58d72d0 100644 --- a/crates/bevy_render/src/batching/no_gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/no_gpu_preprocessing.rs @@ -75,7 +75,7 @@ pub fn batch_and_prepare_sorted_render_phase( for phase in phases.values_mut() { super::batch_and_prepare_sorted_render_phase::(phase, |item| { let (buffer_data, compare_data) = - GBD::get_batch_data(&system_param_item, item.entity())?; + GBD::get_batch_data(&system_param_item, (item.entity(), item.main_entity()))?; let buffer_index = batched_instance_buffer.push(buffer_data); let index = buffer_index.index; @@ -106,8 +106,9 @@ pub fn batch_and_prepare_binned_render_phase( for key in &phase.batchable_mesh_keys { let mut batch_set: SmallVec<[BinnedRenderPhaseBatch; 1]> = smallvec![]; - for &entity in &phase.batchable_mesh_values[key] { - let Some(buffer_data) = GFBD::get_binned_batch_data(&system_param_item, entity) + for &(entity, main_entity) in &phase.batchable_mesh_values[key] { + let Some(buffer_data) = + GFBD::get_binned_batch_data(&system_param_item, (entity, main_entity)) else { continue; }; @@ -124,7 +125,7 @@ pub fn batch_and_prepare_binned_render_phase( == PhaseItemExtraIndex::maybe_dynamic_offset(instance.dynamic_offset) }) { batch_set.push(BinnedRenderPhaseBatch { - representative_entity: entity, + representative_entity: (entity, main_entity), instance_range: instance.index..instance.index, extra_index: PhaseItemExtraIndex::maybe_dynamic_offset( instance.dynamic_offset, diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 58fda6529c891..4c22cf93ff9b3 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -1,3 +1,6 @@ +use super::{ClearColorConfig, Projection}; +use crate::sync_world::TemporaryRenderEntity; +use crate::view::RenderVisibleEntities; use crate::{ batching::gpu_preprocessing::GpuPreprocessingSupport, camera::{CameraProjection, ManualTextureViewHandle, ManualTextureViews}, @@ -40,8 +43,6 @@ use core::ops::Range; use derive_more::derive::From; use wgpu::{BlendState, TextureFormat, TextureUsages}; -use super::{ClearColorConfig, Projection}; - /// Render viewport configuration for the [`Camera`] component. /// /// The viewport defines the area on the render target to which the camera renders its image. @@ -1034,6 +1035,7 @@ pub fn extract_cameras( >, primary_window: Extract>>, gpu_preprocessing_support: Res, + mapper: Extract>, ) { let primary_window = primary_window.iter().next(); for ( @@ -1072,6 +1074,28 @@ pub fn extract_cameras( continue; } + let render_visible_entities = RenderVisibleEntities { + entities: visible_entities + .entities + .iter() + .map(|(type_id, entities)| { + let entities = entities + .iter() + .map(|entity| { + let render_entity = mapper + .get(*entity) + .cloned() + .map(|entity| entity.id()) + .unwrap_or_else(|_e| { + commands.spawn(TemporaryRenderEntity).id() + }); + (render_entity, (*entity).into()) + }) + .collect(); + (*type_id, entities) + }) + .collect(), + }; let mut commands = commands.entity(render_entity.id()); commands.insert(( ExtractedCamera { @@ -1104,7 +1128,7 @@ pub fn extract_cameras( ), color_grading, }, - visible_entities.clone(), + render_visible_entities, *frustum, )); diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_render/src/extract_instances.rs index 24cde74619bb1..b1344c0e78648 100644 --- a/crates/bevy_render/src/extract_instances.rs +++ b/crates/bevy_render/src/extract_instances.rs @@ -9,12 +9,12 @@ use core::marker::PhantomData; use bevy_app::{App, Plugin}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - entity::EntityHashMap, prelude::Entity, query::{QueryFilter, QueryItem, ReadOnlyQueryData}, system::{Query, ResMut, Resource}, }; +use crate::sync_world::MainEntityHashMap; use crate::{prelude::ViewVisibility, Extract, ExtractSchedule, RenderApp}; /// Describes how to extract data needed for rendering from a component or @@ -52,7 +52,7 @@ where /// Stores all extract instances of a type in the render world. #[derive(Resource, Deref, DerefMut)] -pub struct ExtractedInstances(EntityHashMap) +pub struct ExtractedInstances(MainEntityHashMap) where EI: ExtractInstance; @@ -113,7 +113,7 @@ fn extract_all( extracted_instances.clear(); for (entity, other) in &query { if let Some(extract_instance) = EI::extract(other) { - extracted_instances.insert(entity, extract_instance); + extracted_instances.insert(entity.into(), extract_instance); } } } @@ -128,7 +128,7 @@ fn extract_visible( for (entity, view_visibility, other) in &query { if view_visibility.get() { if let Some(extract_instance) = EI::extract(other) { - extracted_instances.insert(entity, extract_instance); + extracted_instances.insert(entity.into(), extract_instance); } } } diff --git a/crates/bevy_render/src/render_phase/mod.rs b/crates/bevy_render/src/render_phase/mod.rs index 2fa3e673640e4..5f05bf1870c53 100644 --- a/crates/bevy_render/src/render_phase/mod.rs +++ b/crates/bevy_render/src/render_phase/mod.rs @@ -37,6 +37,7 @@ use encase::{internal::WriteInto, ShaderSize}; use nonmax::NonMaxU32; pub use rangefinder::*; +use crate::sync_world::MainEntity; use crate::{ batching::{ self, @@ -100,7 +101,7 @@ where /// /// Each bin corresponds to a single batch set. For unbatchable entities, /// prefer `unbatchable_values` instead. - pub(crate) batchable_mesh_values: HashMap>, + pub(crate) batchable_mesh_values: HashMap>, /// A list of `BinKey`s for unbatchable items. /// @@ -120,7 +121,7 @@ where /// entity are simply called in order at rendering time. /// /// See the `custom_phase_item` example for an example of how to use this. - pub non_mesh_items: Vec<(BPI::BinKey, Entity)>, + pub non_mesh_items: Vec<(BPI::BinKey, (Entity, MainEntity))>, /// Information on each batch set. /// @@ -142,8 +143,7 @@ pub struct BinnedRenderPhaseBatch { /// An entity that's *representative* of this batch. /// /// Bevy uses this to fetch the mesh. It can be any entity in the batch. - pub representative_entity: Entity, - + pub representative_entity: (Entity, MainEntity), /// The range of instance indices in this batch. pub instance_range: Range, @@ -157,7 +157,7 @@ pub struct BinnedRenderPhaseBatch { /// Information about the unbatchable entities in a bin. pub(crate) struct UnbatchableBinnedEntities { /// The entities. - pub(crate) entities: Vec, + pub(crate) entities: Vec<(Entity, MainEntity)>, /// The GPU array buffer indices of each unbatchable binned entity. pub(crate) buffer_indices: UnbatchableBinnedEntityIndexSet, @@ -276,7 +276,12 @@ where /// The `phase_type` parameter specifies whether the entity is a /// preprocessable mesh and whether it can be binned with meshes of the same /// type. - pub fn add(&mut self, key: BPI::BinKey, entity: Entity, phase_type: BinnedRenderPhaseType) { + pub fn add( + &mut self, + key: BPI::BinKey, + entity: (Entity, MainEntity), + phase_type: BinnedRenderPhaseType, + ) { match phase_type { BinnedRenderPhaseType::BatchableMesh => { match self.batchable_mesh_values.entry(key.clone()) { @@ -846,6 +851,9 @@ pub trait PhaseItem: Sized + Send + Sync + 'static { /// from the render world . fn entity(&self) -> Entity; + /// The main world entity represented by this `PhaseItem`. + fn main_entity(&self) -> MainEntity; + /// Specifies the [`Draw`] function used to render the item. fn draw_function(&self) -> DrawFunctionId; @@ -1018,7 +1026,7 @@ pub trait BinnedPhaseItem: PhaseItem { /// structures, resulting in significant memory savings. fn new( key: Self::BinKey, - representative_entity: Entity, + representative_entity: (Entity, MainEntity), batch_range: Range, extra_index: PhaseItemExtraIndex, ) -> Self; diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index d32462e56f320..ed6414ece2b24 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -1,5 +1,6 @@ use bevy_app::Plugin; use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHash; use bevy_ecs::{ component::Component, entity::Entity, @@ -10,6 +11,7 @@ use bevy_ecs::{ world::{Mut, OnAdd, OnRemove, World}, }; use bevy_reflect::Reflect; +use bevy_utils::hashbrown; /// A plugin that synchronizes entities with [`SyncToRenderWorld`] between the main world and the render world. /// @@ -128,10 +130,16 @@ impl RenderEntity { } } +impl From for RenderEntity { + fn from(entity: Entity) -> Self { + RenderEntity(entity) + } +} + /// Component added on the render world entities to keep track of the corresponding main world entity. /// /// Can also be used as a newtype wrapper for main world entities. -#[derive(Component, Deref, Clone, Debug)] +#[derive(Component, Deref, Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct MainEntity(Entity); impl MainEntity { #[inline] @@ -140,6 +148,18 @@ impl MainEntity { } } +impl From for MainEntity { + fn from(entity: Entity) -> Self { + MainEntity(entity) + } +} + +/// A [`HashMap`](hashbrown::HashMap) pre-configured to use [`EntityHash`] hashing with a [`MainEntity`]. +pub type MainEntityHashMap = hashbrown::HashMap; + +/// A [`HashSet`](hashbrown::HashSet) pre-configured to use [`EntityHash`] hashing with a [`MainEntity`].. +pub type MainEntityHashSet = hashbrown::HashSet; + /// Marker component that indicates that its entity needs to be despawned at the end of the frame. #[derive(Component, Clone, Debug, Default, Reflect)] #[component(storage = "SparseSet")] diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 011686a9a4148..e2e439ec79699 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -17,14 +17,14 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_transform::{components::GlobalTransform, TransformSystem}; use bevy_utils::{Parallel, TypeIdMap}; +use super::NoCpuCulling; +use crate::sync_world::MainEntity; use crate::{ camera::{Camera, CameraProjection}, mesh::{Mesh, Mesh3d, MeshAabb}, primitives::{Aabb, Frustum, Sphere}, }; -use super::NoCpuCulling; - /// User indication of whether an entity is visible. Propagates down the entity hierarchy. /// /// If an entity is hidden in this way, all [`Children`] (and all of their children and so on) who @@ -205,8 +205,8 @@ pub struct NoFrustumCulling; /// /// This component contains all entities which are visible from the currently /// rendered view. The collection is updated automatically by the [`VisibilitySystems::CheckVisibility`] -/// system set, and renderers can use it to optimize rendering of a particular view, to -/// prevent drawing items not visible from that view. +/// system set. Renderers can use the equivalent [`RenderVisibleEntities`] to optimize rendering of +/// a particular view, to prevent drawing items not visible from that view. /// /// This component is intended to be attached to the same entity as the [`Camera`] and /// the [`Frustum`] defining the view. @@ -271,6 +271,49 @@ impl VisibleEntities { } } +/// Collection of entities visible from the current view. +/// +/// This component is extracted from [`VisibleEntities`]. +#[derive(Clone, Component, Default, Debug, Reflect)] +#[reflect(Component, Default, Debug)] +pub struct RenderVisibleEntities { + #[reflect(ignore)] + pub entities: TypeIdMap>, +} + +impl RenderVisibleEntities { + pub fn get(&self) -> &[(Entity, MainEntity)] + where + QF: 'static, + { + match self.entities.get(&TypeId::of::()) { + Some(entities) => &entities[..], + None => &[], + } + } + + pub fn iter(&self) -> impl DoubleEndedIterator + where + QF: 'static, + { + self.get::().iter() + } + + pub fn len(&self) -> usize + where + QF: 'static, + { + self.get::().len() + } + + pub fn is_empty(&self) -> bool + where + QF: 'static, + { + self.get::().is_empty() + } +} + #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] pub enum VisibilitySystems { /// Label for the [`calculate_bounds`], `calculate_bounds_2d` and `calculate_bounds_text2d` systems, diff --git a/crates/bevy_render/src/view/visibility/range.rs b/crates/bevy_render/src/view/visibility/range.rs index 74e089ed9ffc1..c476c9e9c640e 100644 --- a/crates/bevy_render/src/view/visibility/range.rs +++ b/crates/bevy_render/src/view/visibility/range.rs @@ -22,6 +22,8 @@ use bevy_utils::{prelude::default, HashMap}; use nonmax::NonMaxU16; use wgpu::{BufferBindingType, BufferUsages}; +use super::{check_visibility, VisibilitySystems}; +use crate::sync_world::{MainEntity, MainEntityHashMap}; use crate::{ camera::Camera, mesh::Mesh3d, @@ -31,8 +33,6 @@ use crate::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use super::{check_visibility, VisibilitySystems}; - /// We need at least 4 storage buffer bindings available to enable the /// visibility range buffer. /// @@ -195,7 +195,7 @@ impl VisibilityRange { #[derive(Resource)] pub struct RenderVisibilityRanges { /// Information corresponding to each entity. - entities: EntityHashMap, + entities: MainEntityHashMap, /// Maps a [`VisibilityRange`] to its index within the `buffer`. /// @@ -246,7 +246,7 @@ impl RenderVisibilityRanges { } /// Inserts a new entity into the [`RenderVisibilityRanges`]. - fn insert(&mut self, entity: Entity, visibility_range: &VisibilityRange) { + fn insert(&mut self, entity: MainEntity, visibility_range: &VisibilityRange) { // Grab a slot in the GPU buffer, or take the existing one if there // already is one. let buffer_index = *self @@ -276,14 +276,14 @@ impl RenderVisibilityRanges { /// /// If the entity has no visible range, returns `None`. #[inline] - pub fn lod_index_for_entity(&self, entity: Entity) -> Option { + pub fn lod_index_for_entity(&self, entity: MainEntity) -> Option { self.entities.get(&entity).map(|info| info.buffer_index) } /// Returns true if the entity has a visibility range and it isn't abrupt: /// i.e. if it has a crossfade. #[inline] - pub fn entity_has_crossfading_visibility_ranges(&self, entity: Entity) -> bool { + pub fn entity_has_crossfading_visibility_ranges(&self, entity: MainEntity) -> bool { self.entities .get(&entity) .is_some_and(|info| !info.is_abrupt) @@ -423,7 +423,7 @@ pub fn extract_visibility_ranges( render_visibility_ranges.clear(); for (entity, visibility_range) in visibility_ranges_query.iter() { - render_visibility_ranges.insert(entity, visibility_range); + render_visibility_ranges.insert(entity.into(), visibility_range); } } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 847a3104fcf90..2d67164a0399c 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -1,5 +1,9 @@ #![expect(deprecated)] +use crate::{ + DrawMesh2d, Mesh2d, Mesh2dPipeline, Mesh2dPipelineKey, RenderMesh2dInstances, + SetMesh2dBindGroup, SetMesh2dViewBindGroup, +}; use bevy_app::{App, Plugin}; use bevy_asset::{Asset, AssetApp, AssetId, AssetServer, Handle}; use bevy_core_pipeline::{ @@ -8,12 +12,13 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - entity::EntityHashMap, prelude::*, system::{lifetimeless::SRes, SystemParamItem}, }; use bevy_math::FloatOrd; use bevy_reflect::{prelude::ReflectDefault, Reflect}; +use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; +use bevy_render::view::RenderVisibleEntities; use bevy_render::{ mesh::{MeshVertexBufferLayoutRef, RenderMesh}, render_asset::{ @@ -30,7 +35,7 @@ use bevy_render::{ SpecializedMeshPipeline, SpecializedMeshPipelineError, SpecializedMeshPipelines, }, renderer::RenderDevice, - view::{ExtractedView, InheritedVisibility, Msaa, ViewVisibility, Visibility, VisibleEntities}, + view::{ExtractedView, InheritedVisibility, Msaa, ViewVisibility, Visibility}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -38,11 +43,6 @@ use bevy_utils::tracing::error; use core::{hash::Hash, marker::PhantomData}; use derive_more::derive::From; -use crate::{ - DrawMesh2d, Mesh2d, Mesh2dPipeline, Mesh2dPipelineKey, RenderMesh2dInstances, - SetMesh2dBindGroup, SetMesh2dViewBindGroup, -}; - use super::ColorMaterial; /// Materials are used alongside [`Material2dPlugin`], [`Mesh2d`], and [`MeshMaterial2d`] @@ -319,7 +319,7 @@ where } #[derive(Resource, Deref, DerefMut)] -pub struct RenderMaterial2dInstances(EntityHashMap>); +pub struct RenderMaterial2dInstances(MainEntityHashMap>); impl Default for RenderMaterial2dInstances { fn default() -> Self { @@ -339,7 +339,7 @@ fn extract_mesh_materials_2d( ) { for (entity, view_visibility, material) in &query { if view_visibility.get() { - material_instances.insert(entity, material.id()); + material_instances.insert(entity.into(), material.id()); } } } @@ -353,7 +353,7 @@ pub(crate) fn extract_default_materials_2d( for (entity, view_visibility) in &query { if view_visibility.get() { - material_instances.insert(entity, default_material); + material_instances.insert(entity.into(), default_material); } } } @@ -501,7 +501,8 @@ impl RenderCommand

) -> RenderCommandResult { let materials = materials.into_inner(); let material_instances = material_instances.into_inner(); - let Some(material_instance) = material_instances.get(&item.entity()) else { + let Some(material_instance) = material_instances.get(&MainEntity::from(item.entity())) + else { return RenderCommandResult::Skip; }; let Some(material2d) = materials.get(*material_instance) else { @@ -553,7 +554,7 @@ pub fn queue_material2d_meshes( views: Query<( Entity, &ExtractedView, - &VisibleEntities, + &RenderVisibleEntities, &Msaa, Option<&Tonemapping>, Option<&DebandDither>, @@ -592,7 +593,7 @@ pub fn queue_material2d_meshes( view_key |= Mesh2dPipelineKey::DEBAND_DITHER; } } - for visible_entity in visible_entities.iter::>() { + for (render_entity, visible_entity) in visible_entities.iter::>() { let Some(material_asset_id) = render_material_instances.get(visible_entity) else { continue; }; @@ -640,7 +641,7 @@ pub fn queue_material2d_meshes( }; opaque_phase.add( bin_key, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.automatic_batching), ); } @@ -653,13 +654,13 @@ pub fn queue_material2d_meshes( }; alpha_mask_phase.add( bin_key, - *visible_entity, + (*render_entity, *visible_entity), BinnedRenderPhaseType::mesh(mesh_instance.automatic_batching), ); } AlphaMode2d::Blend => { transparent_phase.add(Transparent2d { - entity: *visible_entity, + entity: (*render_entity, *visible_entity), draw_function: draw_transparent_2d, pipeline: pipeline_id, // NOTE: Back-to-front ordering for transparent with ascending sort means far should have the diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index b0b5f39bc9af8..fd65b19d4a02f 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -1,6 +1,7 @@ use bevy_app::Plugin; use bevy_asset::{load_internal_asset, AssetId, Handle}; +use crate::Material2dBindGroupId; use bevy_core_pipeline::{ core_2d::{AlphaMask2d, Camera2d, Opaque2d, Transparent2d, CORE_2D_DEPTH_FORMAT}, tonemapping::{ @@ -9,12 +10,12 @@ use bevy_core_pipeline::{ }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ - entity::EntityHashMap, prelude::*, query::ROQueryItem, system::{lifetimeless::*, SystemParamItem, SystemState}, }; use bevy_math::{Affine3, Vec4}; +use bevy_render::sync_world::{MainEntity, MainEntityHashMap}; use bevy_render::{ batching::{ gpu_preprocessing::IndirectParameters, @@ -46,8 +47,6 @@ use bevy_transform::components::GlobalTransform; use bevy_utils::tracing::error; use nonmax::NonMaxU32; -use crate::Material2dBindGroupId; - #[derive(Default)] pub struct Mesh2dRenderPlugin; @@ -202,7 +201,7 @@ pub struct RenderMesh2dInstance { } #[derive(Default, Resource, Deref, DerefMut)] -pub struct RenderMesh2dInstances(EntityHashMap); +pub struct RenderMesh2dInstances(MainEntityHashMap); #[derive(Component)] pub struct Mesh2dMarker; @@ -226,7 +225,7 @@ pub fn extract_mesh2d( continue; } render_mesh_instances.insert( - entity, + entity.into(), RenderMesh2dInstance { transforms: Mesh2dTransforms { world_from_local: (&transform.affine()).into(), @@ -358,9 +357,9 @@ impl GetBatchData for Mesh2dPipeline { fn get_batch_data( (mesh_instances, _, _): &SystemParamItem, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), ) -> Option<(Self::BufferData, Option)> { - let mesh_instance = mesh_instances.get(&entity)?; + let mesh_instance = mesh_instances.get(&main_entity)?; Some(( (&mesh_instance.transforms).into(), mesh_instance.automatic_batching.then_some(( @@ -376,15 +375,15 @@ impl GetFullBatchData for Mesh2dPipeline { fn get_binned_batch_data( (mesh_instances, _, _): &SystemParamItem, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), ) -> Option { - let mesh_instance = mesh_instances.get(&entity)?; + let mesh_instance = mesh_instances.get(&main_entity)?; Some((&mesh_instance.transforms).into()) } fn get_index_and_compare_data( _: &SystemParamItem, - _query_item: Entity, + _query_item: (Entity, MainEntity), ) -> Option<(NonMaxU32, Option)> { error!( "`get_index_and_compare_data` is only intended for GPU mesh uniform building, \ @@ -395,7 +394,7 @@ impl GetFullBatchData for Mesh2dPipeline { fn get_binned_index( _: &SystemParamItem, - _query_item: Entity, + _query_item: (Entity, MainEntity), ) -> Option { error!( "`get_binned_index` is only intended for GPU mesh uniform building, \ @@ -407,10 +406,10 @@ impl GetFullBatchData for Mesh2dPipeline { fn get_batch_indirect_parameters_index( (mesh_instances, meshes, mesh_allocator): &SystemParamItem, indirect_parameters_buffer: &mut bevy_render::batching::gpu_preprocessing::IndirectParametersBuffer, - entity: Entity, + (_entity, main_entity): (Entity, MainEntity), instance_index: u32, ) -> Option { - let mesh_instance = mesh_instances.get(&entity)?; + let mesh_instance = mesh_instances.get(&main_entity)?; let mesh = meshes.get(mesh_instance.mesh_asset_id)?; let vertex_buffer_slice = mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id)?; @@ -817,7 +816,7 @@ impl RenderCommand

for DrawMesh2d { let mesh_allocator = mesh_allocator.into_inner(); let Some(RenderMesh2dInstance { mesh_asset_id, .. }) = - render_mesh2d_instances.get(&item.entity()) + render_mesh2d_instances.get(&item.main_entity()) else { return RenderCommandResult::Skip; }; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index c3e9ba9b5b96c..300a16ce637c6 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -14,12 +14,13 @@ use bevy_core_pipeline::{ }, }; use bevy_ecs::{ - entity::EntityHashMap, prelude::*, query::ROQueryItem, system::{lifetimeless::*, SystemParamItem, SystemState}, }; use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4}; +use bevy_render::sync_world::MainEntity; +use bevy_render::view::RenderVisibleEntities; use bevy_render::{ render_asset::RenderAssets, render_phase::{ @@ -38,7 +39,7 @@ use bevy_render::{ }, view::{ ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, - ViewVisibility, VisibleEntities, + ViewVisibility, }, Extract, }; @@ -346,7 +347,7 @@ pub struct ExtractedSprite { #[derive(Resource, Default)] pub struct ExtractedSprites { - pub sprites: EntityHashMap, + pub sprites: HashMap<(Entity, MainEntity), ExtractedSprite>, } #[derive(Resource, Default)] @@ -392,7 +393,15 @@ pub fn extract_sprites( extracted_sprites.sprites.extend( slices .extract_sprites(transform, original_entity, sprite) - .map(|e| (commands.spawn(TemporaryRenderEntity).id(), e)), + .map(|e| { + ( + ( + commands.spawn(TemporaryRenderEntity).id(), + original_entity.into(), + ), + e, + ) + }), ); } else { let atlas_rect = sprite @@ -413,7 +422,7 @@ pub fn extract_sprites( // PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive extracted_sprites.sprites.insert( - entity.id(), + (entity.id(), original_entity.into()), ExtractedSprite { color: sprite.color.into(), transform: *transform, @@ -498,7 +507,7 @@ pub fn queue_sprites( mut transparent_render_phases: ResMut>, mut views: Query<( Entity, - &VisibleEntities, + &RenderVisibleEntities, &ExtractedView, &Msaa, Option<&Tonemapping>, @@ -544,14 +553,14 @@ pub fn queue_sprites( view_entities.extend( visible_entities .iter::() - .map(|e| e.index() as usize), + .map(|(_, e)| e.index() as usize), ); transparent_phase .items .reserve(extracted_sprites.sprites.len()); - for (entity, extracted_sprite) in extracted_sprites.sprites.iter() { + for ((entity, main_entity), extracted_sprite) in extracted_sprites.sprites.iter() { let index = extracted_sprite.original_entity.unwrap_or(*entity).index(); if !view_entities.contains(index as usize) { @@ -565,7 +574,7 @@ pub fn queue_sprites( transparent_phase.add(Transparent2d { draw_function: draw_sprite_function, pipeline, - entity: *entity, + entity: (*entity, *main_entity), sort_key, // batch_range and dynamic_offset will be calculated in prepare_sprites batch_range: 0..0, @@ -739,7 +748,7 @@ pub fn prepare_sprite_image_bind_groups( batch_item_index = item_index; batches.push(( - item.entity, + item.entity(), SpriteBatch { image_handle_id: batch_image_handle, range: index..index, diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 405783bc2153b..03fa387e05a34 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -193,7 +193,10 @@ pub fn extract_text2d_sprite( let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); extracted_sprites.sprites.insert( - commands.spawn(TemporaryRenderEntity).id(), + ( + commands.spawn(TemporaryRenderEntity).id(), + original_entity.into(), + ), ExtractedSprite { transform: transform * GlobalTransform::from_translation(position.extend(0.)), color, diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index 8f56581ce6f9d..b874a93919e64 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -1,5 +1,9 @@ use core::{hash::Hash, ops::Range}; +use crate::{ + BoxShadow, CalculatedClip, DefaultUiCamera, Node, RenderUiSystem, ResolvedBorderRadius, + TargetCamera, TransparentUi, UiBoxShadowSamples, UiScale, Val, +}; use bevy_app::prelude::*; use bevy_asset::*; use bevy_color::{Alpha, ColorToComponents, LinearRgba}; @@ -13,6 +17,7 @@ use bevy_ecs::{ }, }; use bevy_math::{vec2, FloatOrd, Mat4, Rect, Vec2, Vec3Swizzles, Vec4Swizzles}; +use bevy_render::sync_world::MainEntity; use bevy_render::RenderApp; use bevy_render::{ camera::Camera, @@ -27,11 +32,6 @@ use bevy_render::{ use bevy_transform::prelude::GlobalTransform; use bytemuck::{Pod, Zeroable}; -use crate::{ - BoxShadow, CalculatedClip, DefaultUiCamera, Node, RenderUiSystem, ResolvedBorderRadius, - TargetCamera, TransparentUi, UiBoxShadowSamples, UiScale, Val, -}; - use super::{QUAD_INDICES, QUAD_VERTEX_POSITIONS}; pub const BOX_SHADOW_SHADER_HANDLE: Handle = Handle::weak_from_u128(17717747047134343426); @@ -221,6 +221,7 @@ pub struct ExtractedBoxShadow { pub radius: ResolvedBorderRadius, pub blur_radius: f32, pub size: Vec2, + pub main_entity: MainEntity, } /// List of extracted shadows to be sorted and queued for rendering @@ -237,6 +238,7 @@ pub fn extract_shadows( camera_query: Extract>, box_shadow_query: Extract< Query<( + Entity, &Node, &GlobalTransform, &ViewVisibility, @@ -247,7 +249,8 @@ pub fn extract_shadows( >, mapping: Extract>, ) { - for (uinode, transform, view_visibility, box_shadow, clip, camera) in &box_shadow_query { + for (entity, uinode, transform, view_visibility, box_shadow, clip, camera) in &box_shadow_query + { let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) else { continue; @@ -322,6 +325,7 @@ pub fn extract_shadows( radius, blur_radius, size: shadow_size, + main_entity: entity.into(), }, ); } @@ -359,7 +363,7 @@ pub fn queue_shadows( transparent_phase.add(TransparentUi { draw_function, pipeline, - entity: *entity, + entity: (*entity, extracted_shadow.main_entity), sort_key: ( FloatOrd(extracted_shadow.stack_index as f32 - 0.1), entity.index(), @@ -402,7 +406,7 @@ pub fn prepare_shadows( while item_index < ui_phase.items.len() { let item = &mut ui_phase.items[item_index]; - if let Some(box_shadow) = extracted_shadows.box_shadows.get(item.entity) { + if let Some(box_shadow) = extracted_shadows.box_shadows.get(item.entity()) { let uinode_rect = box_shadow.rect; let rect_size = uinode_rect.size().extend(1.0); @@ -485,7 +489,7 @@ pub fn prepare_shadows( } batches.push(( - item.entity, + item.entity(), UiShadowsBatch { range: vertices_index..vertices_index + 6, camera: box_shadow.camera_entity, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 87fce7413c9dc..645356b5d6a0f 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -18,6 +18,7 @@ use bevy_ecs::entity::{EntityHashMap, EntityHashSet}; use bevy_ecs::prelude::*; use bevy_math::{FloatOrd, Mat4, Rect, URect, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4Swizzles}; use bevy_render::render_phase::ViewSortedRenderPhases; +use bevy_render::sync_world::MainEntity; use bevy_render::texture::TRANSPARENT_IMAGE_HANDLE; use bevy_render::{ camera::Camera, @@ -167,6 +168,7 @@ pub struct ExtractedUiNode { // Nodes with ambiguous camera will be ignored. pub camera_entity: Entity, pub item: ExtractedUiItem, + pub main_entity: MainEntity, } /// The type of UI node. @@ -224,6 +226,7 @@ pub fn extract_uinode_background_colors( default_ui_camera: Extract, uinode_query: Extract< Query<( + Entity, &Node, &GlobalTransform, &ViewVisibility, @@ -234,7 +237,9 @@ pub fn extract_uinode_background_colors( >, mapping: Extract>, ) { - for (uinode, transform, view_visibility, clip, camera, background_color) in &uinode_query { + for (entity, uinode, transform, view_visibility, clip, camera, background_color) in + &uinode_query + { let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) else { continue; @@ -270,6 +275,7 @@ pub fn extract_uinode_background_colors( border_radius: uinode.border_radius(), node_type: NodeType::Rect, }, + main_entity: entity.into(), }, ); } @@ -284,6 +290,7 @@ pub fn extract_uinode_images( uinode_query: Extract< Query< ( + Entity, &Node, &GlobalTransform, &ViewVisibility, @@ -297,7 +304,7 @@ pub fn extract_uinode_images( >, mapping: Extract>, ) { - for (uinode, transform, view_visibility, clip, camera, image, atlas) in &uinode_query { + for (entity, uinode, transform, view_visibility, clip, camera, image, atlas) in &uinode_query { let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) else { continue; @@ -360,6 +367,7 @@ pub fn extract_uinode_images( border_radius: uinode.border_radius, node_type: NodeType::Rect, }, + main_entity: entity.into(), }, ); } @@ -371,6 +379,7 @@ pub fn extract_uinode_borders( default_ui_camera: Extract, uinode_query: Extract< Query<( + Entity, &Node, &GlobalTransform, &ViewVisibility, @@ -384,6 +393,7 @@ pub fn extract_uinode_borders( let image = AssetId::::default(); for ( + entity, uinode, global_transform, view_visibility, @@ -435,6 +445,7 @@ pub fn extract_uinode_borders( border_radius: uinode.border_radius(), node_type: NodeType::Border, }, + main_entity: entity.into(), }, ); } @@ -463,6 +474,7 @@ pub fn extract_uinode_borders( border_radius: uinode.outline_radius(), node_type: NodeType::Border, }, + main_entity: entity.into(), }, ); } @@ -584,6 +596,7 @@ pub fn extract_text_sections( ui_scale: Extract>, uinode_query: Extract< Query<( + Entity, &Node, &GlobalTransform, &ViewVisibility, @@ -601,6 +614,7 @@ pub fn extract_text_sections( let default_ui_camera = default_ui_camera.get(); for ( + entity, uinode, global_transform, view_visibility, @@ -706,6 +720,7 @@ pub fn extract_text_sections( atlas_scaling: Vec2::splat(inverse_scale_factor), range: start..end, }, + main_entity: entity.into(), }, ); start = end; @@ -809,7 +824,7 @@ pub fn queue_uinodes( transparent_phase.add(TransparentUi { draw_function, pipeline, - entity: *entity, + entity: (*entity, extracted_uinode.main_entity), sort_key: ( FloatOrd(extracted_uinode.stack_index as f32), entity.index(), @@ -875,7 +890,7 @@ pub fn prepare_uinodes( for item_index in 0..ui_phase.items.len() { let item = &mut ui_phase.items[item_index]; - if let Some(extracted_uinode) = extracted_uinodes.uinodes.get(&item.entity) { + if let Some(extracted_uinode) = extracted_uinodes.uinodes.get(&item.entity()) { let mut existing_batch = batches.last_mut(); if batch_image_handle == AssetId::invalid() @@ -896,7 +911,7 @@ pub fn prepare_uinodes( camera: extracted_uinode.camera_entity, }; - batches.push((item.entity, new_batch)); + batches.push((item.entity(), new_batch)); image_bind_groups .values diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 08ee870668fcf..6c035c3838efc 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -7,6 +7,7 @@ use bevy_ecs::{ system::{lifetimeless::*, SystemParamItem}, }; use bevy_math::FloatOrd; +use bevy_render::sync_world::MainEntity; use bevy_render::{ camera::ExtractedCamera, render_graph::*, @@ -91,7 +92,7 @@ impl Node for UiPassNode { pub struct TransparentUi { pub sort_key: (FloatOrd, u32), - pub entity: Entity, + pub entity: (Entity, MainEntity), pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, pub batch_range: Range, @@ -101,7 +102,11 @@ pub struct TransparentUi { impl PhaseItem for TransparentUi { #[inline] fn entity(&self) -> Entity { - self.entity + self.entity.0 + } + + fn main_entity(&self) -> MainEntity { + self.entity.1 } #[inline] diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index 91c05bc6528f2..990f48555c912 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -1,5 +1,6 @@ use core::{hash::Hash, marker::PhantomData, ops::Range}; +use crate::*; use bevy_asset::*; use bevy_ecs::{ prelude::Component, @@ -11,6 +12,7 @@ use bevy_ecs::{ }, }; use bevy_math::{FloatOrd, Mat4, Rect, Vec2, Vec4Swizzles}; +use bevy_render::sync_world::MainEntity; use bevy_render::{ extract_component::ExtractComponentPlugin, globals::{GlobalsBuffer, GlobalsUniform}, @@ -26,8 +28,6 @@ use bevy_render::{ use bevy_transform::prelude::GlobalTransform; use bytemuck::{Pod, Zeroable}; -use crate::*; - pub const UI_MATERIAL_SHADER_HANDLE: Handle = Handle::weak_from_u128(10074188772096983955); const UI_VERTEX_OUTPUT_SHADER_HANDLE: Handle = Handle::weak_from_u128(10123618247720234751); @@ -341,6 +341,7 @@ pub struct ExtractedUiMaterialNode { // it is defaulted to a single camera if only one exists. // Nodes with ambiguous camera will be ignored. pub camera_entity: Entity, + pub main_entity: MainEntity, } #[derive(Resource)] @@ -364,6 +365,7 @@ pub fn extract_ui_material_nodes( uinode_query: Extract< Query< ( + Entity, &Node, &GlobalTransform, &UiMaterialHandle, @@ -379,7 +381,7 @@ pub fn extract_ui_material_nodes( // If there is only one camera, we use it as default let default_single_camera = default_ui_camera.get(); - for (uinode, transform, handle, view_visibility, clip, camera) in uinode_query.iter() { + for (entity, uinode, transform, handle, view_visibility, clip, camera) in uinode_query.iter() { let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_single_camera) else { continue; }; @@ -418,6 +420,7 @@ pub fn extract_ui_material_nodes( border, clip: clip.map(|clip| clip.clip), camera_entity: camera_entity.id(), + main_entity: entity.into(), }, ); } @@ -456,7 +459,7 @@ pub fn prepare_uimaterial_nodes( for item_index in 0..ui_phase.items.len() { let item = &mut ui_phase.items[item_index]; - if let Some(extracted_uinode) = extracted_uinodes.uinodes.get(item.entity) { + if let Some(extracted_uinode) = extracted_uinodes.uinodes.get(item.entity()) { let mut existing_batch = batches .last_mut() .filter(|_| batch_shader_handle == extracted_uinode.material); @@ -470,7 +473,7 @@ pub fn prepare_uimaterial_nodes( material: extracted_uinode.material, }; - batches.push((item.entity, new_batch)); + batches.push((item.entity(), new_batch)); existing_batch = batches.last_mut(); } @@ -645,7 +648,7 @@ pub fn queue_ui_material_nodes( transparent_phase.add(TransparentUi { draw_function, pipeline, - entity: *entity, + entity: (*entity, extracted_uinode.main_entity), sort_key: ( FloatOrd(extracted_uinode.stack_index as f32), entity.index(), diff --git a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs index f33f50b1717f3..b33d2707d4522 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -1,5 +1,6 @@ use core::{hash::Hash, ops::Range}; +use crate::*; use bevy_asset::*; use bevy_color::{Alpha, ColorToComponents, LinearRgba}; use bevy_ecs::{ @@ -11,6 +12,7 @@ use bevy_ecs::{ }, }; use bevy_math::{FloatOrd, Mat4, Rect, Vec2, Vec4Swizzles}; +use bevy_render::sync_world::MainEntity; use bevy_render::{ render_asset::RenderAssets, render_phase::*, @@ -30,8 +32,6 @@ use bevy_utils::HashMap; use binding_types::{sampler, texture_2d}; use bytemuck::{Pod, Zeroable}; -use crate::*; - pub const UI_SLICER_SHADER_HANDLE: Handle = Handle::weak_from_u128(11156288772117983964); pub struct UiTextureSlicerPlugin; @@ -235,6 +235,7 @@ pub struct ExtractedUiTextureSlice { pub image_scale_mode: ImageScaleMode, pub flip_x: bool, pub flip_y: bool, + pub main_entity: MainEntity, } #[derive(Resource, Default)] @@ -249,6 +250,7 @@ pub fn extract_ui_texture_slices( texture_atlases: Extract>>, slicers_query: Extract< Query<( + Entity, &Node, &GlobalTransform, &ViewVisibility, @@ -261,8 +263,17 @@ pub fn extract_ui_texture_slices( >, mapping: Extract>, ) { - for (uinode, transform, view_visibility, clip, camera, image, image_scale_mode, atlas) in - &slicers_query + for ( + entity, + uinode, + transform, + view_visibility, + clip, + camera, + image, + image_scale_mode, + atlas, + ) in &slicers_query { let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) else { @@ -313,6 +324,7 @@ pub fn extract_ui_texture_slices( atlas_rect, flip_x: image.flip_x, flip_y: image.flip_y, + main_entity: entity.into(), }, ); } @@ -346,7 +358,7 @@ pub fn queue_ui_slices( transparent_phase.add(TransparentUi { draw_function, pipeline, - entity: *entity, + entity: (*entity, extracted_slicer.main_entity), sort_key: ( FloatOrd(extracted_slicer.stack_index as f32), entity.index(), @@ -407,7 +419,7 @@ pub fn prepare_ui_slices( for item_index in 0..ui_phase.items.len() { let item = &mut ui_phase.items[item_index]; - if let Some(texture_slices) = extracted_slices.slices.get(item.entity) { + if let Some(texture_slices) = extracted_slices.slices.get(item.entity()) { let mut existing_batch = batches.last_mut(); if batch_image_handle == AssetId::invalid() @@ -429,7 +441,7 @@ pub fn prepare_ui_slices( camera: texture_slices.camera_entity, }; - batches.push((item.entity, new_batch)); + batches.push((item.entity(), new_batch)); image_bind_groups .values diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index e3b471610b8b1..75c4c9b37b3d5 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -8,7 +8,6 @@ use bevy::{ color::palettes::basic::YELLOW, core_pipeline::core_2d::{Transparent2d, CORE_2D_DEPTH_FORMAT}, - ecs::entity::EntityHashMap, math::{ops, FloatOrd}, prelude::*, render::{ @@ -25,8 +24,9 @@ use bevy::{ SpecializedRenderPipeline, SpecializedRenderPipelines, StencilFaceState, StencilState, TextureFormat, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, }, + sync_world::MainEntityHashMap, texture::BevyDefault, - view::{ExtractedView, ViewTarget, VisibleEntities}, + view::{ExtractedView, RenderVisibleEntities, ViewTarget}, Extract, Render, RenderApp, RenderSet, }, sprite::{ @@ -290,7 +290,7 @@ pub const COLORED_MESH2D_SHADER_HANDLE: Handle = /// Our custom pipeline needs its own instance storage #[derive(Resource, Deref, DerefMut, Default)] -pub struct RenderColoredMesh2dInstances(EntityHashMap); +pub struct RenderColoredMesh2dInstances(MainEntityHashMap); impl Plugin for ColoredMesh2dPlugin { fn build(&self, app: &mut App) { @@ -346,7 +346,7 @@ pub fn extract_colored_mesh2d( values.push((entity, ColoredMesh2d)); render_mesh_instances.insert( - entity, + entity.into(), RenderMesh2dInstance { mesh_asset_id: handle.0.id(), transforms, @@ -369,7 +369,7 @@ pub fn queue_colored_mesh2d( render_meshes: Res>, render_mesh_instances: Res, mut transparent_render_phases: ResMut>, - views: Query<(Entity, &VisibleEntities, &ExtractedView, &Msaa)>, + views: Query<(Entity, &RenderVisibleEntities, &ExtractedView, &Msaa)>, ) { if render_mesh_instances.is_empty() { return; @@ -386,7 +386,7 @@ pub fn queue_colored_mesh2d( | Mesh2dPipelineKey::from_hdr(view.hdr); // Queue all entities visible to that view - for visible_entity in visible_entities.iter::>() { + for (render_entity, visible_entity) in visible_entities.iter::>() { if let Some(mesh_instance) = render_mesh_instances.get(visible_entity) { let mesh2d_handle = mesh_instance.mesh_asset_id; let mesh2d_transforms = &mesh_instance.transforms; @@ -402,7 +402,7 @@ pub fn queue_colored_mesh2d( let mesh_z = mesh2d_transforms.world_from_local.translation.z; transparent_phase.add(Transparent2d { - entity: *visible_entity, + entity: (*render_entity, *visible_entity), draw_function: draw_colored_mesh2d, pipeline: pipeline_id, // The 2d render items are sorted according to their z value before rendering, diff --git a/examples/shader/custom_phase_item.rs b/examples/shader/custom_phase_item.rs index bb32a62c4eb91..5dbff53f2c727 100644 --- a/examples/shader/custom_phase_item.rs +++ b/examples/shader/custom_phase_item.rs @@ -31,7 +31,7 @@ use bevy::{ }, renderer::{RenderDevice, RenderQueue}, texture::BevyDefault as _, - view::{self, ExtractedView, VisibilitySystems, VisibleEntities}, + view::{self, ExtractedView, RenderVisibleEntities, VisibilitySystems}, Render, RenderApp, RenderSet, }, }; @@ -234,7 +234,7 @@ fn queue_custom_phase_item( mut opaque_render_phases: ResMut>, opaque_draw_functions: Res>, mut specialized_render_pipelines: ResMut>, - views: Query<(Entity, &VisibleEntities, &Msaa), With>, + views: Query<(Entity, &RenderVisibleEntities, &Msaa), With>, ) { let draw_custom_phase_item = opaque_draw_functions .read() diff --git a/examples/shader/custom_shader_instancing.rs b/examples/shader/custom_shader_instancing.rs index 839d0ea2a13d7..ac0bbf2d6a602 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -29,6 +29,7 @@ use bevy::{ }, render_resource::*, renderer::RenderDevice, + sync_world::MainEntity, view::{ExtractedView, NoFrustumCulling}, Render, RenderApp, RenderSet, }, @@ -127,7 +128,7 @@ fn queue_custom( pipeline_cache: Res, meshes: Res>, render_mesh_instances: Res, - material_meshes: Query>, + material_meshes: Query<(Entity, &MainEntity), With>, mut transparent_render_phases: ResMut>, views: Query<(Entity, &ExtractedView, &Msaa)>, ) { @@ -142,8 +143,9 @@ fn queue_custom( let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr); let rangefinder = view.rangefinder3d(); - for entity in &material_meshes { - let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity) else { + for (entity, main_entity) in &material_meshes { + let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(*main_entity) + else { continue; }; let Some(mesh) = meshes.get(mesh_instance.mesh_asset_id) else { @@ -155,7 +157,7 @@ fn queue_custom( .specialize(&pipeline_cache, &custom_pipeline, key, &mesh.layout) .unwrap(); transparent_phase.add(Transparent3d { - entity, + entity: (entity, *main_entity), pipeline, draw_function: draw_custom, distance: rangefinder.distance_translation(&mesh_instance.translation), @@ -268,7 +270,7 @@ impl RenderCommand

for DrawMeshInstanced { // A borrow check workaround. let mesh_allocator = mesh_allocator.into_inner(); - let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity()) + let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.main_entity()) else { return RenderCommandResult::Skip; }; diff --git a/examples/shader/specialized_mesh_pipeline.rs b/examples/shader/specialized_mesh_pipeline.rs index cc8cae101d853..291c61bae9a67 100644 --- a/examples/shader/specialized_mesh_pipeline.rs +++ b/examples/shader/specialized_mesh_pipeline.rs @@ -29,7 +29,7 @@ use bevy::{ SpecializedMeshPipelines, TextureFormat, VertexState, }, texture::BevyDefault as _, - view::{self, ExtractedView, ViewTarget, VisibilitySystems, VisibleEntities}, + view::{self, ExtractedView, RenderVisibleEntities, ViewTarget, VisibilitySystems}, Render, RenderApp, RenderSet, }, }; @@ -280,7 +280,7 @@ fn queue_custom_mesh_pipeline( mut opaque_render_phases: ResMut>, opaque_draw_functions: Res>, mut specialized_mesh_pipelines: ResMut>, - views: Query<(Entity, &VisibleEntities, &ExtractedView, &Msaa), With>, + views: Query<(Entity, &RenderVisibleEntities, &ExtractedView, &Msaa), With>, render_meshes: Res>, render_mesh_instances: Res, ) { @@ -303,7 +303,7 @@ fn queue_custom_mesh_pipeline( // Find all the custom rendered entities that are visible from this // view. - for &visible_entity in view_visible_entities + for &(render_entity, visible_entity) in view_visible_entities .get::() .iter() { @@ -348,7 +348,7 @@ fn queue_custom_mesh_pipeline( material_bind_group_id: None, lightmap_image: None, }, - visible_entity, + (render_entity, visible_entity), // This example supports batching, but if your pipeline doesn't // support it you can use `BinnedRenderPhaseType::UnbatchableMesh` BinnedRenderPhaseType::BatchableMesh,