Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Divide the single VisibleEntities list into separate lists for 2D meshes, 3D meshes, lights, and UI elements, for performance. #12582

Merged
merged 16 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ use bevy_render::{
render_graph::RenderGraph,
render_resource::Shader,
texture::{GpuImage, Image},
view::VisibilitySystems,
view::{check_visibility, VisibilitySystems},
ExtractSchedule, Render, RenderApp, RenderSet,
};
use bevy_transform::TransformSystem;
Expand Down Expand Up @@ -349,6 +349,14 @@ impl Plugin for PbrPlugin {
.in_set(SimulationLightSystems::UpdateLightFrusta)
.after(TransformSystem::TransformPropagate)
.after(SimulationLightSystems::AssignLightsToClusters),
check_visibility::<WithLight>
.in_set(VisibilitySystems::CheckVisibility)
.after(VisibilitySystems::CalculateBounds)
.after(VisibilitySystems::UpdateOrthographicFrusta)
.after(VisibilitySystems::UpdatePerspectiveFrusta)
.after(VisibilitySystems::UpdateProjectionFrusta)
.after(VisibilitySystems::VisibilityPropagate)
.after(TransformSystem::TransformPropagate),
check_light_mesh_visibility
.in_set(SimulationLightSystems::CheckLightVisibility)
.after(VisibilitySystems::CalculateBounds)
Expand Down
31 changes: 20 additions & 11 deletions crates/bevy_pbr/src/light/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ use bevy_render::{
camera::{Camera, CameraProjection},
extract_component::ExtractComponent,
extract_resource::ExtractResource,
mesh::Mesh,
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere},
render_resource::BufferBindingType,
renderer::RenderDevice,
view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities},
view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities, WithMesh},
};
use bevy_transform::components::{GlobalTransform, Transform};
use bevy_utils::tracing::warn;
Expand Down Expand Up @@ -98,6 +99,10 @@ impl Default for PointLightShadowMap {
}
}

/// A convenient alias for `Or<(With<PointLight>, With<SpotLight>,
/// With<DirectionalLight>)>`, for use with [`VisibleEntities`].
pub type WithLight = Or<(With<PointLight>, With<SpotLight>, With<DirectionalLight>)>;

