From c9ae671dea9c91a56c9be1f79ef51ca79e822d7e Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Sun, 6 Apr 2025 18:55:06 -0700 Subject: [PATCH 1/3] Recompute AABBs when meshes change. Optimize sprite AABB re-computation to avoid component insertion where mutation is possible. --- crates/bevy_render/src/view/visibility/mod.rs | 34 +++++++--- crates/bevy_sprite/src/lib.rs | 65 ++++++++++++++----- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 63c931a8b035d..8f933051830e6 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -10,14 +10,6 @@ use derive_more::derive::{Deref, DerefMut}; pub use range::*; pub use render_layers::*; -use bevy_app::{Plugin, PostUpdate}; -use bevy_asset::Assets; -use bevy_ecs::{hierarchy::validate_parent_has_component, prelude::*}; -use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_transform::{components::GlobalTransform, TransformSystem}; -use bevy_utils::{Parallel, TypeIdMap}; -use smallvec::SmallVec; - use super::NoCpuCulling; use crate::{ camera::{Camera, CameraProjection, Projection}, @@ -25,6 +17,13 @@ use crate::{ primitives::{Aabb, Frustum, Sphere}, sync_world::MainEntity, }; +use bevy_app::{Plugin, PostUpdate}; +use bevy_asset::prelude::*; +use bevy_ecs::{hierarchy::validate_parent_has_component, prelude::*}; +use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_transform::{components::GlobalTransform, TransformSystem}; +use bevy_utils::{Parallel, TypeIdMap}; +use smallvec::SmallVec; /// User indication of whether an entity is visible. Propagates down the entity hierarchy. /// @@ -369,15 +368,30 @@ impl Plugin for VisibilityPlugin { pub fn calculate_bounds( mut commands: Commands, meshes: Res>, - without_aabb: Query<(Entity, &Mesh3d), (Without, Without)>, + new_aabb: Query<(Entity, &Mesh3d), (Without, Without)>, + mut update_aabb: Query< + (&Mesh3d, &mut Aabb), + ( + Or<(AssetChanged, Changed)>, + Without, + ), + >, ) { - for (entity, mesh_handle) in &without_aabb { + for (entity, mesh_handle) in &new_aabb { if let Some(mesh) = meshes.get(mesh_handle) { if let Some(aabb) = mesh.compute_aabb() { commands.entity(entity).try_insert(aabb); } } } + + for (mesh_handle, mut old_aabb) in &mut update_aabb { + if let Some(mesh) = meshes.get(mesh_handle) { + if let Some(aabb) = mesh.compute_aabb() { + *old_aabb = aabb; + } + } + } } /// Updates [`Frustum`]. diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index 37d4d2d6e48ef..9b77e3d411048 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -42,10 +42,12 @@ pub use sprite::*; pub use texture_slice::*; use bevy_app::prelude::*; +use bevy_asset::prelude::AssetChanged; use bevy_asset::{load_internal_asset, weak_handle, AssetEvents, Assets, Handle}; use bevy_core_pipeline::core_2d::{AlphaMask2d, Opaque2d, Transparent2d}; use bevy_ecs::prelude::*; use bevy_image::{prelude::*, TextureAtlasPlugin}; +use bevy_math::Vec2; use bevy_render::{ batching::sort_binned_render_phase, mesh::{Mesh, Mesh2d, MeshAabb}, @@ -163,24 +165,43 @@ pub fn calculate_bounds_2d( meshes: Res>, images: Res>, atlases: Res>, - meshes_without_aabb: Query<(Entity, &Mesh2d), (Without, Without)>, - sprites_to_recalculate_aabb: Query< - (Entity, &Sprite), + new_mesh_aabb: Query<(Entity, &Mesh2d), (Without, Without)>, + mut update_mesh_aabb: Query< + (&Mesh2d, &mut Aabb), ( - Or<(Without, Changed)>, + Or<(AssetChanged, Changed)>, Without, + Without, // disjoint mutable query + ), + >, + new_sprite_aabb: Query<(Entity, &Sprite), (Without, Without)>, + mut update_sprite_aabb: Query< + (&Sprite, &mut Aabb), + ( + Changed, + Without, + Without, // disjoint mutable query ), >, ) { - for (entity, mesh_handle) in &meshes_without_aabb { - if let Some(mesh) = meshes.get(&mesh_handle.0) { + for (entity, mesh_handle) in &new_mesh_aabb { + if let Some(mesh) = meshes.get(mesh_handle) { if let Some(aabb) = mesh.compute_aabb() { commands.entity(entity).try_insert(aabb); } } } - for (entity, sprite) in &sprites_to_recalculate_aabb { - if let Some(size) = sprite + + for (mesh_handle, mut old_aabb) in &mut update_mesh_aabb { + if let Some(mesh) = meshes.get(mesh_handle) { + if let Some(aabb) = mesh.compute_aabb() { + *old_aabb = aabb; + } + } + } + + let sprite_size = |sprite: &Sprite| -> Option { + sprite .custom_size .or_else(|| sprite.rect.map(|rect| rect.size())) .or_else(|| match &sprite.texture_atlas { @@ -191,13 +212,27 @@ pub fn calculate_bounds_2d( .texture_rect(&atlases) .map(|rect| rect.size().as_vec2()), }) - { - let aabb = Aabb { - center: (-sprite.anchor.as_vec() * size).extend(0.0).into(), - half_extents: (0.5 * size).extend(0.0).into(), - }; - commands.entity(entity).try_insert(aabb); - } + }; + + for (size, (entity, sprite)) in new_sprite_aabb + .iter() + .filter_map(|(entity, sprite)| sprite_size(sprite).zip(Some((entity, sprite)))) + { + let aabb = Aabb { + center: (-sprite.anchor.as_vec() * size).extend(0.0).into(), + half_extents: (0.5 * size).extend(0.0).into(), + }; + commands.entity(entity).try_insert(aabb); + } + + for (size, (sprite, mut old_aabb)) in update_sprite_aabb + .iter_mut() + .filter_map(|(sprite, aabb)| sprite_size(sprite).zip(Some((sprite, aabb)))) + { + *old_aabb = Aabb { + center: (-sprite.anchor.as_vec() * size).extend(0.0).into(), + half_extents: (0.5 * size).extend(0.0).into(), + }; } } From 539b9082808cddc504b3c8c4bbf0af7161c6bb2d Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Sun, 6 Apr 2025 20:05:48 -0700 Subject: [PATCH 2/3] Make AABB computation parallel. --- crates/bevy_render/src/view/visibility/mod.rs | 10 +++---- crates/bevy_sprite/src/lib.rs | 29 ++++++++++--------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 8f933051830e6..dfd5244bef1fc 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -385,13 +385,13 @@ pub fn calculate_bounds( } } - for (mesh_handle, mut old_aabb) in &mut update_aabb { - if let Some(mesh) = meshes.get(mesh_handle) { - if let Some(aabb) = mesh.compute_aabb() { + update_aabb + .par_iter_mut() + .for_each(|(mesh_handle, mut old_aabb)| { + if let Some(aabb) = meshes.get(mesh_handle).and_then(MeshAabb::compute_aabb) { *old_aabb = aabb; } - } - } + }); } /// Updates [`Frustum`]. diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index 9b77e3d411048..5aa3e6b2339dd 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -192,13 +192,13 @@ pub fn calculate_bounds_2d( } } - for (mesh_handle, mut old_aabb) in &mut update_mesh_aabb { - if let Some(mesh) = meshes.get(mesh_handle) { - if let Some(aabb) = mesh.compute_aabb() { + update_mesh_aabb + .par_iter_mut() + .for_each(|(mesh_handle, mut old_aabb)| { + if let Some(aabb) = meshes.get(mesh_handle).and_then(MeshAabb::compute_aabb) { *old_aabb = aabb; } - } - } + }); let sprite_size = |sprite: &Sprite| -> Option { sprite @@ -225,15 +225,16 @@ pub fn calculate_bounds_2d( commands.entity(entity).try_insert(aabb); } - for (size, (sprite, mut old_aabb)) in update_sprite_aabb - .iter_mut() - .filter_map(|(sprite, aabb)| sprite_size(sprite).zip(Some((sprite, aabb)))) - { - *old_aabb = Aabb { - center: (-sprite.anchor.as_vec() * size).extend(0.0).into(), - half_extents: (0.5 * size).extend(0.0).into(), - }; - } + update_sprite_aabb + .par_iter_mut() + .for_each(|(sprite, mut aabb)| { + if let Some(size) = sprite_size(sprite) { + *aabb = Aabb { + center: (-sprite.anchor.as_vec() * size).extend(0.0).into(), + half_extents: (0.5 * size).extend(0.0).into(), + }; + } + }); } #[cfg(test)] From 63ca0f2fcc07d028de760b65e52ce80f0d21e0c6 Mon Sep 17 00:00:00 2001 From: Aevyrie Roessler Date: Sun, 6 Apr 2025 20:46:19 -0700 Subject: [PATCH 3/3] Fix ambiguity --- crates/bevy_render/src/view/visibility/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index dfd5244bef1fc..284a4d469fa51 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -18,7 +18,7 @@ use crate::{ sync_world::MainEntity, }; use bevy_app::{Plugin, PostUpdate}; -use bevy_asset::prelude::*; +use bevy_asset::{prelude::*, AssetEvents}; use bevy_ecs::{hierarchy::validate_parent_has_component, prelude::*}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_transform::{components::GlobalTransform, TransformSystem}; @@ -339,10 +339,18 @@ impl Plugin for VisibilityPlugin { app.register_type::() .configure_sets( PostUpdate, - (CalculateBounds, UpdateFrusta, VisibilityPropagate) + (UpdateFrusta, VisibilityPropagate) .before(CheckVisibility) .after(TransformSystem::TransformPropagate), ) + .configure_sets( + PostUpdate, + (CalculateBounds) + .before(CheckVisibility) + .after(TransformSystem::TransformPropagate) + .after(AssetEvents) + .ambiguous_with(CalculateBounds), + ) .configure_sets( PostUpdate, MarkNewlyHiddenEntitiesInvisible.after(CheckVisibility),