/// Controls the resolution of [`DirectionalLight`] shadow maps.
#[derive(Resource, Clone, Debug, Reflect)]
#[reflect(Resource)]
Expand Down Expand Up @@ -432,19 +437,19 @@ fn calculate_cascade(
texel_size: cascade_texel_size,
}
}
/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not cast shadows.
/// Add this component to make a [`Mesh`] not cast shadows.
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct NotShadowCaster;
/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not receive shadows.
/// Add this component to make a [`Mesh`] not receive shadows.
///
/// **Note:** If you're using diffuse transmission, setting [`NotShadowReceiver`] will
/// cause both “regular” shadows as well as diffusely transmitted shadows to be disabled,
/// even when [`TransmittedShadowReceiver`] is being used.
#[derive(Component, Reflect, Default)]
#[reflect(Component, Default)]
pub struct NotShadowReceiver;
/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0`
/// Add this component to make a [`Mesh`] using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0`
/// receive shadows on its diffuse transmission lobe. (i.e. its “backside”)
///
/// Not enabled by default, as it requires carefully setting up [`thickness`](crate::pbr_material::StandardMaterial::thickness)
Expand Down Expand Up @@ -1859,7 +1864,11 @@ pub fn check_light_mesh_visibility(
Option<&Aabb>,
Option<&GlobalTransform>,
),
(Without<NotShadowCaster>, Without<DirectionalLight>),
(
Without<NotShadowCaster>,
Without<DirectionalLight>,
With<Handle<Mesh>>,
),
>,
) {
fn shrink_entities(visible_entities: &mut VisibleEntities) {
Expand Down Expand Up @@ -1947,7 +1956,7 @@ pub fn check_light_mesh_visibility(
}

view_visibility.set();
frustum_visible_entities.entities.push(entity);
frustum_visible_entities.get_mut::<WithMesh>().push(entity);
}
}
} else {
Expand All @@ -1959,7 +1968,7 @@ pub fn check_light_mesh_visibility(
.expect("Per-view visible entities should have been inserted already");

for frustum_visible_entities in view_visible_entities {
frustum_visible_entities.entities.push(entity);
frustum_visible_entities.get_mut::<WithMesh>().push(entity);
}
}
}
Expand Down Expand Up @@ -2028,13 +2037,13 @@ pub fn check_light_mesh_visibility(
{
if frustum.intersects_obb(aabb, &model_to_world, true, true) {
view_visibility.set();
visible_entities.entities.push(entity);
visible_entities.push::<WithMesh>(entity);
}
}
} else {
view_visibility.set();
for visible_entities in cubemap_visible_entities.iter_mut() {
visible_entities.entities.push(entity);
visible_entities.push::<WithMesh>(entity);
}
}
}
Expand Down Expand Up @@ -2089,11 +2098,11 @@ pub fn check_light_mesh_visibility(

if frustum.intersects_obb(aabb, &model_to_world, true, true) {
view_visibility.set();
visible_entities.entities.push(entity);
visible_entities.push::<WithMesh>(entity);
}
} else {
view_visibility.set();
visible_entities.entities.push(entity);
visible_entities.push::<WithMesh>(entity);
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use bevy_render::{
render_resource::*,
renderer::RenderDevice,
texture::FallbackImage,
view::{ExtractedView, Msaa, VisibleEntities},
view::{ExtractedView, Msaa, VisibleEntities, WithMesh},
};
use bevy_utils::tracing::error;
use std::marker::PhantomData;
Expand Down Expand Up @@ -645,7 +645,7 @@ pub fn queue_material_meshes<M: Material>(
}

let rangefinder = view.rangefinder3d();
for visible_entity in &visible_entities.entities {
for visible_entity in visible_entities.iter::<WithMesh>() {
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
continue;
};
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod prepass_bindings;

use bevy_render::mesh::{GpuMesh, MeshVertexBufferLayoutRef};
use bevy_render::render_resource::binding_types::uniform_buffer;
use bevy_render::view::WithMesh;
pub use prepass_bindings::*;

use bevy_asset::{load_internal_asset, AssetServer};
Expand Down Expand Up @@ -774,7 +775,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
}

for visible_entity in &visible_entities.entities {
for visible_entity in visible_entities.iter::<WithMesh>() {
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
continue;
};
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use bevy_render::{
render_resource::*,
renderer::{RenderContext, RenderDevice, RenderQueue},
texture::*,
view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities},
view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities, WithMesh},
Extract,
};
use bevy_transform::{components::GlobalTransform, prelude::Transform};
Expand Down Expand Up @@ -1647,13 +1647,13 @@ pub fn queue_shadows<M: Material>(
.get(*light_entity)
.expect("Failed to get spot light visible entities"),
};
// NOTE: Lights with shadow mapping disabled will have no visible entities
// so no meshes will be queued

let mut light_key = MeshPipelineKey::DEPTH_PREPASS;
light_key.set(MeshPipelineKey::DEPTH_CLAMP_ORTHO, is_directional_light);

for entity in visible_entities.iter().copied() {
// NOTE: Lights with shadow mapping disabled will have no visible entities
// so no meshes will be queued

for entity in visible_entities.iter::<WithMesh>().copied() {
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(entity)
else {
continue;
Expand Down
108 changes: 82 additions & 26 deletions crates/bevy_render/src/view/visibility/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod render_layers;

use std::any::TypeId;

use bevy_derive::Deref;
use bevy_ecs::query::QueryFilter;
pub use render_layers::*;

use bevy_app::{Plugin, PostUpdate};
Expand All @@ -9,7 +12,7 @@ use bevy_ecs::prelude::*;
use bevy_hierarchy::{Children, Parent};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_transform::{components::GlobalTransform, TransformSystem};
use bevy_utils::Parallel;
use bevy_utils::{Parallel, TypeIdMap};

use crate::{
camera::{
Expand Down Expand Up @@ -170,23 +173,67 @@ pub struct NoFrustumCulling;
#[reflect(Component, Default)]
pub struct VisibleEntities {
#[reflect(ignore)]
pub entities: Vec<Entity>,
pub entities: TypeIdMap<Vec<Entity>>,
}

impl VisibleEntities {
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &Entity> {
self.entities.iter()
pub fn get<QF>(&self) -> &[Entity]
where
QF: 'static,
{
match self.entities.get(&TypeId::of::<QF>()) {
Some(entities) => &entities[..],
None => &[],
}
}

pub fn get_mut<QF>(&mut self) -> &mut Vec<Entity>
where
QF: 'static,
{
self.entities.entry(TypeId::of::<QF>()).or_default()
}

pub fn iter<QF>(&self) -> impl DoubleEndedIterator<Item = &Entity>
where
QF: 'static,
{
self.get::<QF>().iter()
}

pub fn len<QF>(&self) -> usize
where
QF: 'static,
{
self.get::<QF>().len()
}

pub fn len(&self) -> usize {
self.entities.len()
pub fn is_empty<QF>(&self) -> bool
where
QF: 'static,
{
self.get::<QF>().is_empty()
}

pub fn is_empty(&self) -> bool {
self.entities.is_empty()
pub fn clear<QF>(&mut self)
where
QF: 'static,
{
self.get_mut::<QF>().clear();
}

pub fn push<QF>(&mut self, entity: Entity)
where
QF: 'static,
{
self.get_mut::<QF>().push(entity);
}
}

/// A convenient alias for `With<Handle<Mesh>>`, for use with
/// [`VisibleEntities`].
pub type WithMesh = With<Handle<Mesh>>;

#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
pub enum VisibilitySystems {
/// Label for the [`calculate_bounds`], `calculate_bounds_2d` and `calculate_bounds_text2d` systems,
Expand Down Expand Up @@ -238,7 +285,7 @@ impl Plugin for VisibilityPlugin {
.after(camera_system::<Projection>)
.after(TransformSystem::TransformPropagate),
(visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate),
check_visibility
check_visibility::<WithMesh>
.in_set(CheckVisibility)
.after(CalculateBounds)
.after(UpdateOrthographicFrusta)
Expand Down Expand Up @@ -366,35 +413,44 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) {

/// System updating the visibility of entities each frame.
///
/// The system is part of the [`VisibilitySystems::CheckVisibility`] set. Each frame, it updates the
/// [`ViewVisibility`] of all entities, and for each view also compute the [`VisibleEntities`]
/// for that view.
pub fn check_visibility(
/// The system is part of the [`VisibilitySystems::CheckVisibility`] set. Each
/// frame, it updates the [`ViewVisibility`] of all entities, and for each view
/// also compute the [`VisibleEntities`] for that view.
///
/// This system needs to be run for each type of renderable entity. If you add a
/// new type of renderable entity, you'll need to add an instantiation of this
/// system to the [`VisibilitySystems::CheckVisibility`] set so that Bevy will
/// detect visibility properly for those entities.
pub fn check_visibility<QF>(
mut thread_queues: Local<Parallel<Vec<Entity>>>,
mut view_query: Query<(
&mut VisibleEntities,
&Frustum,
Option<&RenderLayers>,
&Camera,
)>,
mut visible_aabb_query: Query<(
Entity,
&InheritedVisibility,
&mut ViewVisibility,
Option<&RenderLayers>,
Option<&Aabb>,
&GlobalTransform,
Has<NoFrustumCulling>,
)>,
) {
mut visible_aabb_query: Query<
(
Entity,
&InheritedVisibility,
&mut ViewVisibility,
Option<&RenderLayers>,
Option<&Aabb>,
&GlobalTransform,
Has<NoFrustumCulling>,
),
QF,
>,
) where
QF: QueryFilter + 'static,
{
for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query {
if !camera.is_active {
continue;
}

let view_mask = maybe_view_mask.copied().unwrap_or_default();

visible_entities.entities.clear();
visible_aabb_query.par_iter_mut().for_each(|query_item| {
let (
entity,
Expand Down Expand Up @@ -442,8 +498,8 @@ pub fn check_visibility(
});
});

visible_entities.entities.clear();
thread_queues.drain_into(&mut visible_entities.entities);
visible_entities.clear::<QF>();
thread_queues.drain_into(visible_entities.get_mut::<QF>());
}
}

Expand Down
11 changes: 10 additions & 1 deletion crates/bevy_sprite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod prelude {
};
}

use bevy_transform::TransformSystem;
pub use bundle::*;
pub use dynamic_texture_atlas_builder::*;
pub use mesh2d::*;
Expand All @@ -51,7 +52,7 @@ use bevy_render::{
render_phase::AddRenderCommand,
render_resource::{Shader, SpecializedRenderPipelines},
texture::Image,
view::{NoFrustumCulling, VisibilitySystems},
view::{check_visibility, NoFrustumCulling, VisibilitySystems},
ExtractSchedule, Render, RenderApp, RenderSet,
};

Expand Down Expand Up @@ -94,6 +95,14 @@ impl Plugin for SpritePlugin {
compute_slices_on_sprite_change,
)
.in_set(SpriteSystem::ComputeSlices),
check_visibility::<WithMesh2d>
.in_set(VisibilitySystems::CheckVisibility)
.after(VisibilitySystems::CalculateBounds)
.after(VisibilitySystems::UpdateOrthographicFrusta)
.after(VisibilitySystems::UpdatePerspectiveFrusta)
.after(VisibilitySystems::UpdateProjectionFrusta)
.after(VisibilitySystems::VisibilityPropagate)
.after(TransformSystem::TransformPropagate),
),
);

Expand Down