From faba920c798ec9702fd10dffd298b8eeffa7eb45 Mon Sep 17 00:00:00 2001 From: koe Date: Fri, 15 Mar 2024 17:37:00 -0500 Subject: [PATCH 01/54] add RenderGroups (WIP) --- .../src/view/visibility/render_groups.rs | 476 ++++++++++++++++++ 1 file changed, 476 insertions(+) create mode 100644 crates/bevy_render/src/view/visibility/render_groups.rs diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs new file mode 100644 index 0000000000000..8be496f7678c9 --- /dev/null +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -0,0 +1,476 @@ +use bevy_ecs::Entity; + +use smallvec::SmallVec; + +/// The default [`RenderLayer`]. +pub const DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); + +/// Wraps a specific render layer that can be stored in [`RenderLayers`]. +/// +/// Stores an index into the [`RenderLayers`] internal bitmask. +//todo: Upper limit policy for render layer indices. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub struct RenderLayer(pub usize); + +impl RenderLayer +{ + /// Returns `true` if equal to [`DEFAULT_RENDER_LAYER`]. + pub fn is_default(&self) -> bool { + *self == DEFAULT_RENDER_LAYER + } +} + +impl Default for RenderLayer { + fn default() -> Self { + Self(DEFAULT_RENDER_LAYER) + } +} + +/// Records a growable bitmask of flags for controlling which entities +/// are visible to which cameras. +/// +/// Individual render layers can be defined with [`RenderLayer`], which is an index +/// into the internal `RenderLayers` bitmask. +/// +/// `RenderLayers::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default +/// layer. +/// +/// ### Performance +/// +/// `RenderLayers` occupies 24 bytes on the stack. +/// +/// `RenderLayers` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte +/// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. +#[derive(Debug, Clone)] +pub struct RenderLayers +{ + layers: SmallVec<[u64; 1]>, +} + +impl RenderLayers { + /// Makes a new `RenderLayers` with no layers. + pub fn empty() -> Self { + Self{ layers: SmallVec::default() } + } + + /// Adds a [`RenderLayer`]. + pub fn add(&mut self, layer: RenderLayer) { + let (buffer_index, bit) = Self::layer_info(layer); + self.extend_buffer(buffer_index + 1); + self.layers[buffer_index] |= bit; + } + + /// Removes a [`RenderLayer`]. + /// + /// Does not shrink the internal buffer even if doing so is possible after + /// removing the layer. We assume if you added a large layer then it is + /// possible you may re-add another large layer. + pub fn remove(&mut self, layer: RenderLayer) { + let (buffer_index, bit) = Self::layer_info(layer); + if buffer_index >= self.layers.len() { + return; + } + self.layers[buffer_index] &= ~bit; + } + + /// Clears all stored render layers without deallocating. + pub fn clear(&mut self) { + self.layers.clear(); + } + + /// Copies `other` into `Self`. + /// + /// This is more efficient than cloning `other` if you want to reuse a `RenderLayers` + /// that is potentially allocated. + pub fn set_from(&mut self, other: &Self) { + self.layers.clear(); + self.layers.reserve_exact(other.len()); + self.layers.extend_from_slice(other.layers.as_slice()); + } + + /// Merges `other` into `Self`. + /// + /// After merging, `Self` will include all set bits from `other` and `Self`. + /// + /// Will allocate if necessary to include all set bits of `other`. + pub fn merge(&mut self, other: &Self) { + self.extend_buffer(other.len()); + + for (self_layer, other_layer) in self.layers + .iter_mut() + .zip(other.layers.iter()) + { + *self_layer |= *other_layer; + } + } + + /// Iterates the internal render layers. + pub fn iter(&self) -> impl Iterator + '_ { + self.layers + .iter() + .copied() + .map(Self::iter_layers) + .flatten() + } + + /// Returns `true` if the specified render layer is included in this `RenderLayers`. + pub fn contains_layer(&self, layer: RenderLayer) -> bool { + let (buffer_index, bit) = Self::layer_info(layer); + if buffer_index >= self.layers.len() { + return false; + } + (self.layers[buffer_index] & bit) != 0 + } + + /// Returns `true` if `Self` and `other` contain any matching layers. + pub fn intersects(&self, other: &Self) -> bool { + for (self_layer, other_layer) in self.layers + .iter() + .zip(other.layers.iter()) + { + if (*self_layer & *other_layer) != 0 { + return true; + } + } + + false + } + + fn layer_info(layer: usize) -> (usize, u64) { + let buffer_index = layer / 64; + let bit_index = layer % 64; + let bit = 1u64 << bit_index; + + (buffer_index, bit) + } + + fn extend_buffer(&mut self, other_len: usize) { + let new_size = std::cmp::max(self.layers.len(), other_len); + self.layers.reserve_exact(new_size - self.layers.len()); + self.layers.resize(new_size, 0u64); + } + + fn iter_layers(mut buffer: u64) -> impl Iterator + '_ { + let mut layer = 0; + std::iter::from_fn( + move { + if buffer == 0 { + return None; + } + let next = buffer.trailing_zeroes() + 1; + buffer >>= next; + layer += next; + Some(layer - 1) + } + ) + } +} + +impl From for RenderLayers { + fn from(layer: RenderLayer) -> Self { + let mut layers = Self{ layers: SmallVec::default() }; + layers.add(layer); + layers + } +} + +impl Default for RenderLayers { + fn default() -> Self { + Self::from(DEFAULT_RENDER_LAYER) + } +} + +/// Component on an entity that controls which cameras can see it. +/// +/// There are two kinds of render groups: +/// - [`RenderLayers`]: These are grouping categories that many cameras can view (see [`CameraView`]). +/// - *Camera entity*: This is a specific camera that the entity is affiliated with. This is especially +/// useful for UI in combination with [`PropagateRenderGroups`]. +/// +/// An entity can be a member of multiple [`RenderLayers`] in addition to having a camera affiliation. +/// +/// ### Default behavior +/// +/// A default-constructed `RenderGroups` will include [`DEFAULT_RENDER_LAYER`]. +/// If you don't want that, then use [`Self::empty`], [`Self::new_with_camera`], or +/// [`Self::from::`]. +/// +/// ### Entity default behavior +/// +/// All entities without a [`RenderGroups`] component are in [`DEFAULT_RENDER_LAYER`] by +/// default (layer 0). If you add a [`RenderGroups`] component to an entity, it may no longer +/// be in the default layer if the [`RenderGroups`] component doesn't include it. +/// +/// For example, if you do `entity.insert(RenderGroups::from(RenderLayer(1)))`, then `entity` +/// will only be in layer 1. You can instead do: +/** +```no-run +// Option 1: default +let mut groups = RenderGroups::default(); +groups.add(RenderLayer(1); +entity.insert(groups); + +// Option 2: explicit +let mut groups = RenderGroups::from(0); +groups.add(RenderLayer(1); +entity.insert(groups); +``` +*/ +#[derive(Component, Debug, Clone)] +pub struct RenderGroups +{ + layers: RenderLayers, + camera: Option, +} + +impl RenderGroups { + /// Makes a new `RenderGroups` with no groups. + pub fn empty() -> Self { + Self{ layers: RenderLayers::empty(), camera: None } + } + + /// Makes a new `RenderGroups` with just a camera. + pub fn new_with_camera(camera: Entity) -> Self { + Self{ layers: RenderLayers::empty(), camera: Some(camera) } + } + + /// Adds a [`RenderLayer`]. + /// + /// See [`RenderLayers::add`]. + pub fn add(&mut self, layer: RenderLayer) -> &mut Self { + self.layers.add(layer); + self + } + + /// Removes a [`RenderLayer`]. + /// + /// See [`RenderLayers::remove`]. + pub fn remove(&mut self, layer: RenderLayer) -> &mut Self { + self.layers.remove(layer); + self + } + + /// Clears all stored render layers without deallocating, and unsets the camera affiliation. + pub fn clear(&mut self) { + self.layers.clear(); + self.camera = None; + } + + /// Merges `other` into `Self`. + /// + /// After merging, `Self` will include all [`RenderLayers`] from `other` and `Self`. + /// If both `Self` and `other` have a camera affiliation, then the `Self` camera + /// will be in the merged result. Otherwise the `other` camera will be in the result. + /// + /// Will allocate if necessary to include all [`RenderLayers`] of `other`. + pub fn merge(&mut self, other: &Self) { + self.layers.merge(&other.layers); + self.camera = self.camera.or(other.camera); + } + + /// Copies `other` into `Self. + /// + /// This is more efficient than cloning `other` if you want to reuse a `RenderGroups` + /// that is potentially allocated. + pub fn set_from(&mut self, other: &Self) { + self.layers.set_from(&other.layers); + self.camera = other.camera; + } + + /// Sets the camera affiliation. + /// + /// Returns the previous camera. + pub fn set_camera(&mut self, camera: Entity) -> Option { + self.camera.replace(Some(camera)) + } + + /// Removes the current camera affiliation. + /// + /// Returns the removed camera. + pub fn remove_camera(&mut self) -> Option { + self.camera.take() + } + + /// Returns an iterator over [`RenderLayer`]. + pub fn iter_layers(&self) -> Impl Iterator + '_ { + self.layers.iter() + } + + /// Returns `true` if the specified render layer is included in this + /// `RenderGroups`. + pub fn contains_layer(&self, layer: RenderLayer) -> bool { + self.layers.contains(layer) + } + + /// Returns `true` if `Self` intersects with `other`. + /// + /// Checks both camera affiliation and [`RenderLayers`] intersection. + pub fn intersects(&self, other: &Self) -> bool { + if let (Some(a), Some(b)) = (self.camera, other.camera) { + if a == b { + return true; + } + } + self.layers.intersects(&other.layers) + } + + /// Gets the camera affiliation. + pub fn camera(&self) -> Option { + self.camera + } +} + +impl From for RenderGroups { + /// Makes a new `RenderGroups` from a specific [`RenderLayer`]. + fn from(layer: RenderLayer) -> Self { + Self{ layers: RenderLayers::from(layer), camera: None } + } +} + +impl From for RenderGroups { + /// Makes a new `RenderGroups` from a [`RenderLayers`]. + fn from(layers: RenderLayers) -> Self { + Self{ layers, camera: None } + } +} + +impl Default for RenderGroups { + /// Equivalent to `Self::from(DEFAULT_RENDER_LAYER)`. + fn default() -> Self { + Self::from(DEFAULT_RENDER_LAYER) + } +} + +/// Component on an entity computed by merging its [`RenderGroups`] component with +/// a [`RenderGroups`] propagated by the entity's parent via [`PropagateRenderGroups`]. +/// +/// This is automatically updated in [`PostUpdate`] in the TODO visibility system. +/// The component will be removed if the entity has no [`RenderGroups`] component and no +/// component is propagated. +/// +/// ### Merge details +/// +/// This will equal the entity's [`RenderGroups`] component if no groups are propagated, and +/// vice versa if a [`RenderGroups`] is propagated and an entity has no [`RenderGroups`] component. +/// +/// The merge direction is 'entity_rendergroups.merge(propagated_rendergroups)` +/// (see [`RenderGroups::merge`]). +/// This means `InheritedRenderGroups` will prioritize the entity's affiliated camera +/// over the propagated affiliated camera. +#[derive(Component, Debug, Clone)] +pub struct InheritedRenderGroups(RenderGroups); + +/// Component on camera entities that controls which [`RenderLayers`] are visible to +/// the camera. +/// +/// A camera will see any entity that satisfies either of these conditions: +/// - The entity is in a [`RenderLayer`] visible to the camera. +/// - The entity has a [`RenderGroups`] component with camera affiliation equal to the camera. +/// +/// Cameras use entities' [`InheritedRenderGroups] to determine visibility. If an entity has no +/// [`InheritedRenderGroups`] component, then the camera will only see it if the camera can +/// view the [`DEFAULT_RENDER_LAYER`] layer. +/// +/// A camera without the `CameraView` component will see the [`DEFAULT_RENDER_LAYER`] +/// layer, in addition to relevant [`RenderGroups`] camera affiliations. +/// +/// A default `CameraView` will include the [`DEFAULT_RENDER_LAYER`]. +#[derive(Component, Debug, Clone)] +pub struct CameraView +{ + layers: RenderLayers, +} + +impl CameraView { + /// Makes a new `CameraView` with no visibile [`RenderLayer`]. + pub fn empty() -> Self { + Self{ layers: RenderLayers::empty() } + } + + /// Adds a [`RenderLayer`]. + /// + /// See [`RenderLayers::add`]. + pub fn add(&mut self, layer: RenderLayer) -> &mut Self { + self.layers.add(layer); + self + } + + /// Removes a [`RenderLayer`]. + /// + /// See [`RenderLayers::remove`]. + pub fn remove(&mut self, layer: RenderLayer) -> &mut Self { + self.layers.remove(layer); + self + } + + /// Clears all stored render layers without deallocating. + pub fn clear(&mut self) { + self.layers.clear(); + } + + /// Returns an iterator over [`RenderLayer`]. + pub fn iter_layers(&self) -> Impl Iterator + '_ { + self.layers.iter() + } + + /// Returns `true` if the specified render layer is included in this `CameraView`. + pub fn contains_layer(&self, layer: RenderLayer) -> bool { + self.layers.contains(layer) + } + + /// Returns `true` if the entity with the specified [`RenderGroups`] is visible + /// to the `camera` that has this `CameraView`. + /// + /// Checks both camera affiliation and [`RenderLayers`] intersection. + pub fn entity_is_visible(&self, camera: Entity, groups: &RenderGroups) -> bool { + if Some(camera) == groups.camera { + return true; + } + self.layers.intersects(&groups.layers) + } + + /// Converts the internal [`RenderLayers`] into a [`RenderGroups`] affiliated + /// with the camera that has this `CameraView`. + pub fn get_groups(&self, camera: Entity) -> RenderGroups { + let mut groups = RenderGroups::from(self.layers.clone()); + groups.set_camera(camera); + groups + } +} + +impl From for CameraView { + /// Makes a new `CameraView` from a specific [`RenderLayer`]. + fn from(layer: RenderLayer) -> Self { + Self{ layers: RenderLayers::from(layer) } + } +} + +impl Default for CameraView { + /// Equivalent to `Self::from(DEFAULT_RENDER_LAYER)`. + fn default() -> Self { + Self::from(DEFAULT_RENDER_LAYER) + } +} + +/// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. +/// +/// See [`RenderGroups`] and [`CameraView`]. +#[derive(Component)] +pub enum PropagateRenderGroups +{ + /// If the entity has a [`RenderGroups`] component, that value is propagated. + /// + /// Otherwise nothing is propagated and no errors are logged. + Auto, + /// If the entity has a [`Camera`] component, propagates `RenderGroups::new_with_camera(entity)`. + /// + /// Otherwise an error will be logged. + Camera, + /// If the entity has a [`Camera`] component and a [`CameraView`] component, propagates + /// `CameraView::get_groups(entity)`. + /// + /// Otherwise an error will be logged. + CameraWithView, + /// Propagates a custom [`RenderGroups`]. + Custom(RenderGroups), +} From 756191b4037dcc3ececeb593e8a3387586523353 Mon Sep 17 00:00:00 2001 From: koe Date: Fri, 15 Mar 2024 18:02:35 -0500 Subject: [PATCH 02/54] typo --- crates/bevy_render/src/view/visibility/render_groups.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 8be496f7678c9..ee9836da90238 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -382,7 +382,7 @@ pub struct CameraView } impl CameraView { - /// Makes a new `CameraView` with no visibile [`RenderLayer`]. + /// Makes a new `CameraView` with no visible [`RenderLayer`]. pub fn empty() -> Self { Self{ layers: RenderLayers::empty() } } From 0d302b13c3372e4bf61189f6fba66095b7f091e3 Mon Sep 17 00:00:00 2001 From: koe Date: Sat, 16 Mar 2024 00:01:48 -0500 Subject: [PATCH 03/54] integration WIP --- crates/bevy_render/src/view/visibility/mod.rs | 218 +++++++++++++++++- .../src/view/visibility/render_groups.rs | 127 ++++++++-- 2 files changed, 320 insertions(+), 25 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index d7fd09ba7a353..132f5b3ed0507 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,7 +1,8 @@ mod render_layers; use bevy_derive::Deref; -pub use render_layers::*; +pub use render_groups::*; +//pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{Assets, Handle}; @@ -199,8 +200,8 @@ pub enum VisibilitySystems { UpdatePerspectiveFrusta, /// Label for the [`update_frusta`] system. UpdateProjectionFrusta, - /// Label for the system propagating the [`InheritedVisibility`] in a - /// [`hierarchy`](bevy_hierarchy). + /// Label for the system propagating [`InheritedVisibility`] and applying [`PropagateRenderGroups`] + /// in a [`hierarchy`](bevy_hierarchy). VisibilityPropagate, /// Label for the [`check_visibility`] system updating [`ViewVisibility`] /// of each entity and the [`VisibleEntities`] of each view. @@ -238,7 +239,11 @@ impl Plugin for VisibilityPlugin { .in_set(UpdateProjectionFrusta) .after(camera_system::) .after(TransformSystem::TransformPropagate), - (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate), + (visibility_propagate_system, propagate_render_groups, reset_view_visibility).in_set(VisibilityPropagate), + // Use apply_deferred to process InheritedRenderGroups component additions/removals. + apply_deferred + .after(VisibilityPropagate) + .before(CheckVisibility), check_visibility .in_set(CheckVisibility) .after(CalculateBounds) @@ -353,6 +358,190 @@ fn propagate_recursive( Ok(()) } +fn propagate_render_groups( + mut updated_entities: Local, + mut commands: Commands, + // Query for all propagators with changed PropagateRenderGroups. + // This does a 'full propagation' to push changes all the way down the tree. + changed_propagate_cameras_query: Query< + (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), + Changed + >, + // Query for camera propagator entities with changed CameraView. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + changed_view_cameras_query: Query< + (Entity, &CameraView, Option<&RenderGroups>, &PropagateRenderGroups), + (With, Changed), + >, + // Tracker to identify entities that lost CameraView. + mut removed_cameraview: RemovedComponents, + // Query for all propagators with removed CameraView. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + removed_cameraview_propagator_query: Query< + (Entity, Option<&RenderGroups>, Option<&Camera>, &PropagateRenderGroups), + Without + >, + // Query for all propagator entities with updated RenderGroups. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + changed_rendergroups_propagator_query: Query< + (Entity, &RenderGroups, &PropagateRenderGroups), + (Changed) + >, + // Tracker to identify entities that lost RenderGroups. + mut removed_rendergroups: RemovedComponents, + // Query for propagator entities with removed RenderGroups. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + removed_rendergroups_propagator_query: Query< + (Entity, Option<&CameraView>, Option<&Camera>, &PropagateRenderGroups), + Without + >, + // Query for all propagators with changed children. + // This does a 'propagator enity propagation' to push changes to only descendents with different propagater + // entities. + changed_children_propagator_query: Query< + (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), + Changed, + >, + // Tracker to identify entities that lost PropagateRenderGroups. + // These entities and their children will be 'repropagated' from the lost entities' parents. + // - Takes into account where the entity has a parent. + // - Takes into account whether the parent is a propagator or non-propagater. + mut removed_propagate: RemovedComponents, + removed_propagate_entities: Query<&Children, Without>, + // Query for entities with InheritedRenderGroups who gained a new parent. + // - Ignores entities whose parents have PropagateRenderGroups, since that case is handled by + // changed_children_propagator_query. + // - If the parent doesn't have InheritedRenderGroups, then the entity and its children will be 'unpropagated'. + // - If the parent does have InheritedRenderGroups, then propagation will be applied to the entity and + // its children. + // This does a 'propagator enity propagation' to push changes to only descendents with different propagater + // entities. + changed_parents_query: Query< + (Entity, Option<&Children>), + (Changed, With, Without) + >, + // Query for entities with InheritedRenderGroups whose children changed. + // - Ignores children with InheritedRenderGroups, those are handled by changed_parents_query. + // This does a 'propagator enity propagation' to push changes to only descendents with different propagater + // entities. + changed_children_query: Query< + (Entity, &Children), + (Changed, With, Without) + >, + // Query for non-propagator entities with updated RenderGroups. + // This updates the entity's InheritedRenderGroups. + changed_rendergroups_query: Query< + (Entity, &RenderGroups), + (Changed, Without) + >, + // Query for non-propagator entities with InheritedRenderGroups and removed RenderGroups. + // This updates the entity's InheritedRenderGroups. + removed_rendergroups_query: Query< + Entity, + (With, Without) + >, + // Query for entities with PropagateRenderGroups that InheritedRenderGroups needs to be removed from. + dirty_propagators: Query, With)>, + // Query for getting Children. + children_query: Query<&Children>, + // Query for entities that propagate. + // Used when pulling inheritance information from the parent. + propagators: Query< + (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups) + >, + // Query for entities that inherited and don't propagate. + // Used when pulling inheritance information from the parent. + nonpropagators: Query<(), (With, Without)>, + // Query for updating InheritedRenderGroups on entities. + mut maybe_inherited_query: Query<(Entity, Option<&RenderGroups>, Option<&mut InheritedRenderGroups>)>, +) { + + let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); + + let mut propagated = if let Some(propagate) = maybe_propagate_groups { + Some(propagate.get_from_camera(entity, camera_view)) + } else { + None + }; + + + // Track updated entities to prevent redundant updates, as `Commands` changes are deferred. + // + // Using this ensures that once mutations to entities with PropagateRenderGroups have been + // propagated, all entities affected by those changes won't be mutated again. This makes it + // safe to read parent InheritedRenderGroups (in the other cases that need to be handled) in + // order to 'pull in' inherited changes when needed. + updated_entities.clear(); + + // Assuming that TargetCamera is manually set on the root node only, + // update root nodes first, since it implies the biggest change + for (root_node, target_camera) in &changed_root_nodes_query { + update_children_render_groups( + root_node, + target_camera, + &node_query, + &children_query, + &mut commands, + &mut updated_entities, + ); + } + + // If the root node TargetCamera was changed, then every child is updated + // by this point, and iteration will be skipped. + // Otherwise, update changed children + for (parent, target_camera) in &changed_children_query { + update_children_render_groups( + parent, + target_camera, + &node_query, + &children_query, + &mut commands, + &mut updated_entities, + ); + } +} + +fn update_children_render_groups( + entity: Entity, + camera_to_set: Option<&TargetCamera>, + node_query: &Query, With>, + children_query: &Query<&Children, With>, + commands: &mut Commands, + updated_entities: &mut HashSet, +) { + let Ok(children) = children_query.get(entity) else { + return; + }; + + for &child in children { + // Skip if the child has already been updated or update is not needed + if updated_entities.contains(&child) + || camera_to_set == node_query.get(child).ok().flatten() + { + continue; + } + + match camera_to_set { + Some(camera) => { + commands.entity(child).insert(camera.clone()); + } + None => { + commands.entity(child).remove::(); + } + } + updated_entities.insert(child); + + update_children_render_groups( + child, + camera_to_set, + node_query, + children_query, + commands, + updated_entities, + ); + } +} + /// Resets the view visibility of every entity. /// Entities that are visible will be marked as such later this frame /// by a [`VisibilitySystems::CheckVisibility`] system. @@ -372,29 +561,32 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { /// for that view. pub fn check_visibility( mut thread_queues: Local>>, + mut commands: Commands, mut view_query: Query<( + Entity, &mut VisibleEntities, &Frustum, - Option<&RenderLayers>, + Option<&CameraView>, &Camera, )>, mut visible_aabb_query: Query<( Entity, &InheritedVisibility, &mut ViewVisibility, - Option<&RenderLayers>, + Option<&RenderGroups>, + Option<&InheritedRenderGroups>, Option<&Aabb>, &GlobalTransform, Has, )>, deterministic_rendering_config: Res, ) { - for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query { + for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { if !camera.is_active { continue; } - let view_mask = maybe_view_mask.copied().unwrap_or_default(); + let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); visible_entities.entities.clear(); visible_aabb_query.par_iter_mut().for_each(|query_item| { @@ -402,7 +594,8 @@ pub fn check_visibility( entity, inherited_visibility, mut view_visibility, - maybe_entity_mask, + maybe_groups, + maybe_inherited_groups, maybe_model_aabb, transform, no_frustum_culling, @@ -414,8 +607,11 @@ pub fn check_visibility( return; } - let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); - if !view_mask.intersects(&entity_mask) { + // Check render groups. + let entity_groups = maybe_inherited_groups.map(|i| &i.computed) + .or(maybe_groups) + .unwrap_or(&RenderGroups::default()); + if !camera_view.entity_is_visible(camera_entity, entity_groups) { return; } diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index ee9836da90238..b27d9aec3589a 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -114,7 +114,7 @@ impl RenderLayers { } /// Returns `true` if the specified render layer is included in this `RenderLayers`. - pub fn contains_layer(&self, layer: RenderLayer) -> bool { + pub fn contains(&self, layer: RenderLayer) -> bool { let (buffer_index, bit) = Self::layer_info(layer); if buffer_index >= self.layers.len() { return false; @@ -136,6 +136,12 @@ impl RenderLayers { false } + /// Gets the bitmask representation of the contained layers + /// as a slice of bitmasks. + pub fn bits(&self) -> &[u64] { + self.layers.as_slice() + } + fn layer_info(layer: usize) -> (usize, u64) { let buffer_index = layer / 64; let bit_index = layer % 64; @@ -174,6 +180,16 @@ impl From for RenderLayers { } } +impl From<&[RenderLayer]> for RenderLayers { + fn from(layers: &[RenderLayer]) -> Self { + let mut layers = Self{ layers: SmallVec::default() }; + for layer in layers { + layers.add(layer); + } + layers + } +} + impl Default for RenderLayers { fn default() -> Self { Self::from(DEFAULT_RENDER_LAYER) @@ -341,24 +357,33 @@ impl Default for RenderGroups { } } -/// Component on an entity computed by merging its [`RenderGroups`] component with -/// a [`RenderGroups`] propagated by the entity's parent via [`PropagateRenderGroups`]. +/// Component on an entity that stores the result of merging the entity's [`RenderGroups`] +/// component with the [`RenderGroups`] of an entity propagated by the entity's parent. /// -/// This is automatically updated in [`PostUpdate`] in the TODO visibility system. -/// The component will be removed if the entity has no [`RenderGroups`] component and no -/// component is propagated. +/// See [`PropagateRenderGroups`]. /// -/// ### Merge details +/// This is automatically updated in [`PostUpdate`] in the [`VisibilityPropagate`] set. +/// The component will be automatically added or removed depending on if it is needed. /// -/// This will equal the entity's [`RenderGroups`] component if no groups are propagated, and -/// vice versa if a [`RenderGroups`] is propagated and an entity has no [`RenderGroups`] component. +/// ### Merge details /// /// The merge direction is 'entity_rendergroups.merge(propagated_rendergroups)` /// (see [`RenderGroups::merge`]). -/// This means `InheritedRenderGroups` will prioritize the entity's affiliated camera -/// over the propagated affiliated camera. +/// This means the entity's affiliated camera will be prioritized over the propagated affiliated camera. #[derive(Component, Debug, Clone)] -pub struct InheritedRenderGroups(RenderGroups); +pub struct InheritedRenderGroups +{ + /// The entity that propagated [`RenderGroups`] to this entity. + /// + /// This is cached so children of this entity can update themselves without needing to traverse the + /// entire hierarchy. + pub propagater: Entity, + /// The [`RenderGroups`] computed by merging the [`RenderGroups`] of the `Self::propagater` entity into + /// the node's [`RenderGroups`] component. + /// + /// This is cached for efficient access in the [`check_visibility`] system. + pub computed: RenderGroups, +}; /// Component on camera entities that controls which [`RenderLayers`] are visible to /// the camera. @@ -367,8 +392,9 @@ pub struct InheritedRenderGroups(RenderGroups); /// - The entity is in a [`RenderLayer`] visible to the camera. /// - The entity has a [`RenderGroups`] component with camera affiliation equal to the camera. /// -/// Cameras use entities' [`InheritedRenderGroups] to determine visibility. If an entity has no -/// [`InheritedRenderGroups`] component, then the camera will only see it if the camera can +/// Cameras use entities' [`InheritedRenderGroups`] to determine visibility, with a fall-back to the +/// entity's [`RenderGroups`]. If an entity does not have [`InheritedRenderGroups`] +/// or [`RenderGroups`] components, then the camera will only see it if the camera can /// view the [`DEFAULT_RENDER_LAYER`] layer. /// /// A camera without the `CameraView` component will see the [`DEFAULT_RENDER_LAYER`] @@ -454,12 +480,16 @@ impl Default for CameraView { /// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. /// +/// Entities with this component will ignore [`RenderGroups`] propagated by parents. +/// /// See [`RenderGroups`] and [`CameraView`]. #[derive(Component)] pub enum PropagateRenderGroups { /// If the entity has a [`RenderGroups`] component, that value is propagated. /// + /// Note that it is allowed to add a [`RenderGroup`] component to a camera. + /// /// Otherwise nothing is propagated and no errors are logged. Auto, /// If the entity has a [`Camera`] component, propagates `RenderGroups::new_with_camera(entity)`. @@ -474,3 +504,72 @@ pub enum PropagateRenderGroups /// Propagates a custom [`RenderGroups`]. Custom(RenderGroups), } + +#[cfg(test)] +mod rendering_mask_tests { + use super::{RenderLayer, RenderLayers}; + + #[test] + fn rendering_mask_sanity() { + assert_eq!(RenderLayers::default().len(), 1, "default layer contains only one layer"); + assert!(RenderLayers::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default"); + assert_eq!(RenderLayers::from(RenderLayer(1)).len(), 1, "from contains 1 layer"); + assert!(RenderLayers::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate"); + assert!(!RenderLayers::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected"); + + + + assert_eq!(RenderLayers::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3"); + assert_eq!( + RenderLayers::layer(0).with(1).without(0).0, + 2, + "layer 0 + 1 - 0 is mask 2" + ); + assert!( + RenderLayers::layer(1).intersects(&RenderLayers::layer(1)), + "layers match like layers" + ); + assert!( + RenderLayers::layer(0).intersects(&RenderLayers(1)), + "a layer of 0 means the mask is just 1 bit" + ); + + assert!( + RenderLayers::layer(0) + .with(3) + .intersects(&RenderLayers::layer(3)), + "a mask will match another mask containing any similar layers" + ); + + assert!( + RenderLayers::default().intersects(&RenderLayers::default()), + "default masks match each other" + ); + + assert!( + !RenderLayers::layer(0).intersects(&RenderLayers::layer(1)), + "masks with differing layers do not match" + ); + assert!( + !RenderLayers(0).intersects(&RenderLayers(0)), + "empty masks don't match" + ); + assert_eq!( + RenderLayers::from_layers(&[0, 2, 16, 30]) + .iter() + .collect::>(), + vec![0, 2, 16, 30], + "from_layers and get_layers should roundtrip" + ); + assert_eq!( + format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(), + "RenderLayers([0, 1, 2, 3])", + "Debug instance shows layers" + ); + assert_eq!( + RenderLayers::from_layers(&[0, 1, 2]), + >::from_iter(vec![0, 1, 2]), + "from_layers and from_iter are equivalent" + ); + } +} From ac31198fd9ad22abd50fddfe74fc39d045bd102b Mon Sep 17 00:00:00 2001 From: koe Date: Sat, 16 Mar 2024 22:10:57 -0500 Subject: [PATCH 04/54] propagation algorithm WIP --- crates/bevy_render/src/view/visibility/mod.rs | 185 +---- .../visibility/propagate_render_groups.rs | 736 ++++++++++++++++++ .../src/view/visibility/render_groups.rs | 59 +- 3 files changed, 741 insertions(+), 239 deletions(-) create mode 100644 crates/bevy_render/src/view/visibility/propagate_render_groups.rs diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 132f5b3ed0507..14fcf2862f837 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,6 +1,7 @@ mod render_layers; use bevy_derive::Deref; +pub use propagate_render_groups::*; pub use render_groups::*; //pub use render_layers::*; @@ -358,190 +359,6 @@ fn propagate_recursive( Ok(()) } -fn propagate_render_groups( - mut updated_entities: Local, - mut commands: Commands, - // Query for all propagators with changed PropagateRenderGroups. - // This does a 'full propagation' to push changes all the way down the tree. - changed_propagate_cameras_query: Query< - (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), - Changed - >, - // Query for camera propagator entities with changed CameraView. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - changed_view_cameras_query: Query< - (Entity, &CameraView, Option<&RenderGroups>, &PropagateRenderGroups), - (With, Changed), - >, - // Tracker to identify entities that lost CameraView. - mut removed_cameraview: RemovedComponents, - // Query for all propagators with removed CameraView. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - removed_cameraview_propagator_query: Query< - (Entity, Option<&RenderGroups>, Option<&Camera>, &PropagateRenderGroups), - Without - >, - // Query for all propagator entities with updated RenderGroups. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - changed_rendergroups_propagator_query: Query< - (Entity, &RenderGroups, &PropagateRenderGroups), - (Changed) - >, - // Tracker to identify entities that lost RenderGroups. - mut removed_rendergroups: RemovedComponents, - // Query for propagator entities with removed RenderGroups. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - removed_rendergroups_propagator_query: Query< - (Entity, Option<&CameraView>, Option<&Camera>, &PropagateRenderGroups), - Without - >, - // Query for all propagators with changed children. - // This does a 'propagator enity propagation' to push changes to only descendents with different propagater - // entities. - changed_children_propagator_query: Query< - (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), - Changed, - >, - // Tracker to identify entities that lost PropagateRenderGroups. - // These entities and their children will be 'repropagated' from the lost entities' parents. - // - Takes into account where the entity has a parent. - // - Takes into account whether the parent is a propagator or non-propagater. - mut removed_propagate: RemovedComponents, - removed_propagate_entities: Query<&Children, Without>, - // Query for entities with InheritedRenderGroups who gained a new parent. - // - Ignores entities whose parents have PropagateRenderGroups, since that case is handled by - // changed_children_propagator_query. - // - If the parent doesn't have InheritedRenderGroups, then the entity and its children will be 'unpropagated'. - // - If the parent does have InheritedRenderGroups, then propagation will be applied to the entity and - // its children. - // This does a 'propagator enity propagation' to push changes to only descendents with different propagater - // entities. - changed_parents_query: Query< - (Entity, Option<&Children>), - (Changed, With, Without) - >, - // Query for entities with InheritedRenderGroups whose children changed. - // - Ignores children with InheritedRenderGroups, those are handled by changed_parents_query. - // This does a 'propagator enity propagation' to push changes to only descendents with different propagater - // entities. - changed_children_query: Query< - (Entity, &Children), - (Changed, With, Without) - >, - // Query for non-propagator entities with updated RenderGroups. - // This updates the entity's InheritedRenderGroups. - changed_rendergroups_query: Query< - (Entity, &RenderGroups), - (Changed, Without) - >, - // Query for non-propagator entities with InheritedRenderGroups and removed RenderGroups. - // This updates the entity's InheritedRenderGroups. - removed_rendergroups_query: Query< - Entity, - (With, Without) - >, - // Query for entities with PropagateRenderGroups that InheritedRenderGroups needs to be removed from. - dirty_propagators: Query, With)>, - // Query for getting Children. - children_query: Query<&Children>, - // Query for entities that propagate. - // Used when pulling inheritance information from the parent. - propagators: Query< - (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups) - >, - // Query for entities that inherited and don't propagate. - // Used when pulling inheritance information from the parent. - nonpropagators: Query<(), (With, Without)>, - // Query for updating InheritedRenderGroups on entities. - mut maybe_inherited_query: Query<(Entity, Option<&RenderGroups>, Option<&mut InheritedRenderGroups>)>, -) { - - let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); - - let mut propagated = if let Some(propagate) = maybe_propagate_groups { - Some(propagate.get_from_camera(entity, camera_view)) - } else { - None - }; - - - // Track updated entities to prevent redundant updates, as `Commands` changes are deferred. - // - // Using this ensures that once mutations to entities with PropagateRenderGroups have been - // propagated, all entities affected by those changes won't be mutated again. This makes it - // safe to read parent InheritedRenderGroups (in the other cases that need to be handled) in - // order to 'pull in' inherited changes when needed. - updated_entities.clear(); - - // Assuming that TargetCamera is manually set on the root node only, - // update root nodes first, since it implies the biggest change - for (root_node, target_camera) in &changed_root_nodes_query { - update_children_render_groups( - root_node, - target_camera, - &node_query, - &children_query, - &mut commands, - &mut updated_entities, - ); - } - - // If the root node TargetCamera was changed, then every child is updated - // by this point, and iteration will be skipped. - // Otherwise, update changed children - for (parent, target_camera) in &changed_children_query { - update_children_render_groups( - parent, - target_camera, - &node_query, - &children_query, - &mut commands, - &mut updated_entities, - ); - } -} - -fn update_children_render_groups( - entity: Entity, - camera_to_set: Option<&TargetCamera>, - node_query: &Query, With>, - children_query: &Query<&Children, With>, - commands: &mut Commands, - updated_entities: &mut HashSet, -) { - let Ok(children) = children_query.get(entity) else { - return; - }; - - for &child in children { - // Skip if the child has already been updated or update is not needed - if updated_entities.contains(&child) - || camera_to_set == node_query.get(child).ok().flatten() - { - continue; - } - - match camera_to_set { - Some(camera) => { - commands.entity(child).insert(camera.clone()); - } - None => { - commands.entity(child).remove::(); - } - } - updated_entities.insert(child); - - update_children_render_groups( - child, - camera_to_set, - node_query, - children_query, - commands, - updated_entities, - ); - } -} - /// Resets the view visibility of every entity. /// Entities that are visible will be marked as such later this frame /// by a [`VisibilitySystems::CheckVisibility`] system. diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs new file mode 100644 index 0000000000000..672bf1c8c2663 --- /dev/null +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -0,0 +1,736 @@ + +/// Returned by [`PropagateRenderGroups::get_render_groups`]. +pub enum PropagatingRenderGroups<'a> { + Ref(&'a RenderGroups), + Val(RenderGroups), +} + +impl<'a> PropagatingRenderGroups<'a> { + /// Gets a reference to the internal [`RenderGroups`]. + pub fn get(&self) -> &RenderGroups { + match self { + Self::Ref(groups) => groups, + Self::Val(groups) => &groups, + } + } +} + +/// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. +/// +/// Entities with this component will ignore [`RenderGroups`] propagated by parents. +/// +/// See [`RenderGroups`] and [`CameraView`]. +#[derive(Component)] +pub enum PropagateRenderGroups +{ + /// If the entity has a [`RenderGroups`] component, that value is propagated, otherwise a default + /// [`RenderGroups`] is propagated. + /// + /// Note that it is allowed to add a [`RenderGroup`] component to a camera. + Auto, + /// If the entity has a [`Camera`] component, propagates `RenderGroups::new_with_camera(entity)`. + /// + /// Otherwise a warning will be logged and an empty [`RenderGroups`] will be propagated. + Camera, + /// If the entity has a [`Camera`] component and a [`CameraView`] component, propagates + /// `CameraView::get_groups(entity)`. + /// + /// Otherwise a warning will be logged and an empty [`RenderGroups`] will be propagated. + CameraWithView, + /// Propagates a custom [`RenderGroups`]. + Custom(RenderGroups), +} + +impl PropagateRenderGroups { + pub fn get_render_groups( + &self, + entity: Entity, + groups: Option<&RenderGroups>, + view: Option<&CameraView>, + is_camera: bool, + ) -> PropagatingRenderGroups<'_> { + match self { + Self::Auto => + { + let Some(groups) = groups else { + return PropagatingRenderGroups::Val(RenderGroups::default()); + }; + PropagatingRenderGroups::Ref(groups) + } + Self::Camera => + { + if !is_camera { + warn!("failed propagating PropagateRenderGroups::Camera, {entity} doesn't have a camera"); + PropagatingRenderGroups::Val(RenderGroups::empty()); + }; + PropagatingRenderGroups::Val(RenderGroups::new_with_camera(entity)) + } + Self::CameraWithView => + { + if !is_camera { + warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); + PropagatingRenderGroups::Val(RenderGroups::empty()); + }; + let view = view.unwrap_or(&CameraView::empty()); + PropagatingRenderGroups::Val(view.get_groups(entity)) + } + Self::Custom(groups) => + { + PropagatingRenderGroups::Ref(groups) + } + } + } +} + +/// Component on an entity that stores the result of merging the entity's [`RenderGroups`] +/// component with the [`RenderGroups`] of an entity propagated by the entity's parent. +/// +/// See [`PropagateRenderGroups`]. +/// +/// This is automatically updated in [`PostUpdate`] in the [`VisibilityPropagate`] set. +/// The component will be automatically added or removed depending on if it is needed. +/// +/// ### Merge details +/// +/// The merge direction is 'entity_rendergroups.merge(propagated_rendergroups)` +/// (see [`RenderGroups::merge`]). +/// This means the entity's affiliated camera will be prioritized over the propagated affiliated camera. +#[derive(Component, Debug, Clone)] +pub struct InheritedRenderGroups +{ + /// The entity that propagated a [`RenderGroups`] to this entity. + /// + /// This is cached so children of this entity can update themselves without needing to traverse the + /// entire hierarchy. + pub propagater: Entity, + /// The [`RenderGroups`] computed by merging the [`RenderGroups`] of the `Self::propagater` entity into + /// the node's [`RenderGroups`] component. + /// + /// This is cached for efficient access in the [`check_visibility`] system. + pub computed: RenderGroups, +}; + +/* +Notes on propagation algorithm for PropagateRenderGroups. + +## Contributing factors + +Dimensions: +- Conflict: InheritedRenderGroups and PropagateRenderGroups on the same entity +- Add/remove/change RenderGroups +- Add/remove/change CameraView +- Add/remove/change PropagateRenderGroups +- Add/remove Camera +- Gain parent with PropagateRenderGroups +- Gain parent without PropagateRenderGroups + - Entity with InheritedRenderGroups + - Entity without InheritedRenderGroups +- Become un-parented + +Actions: +- Add/remove/change InheritedRenderGroups + - When updating an entity, always compute a fresh InheritedRenderGroups::computed value. + + +## Propagation algorithm + +These steps must be done in order, and they are computed 'accumulatively', which means once +an entity has been updated it won't be updated again (unless explicitly specified). + +### Propagators + +- If an entity has both InheritedRenderGroups and PropagateRenderGroups, then remove its InheritedRenderGroups. + - SYSTEM: clean_propagators +- If a propagator has an updated or new propagate-value, then propagate it. Stop propagating only if another propagator +is encountered. + - SYSTEM: propagate_updated_propagators + - Possible change sources: RenderGroups (add/remove/change), CameraView (add/remove/change), Camera (add/remove), + PropagateRenderGroups (add/change). +- If a propagator gains non-propagator children, then propagate it to the new children. Stop propagating if the +propagator entity is already known to a descendent (do not mark those descendents as updated), or if another +propagator is encountered. + - SYSTEM: propagate_to_new_children + - PROBLEM: If a new entity is inserted between a propagator and another entity, then its parent will run in this + step, and its non-propagator children will run in "If a non-propagator entity is parented to a non-propagator". + This step will insert an InheritedRenderGroups component to the new entity, but it *won't* be added to the + pre-existing entity which already records the propagator. When the pre-eisting entity gets updated because of + being parented to a non-propagator, the parent's InheritedRenderGroups component won't be available since its + insertion is deferred. + - SOLUTION: See "If a non-propagator entity is parented to a non-propagator". + +### Non-Propagators + +- If a non-propagator entity with InheritedRenderGroups is un-parented, then remove InheritedRenderGroups from the +entity and its children (stopping at propagators). + - SYSTEM: handle_orphaned_nonpropagators + - Iterate all children even if one without InheritedRenderGroups is encountered. This ensures 'gaps' caused + by hierarchy changes won't cause problems. For example, an entity without InheritedRenderGroups parented to a + descendent of an un-parented entity will want to pull inheritance from its parent, but removal of + InheritedRenderGroups is deferred so the component it would access would be stale. +- If an entity loses a PropagateRenderGroups component, then inherit its parent's propagator entity, and propagate +that to its own children (stopping when a propagator is encountered). If the parent isn't a propagator and doesn't +have InheritedRenderGroups (or there is no parent), then remove InheritedRenderGroups from the entity and its +children (stopping at propagators). Skip already-updated entities. + - PROBLEM: What if multiple entities in a hierarchy lose PropagateRenderGroups components? + - SOLUTION: Ignore the updated_entities cache and force-update children even if they were previously updated. + This will cause some redundant work, but gives the correct result. + - If a child is marked as updated, then always insert/remove InheritedRenderGroups components to match + the desired policy (regardless of it the entity has InheritedRenderGroups), in case previous updates + of this type need to be overwritten. +- If a non-propagator entity is parented to a non-propagator, then propagate the parent's InheritedRenderGroups +propagator entity (stopping at propagators). If the parent doesn't have +InheritedRenderGroups, then remove InheritedRenderGroups from the entity and its children (stopping at propagators). +Skip already-updated entities. + - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator + stays the same (see "If a propagator gains non-propagator children"). In that case, do not mark the entity updated + and simply skip it. + - Force-update children for the same reason as "If an entity loses a PropagateRenderGroups component". +- If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute +its InheritedRenderGroups::computed field. Skip already-updated entities. +*/ + +//todo: insert resource +#[derive(Resource, Default, Deref, DerefMut)] +struct PropagateRenderGroupsEntityCache(EntityHashMap); + +/// Removes InheritedRenderGroups from entities with PropagateRenderGroups. +fn clean_propagators( + mut commands: Commands, + dirty_propagators: Query, With)> +){ + for dirty in dirty_propagators.iter() { + commands.get_entity(dirty).map(|e| e.remove::()) + } +} + +/// Propagates propagation values that have changed. +//todo: Detect if the propagated value has actually changed? Hard to expect this would matter in practice. +fn propagate_updated_propagators( + mut commands: Commands, + mut updated_entities: ResMut, + // Detect added/changed: RenderGroups, CameraView, Camera (added only), PropagateRenderGroups. + changed_propagators: Query< + ( + Entity, + &Children, + Option<&RenderGroups>, + Option<&CameraView>, + Option<&Camera>, + &PropagateRenderGroups, + ), + (Or<( + Changed, + Changed, + Added, + Changed, + )>) + >, + // Detect removed: RenderGroups, CameraView, Camera. + mut removed_rendergroups: RemovedComponents, + mut removed_cameraview: RemovedComponents, + mut removed_camera: RemovedComponents, + all_propagators: Query< + ( + Entity, + &Children, + Option<&RenderGroups>, + Option<&CameraView>, + Option<&Camera>, + &PropagateRenderGroups, + ) + >, + // Query for getting Children. + children_query: Query<&Children>, + // Query for updating InheritedRenderGroups on non-propagator entities. + mut maybe_inherited: Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, +) { + // Track updated entities to prevent redundant updates, as `Commands` changes are deferred. + // + // Using this ensures that once mutations to entities with PropagateRenderGroups have been + // propagated, all entities affected by those changes won't be mutated again. This makes it + // safe to read parent InheritedRenderGroups (in the other cases that need to be handled) in + // order to 'pull in' inherited changes when needed. See algorithm description for more details. + updated_entities.clear(); + + // Collect aggregate iterator for all propagators that need to propagate. + let mut propagators = changed_propagators.iter() + .chain( + // IMPORTANT: Removals should be ordered first if propagate_to_new_children is merged + // into changed_propagators. + removed_rendergroups.read() + .chain(removed_cameraview.read()) + .chain(removed_camera.read()) + .filter_map(|e| all_propagators.get(*e).ok()) + ); + + // Propagate each propagator. + for ( + propagator, + children, + maybe_render_groups, + maybe_camera_view, + maybe_camera, + propagate, + ) in propagators { + // There can be duplicates due to component removals. + if updated_entities.contains(&propagator) { + continue; + } + + // Get value to propagate. + // Note: This can allocate spuriously if there are no children that need it. + let propagated = propagate.get_render_groups( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera.is_some() + ); + + // Propagate + updated_entities.insert(propagator); + + for child in children.iter() { + apply_full_propagation( + &mut commands, + &mut updated_entities, + &children_query, + &mut maybe_inherited, + propagator, + propagated, + child + ); + } + } +} + +/// Applies propagation to entities for `apply_full_propagation`. +// Note: this does not require checking updated_entities because all children will be fresh. +fn apply_full_propagation( + commands: &mut Commands, + updated_entities: &mut PropagateRenderGroupsEntityCache, + children_query: &Query<&Children>, + maybe_inherited: &mut Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, + propagator: Entity, + propagated: &RenderGroups, + child: Entity, +) { + // Leave if child doesn't exist or has PropagateRenderGroups. + let Ok(maybe_render_groups, mut maybe_inherited_groups) = maybe_inherited.get_mut(child) else { + return; + } + + // Update inherited value or insert a new one. + let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); + let apply_changes = |groups: &mut InheritedRenderGroups| { + groups.propagator = propagator; + groups.computed.set_from(initial_groups); + groups.computed.merge(propagated); + }; + + if let Some(mut inherited) = maybe_inherited_groups { + apply_changes(&mut inherited); + } else { + let mut new = InheritedRenderGroups::empty(); + apply_changes(&mut new); + commands.get(child).insert(new); + } + + // Mark as updated. + updated_entities.insert(child); + + // Continue propagation to children. + let Ok(children) = children_query.get(child) else { + return; + }; + for child in children.iter() { + apply_full_propagation( + commands, + updated_entities, + children_query, + maybe_inherited, + propagator, + propagated, + child + ); + } +} + +/// Propagates propagation values to new children of a propagator. +//todo: Can be merged with apply_full_propagation at the cost of code density. Must make sure iterating +// this comes *after* iterating removals, because this step must come *after* all propagation value changes +// are handled. +fn propagate_to_new_children( + mut commands: Commands, + mut updated_entities: ResMut, + // Changed children. + changed_children: Query< + ( + Entity, + &Children, + Option<&RenderGroups>, + Option<&CameraView>, + Option<&Camera>, + &PropagateRenderGroups, + ), + Changed, + >, + // Query for getting Children. + children_query: Query<&Children>, + // Query for updating InheritedRenderGroups on non-propagator entities. + mut maybe_inherited: Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, +) { + // Propagate each propagator that has new children. + for ( + propagator, + children, + maybe_render_groups, + maybe_camera_view, + maybe_camera, + propagate, + ) in changed_children.iter() { + // The propagator could have been updated in a previous step. + if updated_entities.contains(&propagator) { + continue; + } + + // Get value to propagate. + // Note: This can allocate spuriously if there are no children that need it. + let propagated = propagate.get_render_groups( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera.is_some() + ); + + // Propagate + updated_entities.insert(propagator); + + for child in children.iter() { + apply_new_children_propagation( + &mut commands, + &mut updated_entities, + &children_query, + &mut maybe_inherited, + propagator, + propagated, + child + ); + } + } +} + +/// Applies propagation to entities for `propagate_to_new_children`. +// Note: this does not require checking updated_entities because all children will be fresh. +fn apply_new_children_propagation( + commands: &mut Commands, + updated_entities: &mut PropagateRenderGroupsEntityCache, + children_query: &Query<&Children>, + maybe_inherited: &mut Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, + propagator: Entity, + propagated: &RenderGroups, + child: Entity, +) { + // Leave if child doesn't exist or has PropagateRenderGroups. + let Ok(maybe_render_groups, mut maybe_inherited_groups) = maybe_inherited.get_mut(child) else { + return; + } + + // Leave if the propagator is already known (implying this is a pre-existing child). + if maybe_inherited_groups.map(|i| i.propagator == propagator).unwrap_or(false) { + return; + } + + // Update inherited value or insert a new one. + let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); + let apply_changes = |groups: &mut InheritedRenderGroups| { + groups.propagator = propagator; + groups.computed.set_from(initial_groups); + groups.computed.merge(propagated); + }; + + if let Some(mut inherited) = maybe_inherited_groups { + apply_changes(&mut inherited); + } else { + let mut new = InheritedRenderGroups::empty(); + apply_changes(&mut new); + commands.get(child).insert(new); + } + + // Mark as updated. + updated_entities.insert(child); + + // Continue propagation to children. + let Ok(children) = children_query.get(child) else { + return; + }; + for child in children.iter() { + apply_new_children_propagation( + commands, + updated_entities, + children_query, + maybe_inherited, + propagator, + propagated, + child + ); + } +} + +/// Removes InheritedRenderGroups from orphaned branches of the hierarchy. +fn handle_orphaned_nonpropagators( + mut commands: Commands, + mut updated_entities: ResMut, + // Orphaned non-propagator entities that previously had InheritedRenderGroups. + mut removed_parents: RemovedComponents, + orphaned: Query< + (Entity, Option<&Children>), + (Without, With, Without), + >, + // Query for getting non-propagator children. + nonpropagators: Query, Without>, +) { + for (orphan, maybe_children) in removed_parents.read().filter_map(|r| orphaned.get(r)) { + apply_orphan_cleanup(&mut commands, &mut updated_entities, &nonpropagators, orphan, maybe_children); + } +} + +/// Applies propagation for `handle_orphaned_nonpropagators`. +fn apply_orphan_cleanup( + commands: &mut Commands, + updated_entities: &mut PropagateRenderGroupsEntityCache, + nonpropagators: &Query, Without>, + entity: Entity, + maybe_children: Option<&Children>, +){ + // Remove InheritedRenderGroups. + commands.get_entity(entity).map(|e| e.remove::()); + + // Mark as updated. + updated_entities.insert(entity); + + // Update non-propagator children. + let Some(children) = maybe_children else { + continue; + }; + for child in children.iter() { + // Ignore children that have PropagateRenderGroups. + let Ok(maybe_children) = nonpropagators.get(child) else { + continue; + }; + + apply_orphan_cleanup(commands, updated_entities, nonpropagators, child, maybe_children); + } +} + + +/* + +fn propagate_render_groups_updated_propagators( + mut updated_entities: ResMut, + mut commands: Commands, + // Query for entities with PropagateRenderGroups that InheritedRenderGroups needs to be removed from. + dirty_propagators: Query, With)>, + // Query for all propagators with changed PropagateRenderGroups. + // This does a 'full propagation' to push changes all the way down the tree. + changed_propagate_cameras_query: Query< + (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), + Changed + >, + // Query for camera propagator entities with changed CameraView. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + changed_view_cameras_query: Query< + (Entity, &CameraView, Option<&RenderGroups>, &PropagateRenderGroups), + (With, Changed), + >, + // Tracker to identify entities that lost CameraView. + mut removed_cameraview: RemovedComponents, + // Query for all propagators with removed CameraView. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + removed_cameraview_propagator_query: Query< + (Entity, Option<&RenderGroups>, Option<&Camera>, &PropagateRenderGroups), + Without + >, + // Query for all propagator entities with updated RenderGroups. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + changed_rendergroups_propagator_query: Query< + (Entity, &RenderGroups, &PropagateRenderGroups), + (Changed) + >, + // Tracker to identify entities that lost RenderGroups. + mut removed_rendergroups: RemovedComponents, + // Query for propagator entities with removed RenderGroups. + // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. + removed_rendergroups_propagator_query: Query< + (Entity, Option<&CameraView>, Option<&Camera>, &PropagateRenderGroups), + Without + >, + + // Query for getting Children. + children_query: Query<&Children>, + // Query for updating InheritedRenderGroups on entities. + mut maybe_inherited_query: Query<(Entity, Option<&RenderGroups>, Option<&mut InheritedRenderGroups>)>, +) { + // Track updated entities to prevent redundant updates, as `Commands` changes are deferred. + // + // Using this ensures that once mutations to entities with PropagateRenderGroups have been + // propagated, all entities affected by those changes won't be mutated again. This makes it + // safe to read parent InheritedRenderGroups (in the other cases that need to be handled) in + // order to 'pull in' inherited changes when needed. + updated_entities.clear(); +} + +//todo: chain after propagate_render_groups_full +fn propagate_render_groups_targeted( + mut updated_entities: ResMut, + mut commands: Commands, + // Query for all propagators with changed children. + // This does a 'propagator enity propagation' to push changes to only descendents with different propagator + // entities. + changed_children_propagator_query: Query< + (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), + Changed, + >, + // Tracker to identify entities that lost PropagateRenderGroups. + // - Takes into account where the entity has a parent. + // - Takes into account whether the parent is a propagator or non-propagator. + // These entities and their children will be 'repropagated' from the lost entities' parents. + mut removed_propagate: RemovedComponents, + removed_propagate_entities: Query<&Children, Without>, + // Query for entities with InheritedRenderGroups who gained a new parent. + // - Ignores entities whose parents have PropagateRenderGroups, since that case is handled by + // changed_children_propagator_query. + // - If the parent doesn't have InheritedRenderGroups, then the entity and its children will be 'unpropagated'. + // - If the parent does have InheritedRenderGroups, then propagation will be applied to the entity and + // its children. + // This does a 'propagator enity propagation' to push changes to only descendents with different propagator + // entities. + changed_parents_query: Query< + (Entity, Option<&Children>), + (Changed, With, Without) + >, + // Query for entities with InheritedRenderGroups whose children changed. + // - Ignores children with InheritedRenderGroups, those are handled by changed_parents_query. + // This does a 'propagator enity propagation' to push changes to only descendents with different propagator + // entities. + changed_children_query: Query< + (Entity, &Children), + (Changed, With, Without) + >, + // Query for non-propagator entities with updated RenderGroups. + // This updates the entity's InheritedRenderGroups. + changed_rendergroups_query: Query< + (Entity, &RenderGroups), + (Changed, Without) + >, + // Tracker to identify entities that lost RenderGroups. + mut removed_rendergroups: RemovedComponents, + // Query for non-propagator entities with InheritedRenderGroups and removed RenderGroups. + // This updates the entity's InheritedRenderGroups. + removed_rendergroups_query: Query< + Entity, + (With, Without) + >, + + // Query for getting Children. + children_query: Query<&Children>, + // Query for entities that propagate. + // Used when pulling inheritance information from the parent. + propagators: Query< + (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups) + >, + // Query for entities that inherited and don't propagate. + // Used when pulling inheritance information from the parent. + nonpropagators: Query<(), (With, Without)>, + // Query for updating InheritedRenderGroups on entities. + mut maybe_inherited_query: Query<(Entity, Option<&RenderGroups>, Option<&mut InheritedRenderGroups>)>, +) { + + let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); + + let mut propagated = if let Some(propagate) = maybe_propagate_groups { + Some(propagate.get_from_camera(entity, camera_view)) + } else { + None + }; + + // Assuming that TargetCamera is manually set on the root node only, + // update root nodes first, since it implies the biggest change + for (root_node, target_camera) in &changed_root_nodes_query { + update_children_render_groups( + root_node, + target_camera, + &node_query, + &children_query, + &mut commands, + &mut updated_entities, + ); + } + + // If the root node TargetCamera was changed, then every child is updated + // by this point, and iteration will be skipped. + // Otherwise, update changed children + for (parent, target_camera) in &changed_children_query { + update_children_render_groups( + parent, + target_camera, + &node_query, + &children_query, + &mut commands, + &mut updated_entities, + ); + } +} + +fn update_children_render_groups( + updated_entities: &mut HashSet, + entity: Entity, + camera_to_set: Option<&TargetCamera>, + node_query: &Query, With>, + children_query: &Query<&Children, With>, + commands: &mut Commands, +) { + let Ok(children) = children_query.get(entity) else { + return; + }; + + for &child in children { + // Skip if the child has already been updated or update is not needed + if updated_entities.contains(&child) + || camera_to_set == node_query.get(child).ok().flatten() + { + continue; + } + + match camera_to_set { + Some(camera) => { + commands.entity(child).insert(camera.clone()); + } + None => { + commands.entity(child).remove::(); + } + } + updated_entities.insert(child); + + update_children_render_groups( + child, + camera_to_set, + node_query, + children_query, + commands, + updated_entities, + ); + } +} +*/ diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index b27d9aec3589a..bc9cb40b8dd48 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -231,6 +231,10 @@ let mut groups = RenderGroups::from(0); groups.add(RenderLayer(1); entity.insert(groups); ``` +/// +/// Similarly, if an entity without [`RenderGroups`] inherits from an entity with [`PropagateRenderGroups`] that +/// doesn't propagate layer 0, then the entity's computed [`InheritedRenderGroups`] won't have layer 0 and the +/// entity won't be visible to layer 0. */ #[derive(Component, Debug, Clone)] pub struct RenderGroups @@ -357,34 +361,6 @@ impl Default for RenderGroups { } } -/// Component on an entity that stores the result of merging the entity's [`RenderGroups`] -/// component with the [`RenderGroups`] of an entity propagated by the entity's parent. -/// -/// See [`PropagateRenderGroups`]. -/// -/// This is automatically updated in [`PostUpdate`] in the [`VisibilityPropagate`] set. -/// The component will be automatically added or removed depending on if it is needed. -/// -/// ### Merge details -/// -/// The merge direction is 'entity_rendergroups.merge(propagated_rendergroups)` -/// (see [`RenderGroups::merge`]). -/// This means the entity's affiliated camera will be prioritized over the propagated affiliated camera. -#[derive(Component, Debug, Clone)] -pub struct InheritedRenderGroups -{ - /// The entity that propagated [`RenderGroups`] to this entity. - /// - /// This is cached so children of this entity can update themselves without needing to traverse the - /// entire hierarchy. - pub propagater: Entity, - /// The [`RenderGroups`] computed by merging the [`RenderGroups`] of the `Self::propagater` entity into - /// the node's [`RenderGroups`] component. - /// - /// This is cached for efficient access in the [`check_visibility`] system. - pub computed: RenderGroups, -}; - /// Component on camera entities that controls which [`RenderLayers`] are visible to /// the camera. /// @@ -478,33 +454,6 @@ impl Default for CameraView { } } -/// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. -/// -/// Entities with this component will ignore [`RenderGroups`] propagated by parents. -/// -/// See [`RenderGroups`] and [`CameraView`]. -#[derive(Component)] -pub enum PropagateRenderGroups -{ - /// If the entity has a [`RenderGroups`] component, that value is propagated. - /// - /// Note that it is allowed to add a [`RenderGroup`] component to a camera. - /// - /// Otherwise nothing is propagated and no errors are logged. - Auto, - /// If the entity has a [`Camera`] component, propagates `RenderGroups::new_with_camera(entity)`. - /// - /// Otherwise an error will be logged. - Camera, - /// If the entity has a [`Camera`] component and a [`CameraView`] component, propagates - /// `CameraView::get_groups(entity)`. - /// - /// Otherwise an error will be logged. - CameraWithView, - /// Propagates a custom [`RenderGroups`]. - Custom(RenderGroups), -} - #[cfg(test)] mod rendering_mask_tests { use super::{RenderLayer, RenderLayers}; From 1b7c495dfb601e08fc9b7ff75d635105a2f8dc03 Mon Sep 17 00:00:00 2001 From: koe Date: Sun, 17 Mar 2024 18:52:47 -0500 Subject: [PATCH 05/54] propagation algorithm WIP --- .../visibility/propagate_render_groups.rs | 454 ++++++++++++++---- 1 file changed, 356 insertions(+), 98 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 672bf1c8c2663..ff0d49c778ff7 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -1,3 +1,114 @@ +/* +# Notes on propagation algorithm for PropagateRenderGroups + +The propagation algorithm is in control of updating InheritedRenderGroups on the descendents of +entities with PropagateRenderGroups. It must take into account changes in propagated values, +changes in RenderGroups on descendents, the possibility of entities gaining/losing PropagateRenderGroups, +and all potential hierarchy mutations (which can occur simultaneously with the other factors). + +At the same time, the algorithm must be efficient. It should not update entities more than once (unless +necessary), and it should not update entities that don't need to be updated. + +These goals are achieved with a sequence of update-handling steps that are logically accumulative. As much +as possible, each step covers only update-reasons not covered by previous steps. Note that most steps include +recursion for passing updates down hierarchies, however each step has slightly different propagation rules as +described below. + + +## Contributing factors + +Dimensions: +- InheritedRenderGroups and PropagateRenderGroups should not exist on the same entity +- Add/remove/change RenderGroups +- Add/remove/change CameraView +- Add/remove/change PropagateRenderGroups +- Add/remove Camera +- Gain parent with PropagateRenderGroups +- Gain parent without PropagateRenderGroups + - Entity with InheritedRenderGroups + - Entity without InheritedRenderGroups +- Become un-parented + +Actions: +- Add/remove/change InheritedRenderGroups + - When updating an entity, always compute a fresh InheritedRenderGroups::computed value. + + +## Propagation algorithm + +These steps must be done in order, and they are computed 'accumulatively', which means once +an entity has been updated it won't be updated again (unless explicitly specified). + +We take note of a couple 'problems' that are challenging edge cases, and how they are handled. + +### Propagators + +- If an entity has both InheritedRenderGroups and PropagateRenderGroups, then remove its InheritedRenderGroups. + - SYSTEM: clean_propagators +- If a propagator has an updated or new propagate-value, then propagate it. Stop propagating only if another propagator +is encountered. + - SYSTEM: propagate_updated_propagators + - Possible change sources: RenderGroups (add/remove/change), CameraView (add/remove/change), Camera (add/remove), + PropagateRenderGroups (add/change). +- If a propagator gains non-propagator children, then propagate it to the new children. Stop propagating if the +propagator entity is already known to a descendent (do not mark those descendents as updated), or if another +propagator is encountered. + - SYSTEM: propagate_to_new_children + - PROBLEM: If a new entity is inserted between a propagator and another entity, then the propagator will run in + this step, and the new entity's non-propagator children will run in "If a non-propagator entity is parented to a + non-propagator". This step will insert an InheritedRenderGroups component to the new entity, but it *won't* be + added to the pre-existing entity which already records the propagator. When the pre-eisting entity gets updated + because of being parented to a non-propagator, the parent's InheritedRenderGroups component won't be available + since its insertion is deferred. + - SOLUTION: See "If a non-propagator entity is parented to a non-propagator". + +### Non-Propagators + +- If a non-propagator entity with InheritedRenderGroups is un-parented, then remove InheritedRenderGroups from the +entity and its children (stopping at propagators). + - SYSTEM: handle_orphaned_nonpropagators + - Iterate all children even if one without InheritedRenderGroups is encountered. This ensures 'gaps' caused + by hierarchy changes won't cause problems. For example, an entity without InheritedRenderGroups parented to a + descendent of an un-parented entity will want to pull inheritance from its parent, but removal of + InheritedRenderGroups is deferred so the component it would access would be stale. +- If an entity loses a PropagateRenderGroups component, then inherit its parent's propagator entity, and propagate +that to its own children (stopping when a propagator is encountered). If the parent isn't a propagator and doesn't +have InheritedRenderGroups (or there is no parent), then remove InheritedRenderGroups from the entity and its +children (stopping at propagators). Skip already-updated entities that lost PropagateRenderGroups. + - SYSTEM: handle_lost_propagator + - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator + stays the same (see "If a propagator gains non-propagator children") (this can only happen if InheritedRenderGroups + was manually inserted by a user, since the entity would not have had InheritedRenderGroups in the previous tick + because we force-remove it if an entity has PropagateRenderGroups). In that case, instead of using the parent's + InheritedRenderGroups, recompute this entity's InheritedRenderGroups from its existing propagator (we assume it + is invalid/stale since the entity used to be a propagator). + - PROBLEM: What if multiple entities in a hierarchy lose PropagateRenderGroups components? + - SOLUTION: Ignore the updated_entities cache and force-update children even if they were previously updated. + This will cause some redundant work, but gives the correct result. + - If a child is marked as updated, then always insert/remove InheritedRenderGroups components to match + the desired policy (regardless of it the entity has InheritedRenderGroups), in case previous deferred + updates of this type need to be overwritten. +- If a non-propagator entity is parented to a non-propagator, then propagate the parent's InheritedRenderGroups +propagator entity (stopping at propagators). If the parent doesn't have +InheritedRenderGroups, then remove InheritedRenderGroups from the entity and its children (stopping at propagators). +Skip already-updated entities that were parented to a non-propagator. + - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator + stays the same (see "If a propagator gains non-propagator children"). In that case, do not mark the entity updated + and simply skip it. + - Force-update children for the same reason as "If an entity loses a PropagateRenderGroups component". +- If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute +its InheritedRenderGroups::computed field. Skip already-updated entities. + + +## Performance + +The base-line performance cost of this algorithm comes from iterating in order to detect changes. +- All entities with `Children` and `PropagateRenderGroups` are iterated twice (can potentially be reduced to once). +- `RemovedComponents` is iterated twice. +- `RemovedComponents` is iterated once. +- `RemovedComponents` is iterated once. +- `RemovedComponents` is iterated once. +*/ /// Returned by [`PropagateRenderGroups::get_render_groups`]. pub enum PropagatingRenderGroups<'a> { @@ -110,85 +221,6 @@ pub struct InheritedRenderGroups pub computed: RenderGroups, }; -/* -Notes on propagation algorithm for PropagateRenderGroups. - -## Contributing factors - -Dimensions: -- Conflict: InheritedRenderGroups and PropagateRenderGroups on the same entity -- Add/remove/change RenderGroups -- Add/remove/change CameraView -- Add/remove/change PropagateRenderGroups -- Add/remove Camera -- Gain parent with PropagateRenderGroups -- Gain parent without PropagateRenderGroups - - Entity with InheritedRenderGroups - - Entity without InheritedRenderGroups -- Become un-parented - -Actions: -- Add/remove/change InheritedRenderGroups - - When updating an entity, always compute a fresh InheritedRenderGroups::computed value. - - -## Propagation algorithm - -These steps must be done in order, and they are computed 'accumulatively', which means once -an entity has been updated it won't be updated again (unless explicitly specified). - -### Propagators - -- If an entity has both InheritedRenderGroups and PropagateRenderGroups, then remove its InheritedRenderGroups. - - SYSTEM: clean_propagators -- If a propagator has an updated or new propagate-value, then propagate it. Stop propagating only if another propagator -is encountered. - - SYSTEM: propagate_updated_propagators - - Possible change sources: RenderGroups (add/remove/change), CameraView (add/remove/change), Camera (add/remove), - PropagateRenderGroups (add/change). -- If a propagator gains non-propagator children, then propagate it to the new children. Stop propagating if the -propagator entity is already known to a descendent (do not mark those descendents as updated), or if another -propagator is encountered. - - SYSTEM: propagate_to_new_children - - PROBLEM: If a new entity is inserted between a propagator and another entity, then its parent will run in this - step, and its non-propagator children will run in "If a non-propagator entity is parented to a non-propagator". - This step will insert an InheritedRenderGroups component to the new entity, but it *won't* be added to the - pre-existing entity which already records the propagator. When the pre-eisting entity gets updated because of - being parented to a non-propagator, the parent's InheritedRenderGroups component won't be available since its - insertion is deferred. - - SOLUTION: See "If a non-propagator entity is parented to a non-propagator". - -### Non-Propagators - -- If a non-propagator entity with InheritedRenderGroups is un-parented, then remove InheritedRenderGroups from the -entity and its children (stopping at propagators). - - SYSTEM: handle_orphaned_nonpropagators - - Iterate all children even if one without InheritedRenderGroups is encountered. This ensures 'gaps' caused - by hierarchy changes won't cause problems. For example, an entity without InheritedRenderGroups parented to a - descendent of an un-parented entity will want to pull inheritance from its parent, but removal of - InheritedRenderGroups is deferred so the component it would access would be stale. -- If an entity loses a PropagateRenderGroups component, then inherit its parent's propagator entity, and propagate -that to its own children (stopping when a propagator is encountered). If the parent isn't a propagator and doesn't -have InheritedRenderGroups (or there is no parent), then remove InheritedRenderGroups from the entity and its -children (stopping at propagators). Skip already-updated entities. - - PROBLEM: What if multiple entities in a hierarchy lose PropagateRenderGroups components? - - SOLUTION: Ignore the updated_entities cache and force-update children even if they were previously updated. - This will cause some redundant work, but gives the correct result. - - If a child is marked as updated, then always insert/remove InheritedRenderGroups components to match - the desired policy (regardless of it the entity has InheritedRenderGroups), in case previous updates - of this type need to be overwritten. -- If a non-propagator entity is parented to a non-propagator, then propagate the parent's InheritedRenderGroups -propagator entity (stopping at propagators). If the parent doesn't have -InheritedRenderGroups, then remove InheritedRenderGroups from the entity and its children (stopping at propagators). -Skip already-updated entities. - - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator - stays the same (see "If a propagator gains non-propagator children"). In that case, do not mark the entity updated - and simply skip it. - - Force-update children for the same reason as "If an entity loses a PropagateRenderGroups component". -- If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute -its InheritedRenderGroups::computed field. Skip already-updated entities. -*/ - //todo: insert resource #[derive(Resource, Default, Deref, DerefMut)] struct PropagateRenderGroupsEntityCache(EntityHashMap); @@ -215,7 +247,7 @@ fn propagate_updated_propagators( &Children, Option<&RenderGroups>, Option<&CameraView>, - Option<&Camera>, + Has, &PropagateRenderGroups, ), (Or<( @@ -235,7 +267,7 @@ fn propagate_updated_propagators( &Children, Option<&RenderGroups>, Option<&CameraView>, - Option<&Camera>, + Has, &PropagateRenderGroups, ) >, @@ -286,7 +318,7 @@ fn propagate_updated_propagators( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera.is_some() + maybe_camera ); // Propagate @@ -307,7 +339,7 @@ fn propagate_updated_propagators( } /// Applies propagation to entities for `apply_full_propagation`. -// Note: this does not require checking updated_entities because all children will be fresh. +// Note: This does not require checking updated_entities because all children will be fresh. fn apply_full_propagation( commands: &mut Commands, updated_entities: &mut PropagateRenderGroupsEntityCache, @@ -318,10 +350,10 @@ fn apply_full_propagation( >, propagator: Entity, propagated: &RenderGroups, - child: Entity, + entity: Entity, ) { - // Leave if child doesn't exist or has PropagateRenderGroups. - let Ok(maybe_render_groups, mut maybe_inherited_groups) = maybe_inherited.get_mut(child) else { + // Leave if entity doesn't exist or has PropagateRenderGroups. + let Ok((maybe_render_groups, mut maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { return; } @@ -338,14 +370,14 @@ fn apply_full_propagation( } else { let mut new = InheritedRenderGroups::empty(); apply_changes(&mut new); - commands.get(child).insert(new); + commands.get(entity).insert(new); } // Mark as updated. - updated_entities.insert(child); + updated_entities.insert(entity); // Continue propagation to children. - let Ok(children) = children_query.get(child) else { + let Ok(children) = children_query.get(entity) else { return; }; for child in children.iter() { @@ -375,7 +407,7 @@ fn propagate_to_new_children( &Children, Option<&RenderGroups>, Option<&CameraView>, - Option<&Camera>, + Has, &PropagateRenderGroups, ), Changed, @@ -408,7 +440,7 @@ fn propagate_to_new_children( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera.is_some() + maybe_camera ); // Propagate @@ -429,7 +461,7 @@ fn propagate_to_new_children( } /// Applies propagation to entities for `propagate_to_new_children`. -// Note: this does not require checking updated_entities because all children will be fresh. +// Note: This does not require checking updated_entities because all children will be fresh. fn apply_new_children_propagation( commands: &mut Commands, updated_entities: &mut PropagateRenderGroupsEntityCache, @@ -440,10 +472,10 @@ fn apply_new_children_propagation( >, propagator: Entity, propagated: &RenderGroups, - child: Entity, + entity: Entity, ) { - // Leave if child doesn't exist or has PropagateRenderGroups. - let Ok(maybe_render_groups, mut maybe_inherited_groups) = maybe_inherited.get_mut(child) else { + // Leave if entity doesn't exist or has PropagateRenderGroups. + let Ok((maybe_render_groups, mut maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { return; } @@ -465,14 +497,14 @@ fn apply_new_children_propagation( } else { let mut new = InheritedRenderGroups::empty(); apply_changes(&mut new); - commands.get(child).insert(new); + commands.get(entity).insert(new); } // Mark as updated. - updated_entities.insert(child); + updated_entities.insert(entity); // Continue propagation to children. - let Ok(children) = children_query.get(child) else { + let Ok(children) = children_query.get(entity) else { return; }; for child in children.iter() { @@ -534,6 +566,232 @@ fn apply_orphan_cleanup( } } +/// Handles entities that lost the PropagateRenderGroups component. +fn handle_lost_propagator( + mut commands: Commands, + mut updated_entities: ResMut, + // Entities that lost PropagateRenderGroups + mut removed_propagate: RemovedComponents, + unpropagated: Query<(Entity, Option<&Parent>), Without>, + // Query for accessing propagators + all_propagators: Query< + ( + Entity, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + ) + >, + // Query for getting Children. + children_query: Query<&Children>, + // Query for updating InheritedRenderGroups on non-propagator entities. + mut maybe_inherited: Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, +) { + for (entity, maybe_parent) in removed_propagate.read().filter_map(|r| unpropagated.get(r)) { + // Skip already-updated entities. + if updated_entities.contains(&entity) { + continue; + } + + // Apply propagation. + + // Case 1: no parent + // - Policy: remove all + + // Case 2: parent is a non-propagator without InheritedRenderGroups (not marked updated) + // - Policy: remove all + + // Case 3: parent is a non-propagator with InheritedRenderGroups (not marked updated) + // - Subcase 1: Parent's propagator doesn't exit + // - Policy: remove all (note: this is not an error, because the propagation step to hendle it may not + // have executed yet) + // - Subcase 2: Parent's propagator exists + // - Policy: Compute propagation value from parent's propagator + + // Case 4: parent is a non-propagator marked updated + // - Subcase 1: Self doesn't have InheritedRenderGroups + // - Policy: remove all + // - Subcase 2: Propagator stored in self's InheritedRenderGroups doesn't exist + // - Policy: remove all + // - Subcase 3: Recalculate InheritedRenderGroups with stored propagator + // - Policy: propagate value derived from propagator + + // Case 5: parent is a propagator + // - Policy: propagate value derived from propagator + + // Case 6: parent is missing + // - (this is a hierarchy error but we don't check it) + // - Policy: remove all + + // Check cases where a value should be propagated. + let propagator = if let Some(parent) = maybe_parent { + if let Ok((propagator, ..)) = all_propagators.get(parent) { + // Case 5 + Some(propagator) + } else if updated_entities.contains(parent){ + // Parent was marked updated (but self was not) + let Ok((_, maybe_inherited)) = maybe_inherited.get(entity) { + let Some(inherited) = maybe_inherited { + // Case 4-1, 4-2 + Some(inherited.propagator) + } else { + // Case 4-3 + None + } + } else { + // We already know entity is a non-propagator and exists + unreachable!(); + } + } else { + // Parent was not marked updated + let Ok((_, maybe_inherited)) = maybe_inherited.get(parent) { + let Some(inherited) = maybe_inherited { + // Case 3-1, 3-2 + Some(inherited.propagator) + } else { + // Case 2 + None + } + } else { + // Case 6 + None + } + } + } else { + // Case 1 + None + }; + + // Propagate if possible + // - Case 3-2, 4-2 + // - Cases 3-1, 4-1 are filtered out here. + let Some(( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera, + propagate + )) = propagator.filter_map(|p| all_propagators.get(p)) { + // Propagation value + let propagated = propagate.get_render_groups( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera + ); + + apply_full_propagation_force_update( + &mut commands, + &mut updated_entities, + &children_query, + &mut maybe_inherited, + propagator, + propagated, + entity + ); + // In all other cases, remove all InheritedRenderGroups. + } else { + apply_full_propagation_force_remove( + &mut commands, + &mut updated_entities, + &children_query, + &mut maybe_inherited, + entity + ); + } + } +} + +/// Applies propagation to entities for `handle_lost_propagator`. +// Note: This does not require checking updated_entities because we force-update children regardless of +// previous updates. +fn apply_full_propagation_force_update( + commands: &mut Commands, + updated_entities: &mut PropagateRenderGroupsEntityCache, + children_query: &Query<&Children>, + maybe_inherited: &mut Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, + propagator: Entity, + propagated: &RenderGroups, + entity: Entity, +) { + // Leave if entity doesn't exist or has PropagateRenderGroups. + let Ok((maybe_render_groups, mut maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + return; + } + + // Force-update + let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); + + let mut new = InheritedRenderGroups::empty(); + if let Some(mut inherited) = maybe_inherited_groups { + // Steal existing allocation if possible. + std::mem::swap(&mut inherited, &mut new); + } + + new.propagator = propagator; + new.computed.set_from(initial_groups); + new.computed.merge(propagated); + commands.get(entity).insert(new); + + // Mark as updated. + updated_entities.insert(entity); + + // Continue propagation to children. + let Ok(children) = children_query.get(entity) else { + return; + }; + for child in children.iter() { + apply_full_propagation_force_update( + commands, + updated_entities, + children_query, + maybe_inherited, + propagator, + propagated, + child + ); + } +} + +/// Applies InheritedRenderGroups-removal to entities for `handle_lost_propagator`. +// Note: This does not require checking updated_entities because we force-update children regardless of +// previous updates. +fn apply_full_propagation_force_remove( + commands: &mut Commands, + updated_entities: &mut PropagateRenderGroupsEntityCache, + children_query: &Query<&Children>, + maybe_inherited: &mut Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, + entity: Entity, +) { + // Leave if entity doesn't exist or has PropagateRenderGroups. + let Ok(_) = maybe_inherited.get_mut(entity) else { + return; + } + + // Force-remove InheritedRenderGroups + commands.get(entity).remove::(); + + // Mark as updated. + updated_entities.insert(entity); + + // Continue propagation to children. + let Ok(children) = children_query.get(entity) else { + return; + }; + for child in children.iter() { + apply_full_propagation_force_remove(commands, updated_entities, children_query, maybe_inherited, child); + } +} /* From bd468dc5d01e4583fd1f74469539fdf3e486a9b3 Mon Sep 17 00:00:00 2001 From: koe Date: Sun, 17 Mar 2024 22:57:31 -0500 Subject: [PATCH 06/54] propagation algorithm WIP --- .../visibility/propagate_render_groups.rs | 385 +++++++++--------- 1 file changed, 190 insertions(+), 195 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index ff0d49c778ff7..2f1ea8a00cff1 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -41,7 +41,7 @@ an entity has been updated it won't be updated again (unless explicitly specifie We take note of a couple 'problems' that are challenging edge cases, and how they are handled. -### Propagators +### Propagator Repair - If an entity has both InheritedRenderGroups and PropagateRenderGroups, then remove its InheritedRenderGroups. - SYSTEM: clean_propagators @@ -62,7 +62,7 @@ propagator is encountered. since its insertion is deferred. - SOLUTION: See "If a non-propagator entity is parented to a non-propagator". -### Non-Propagators +### Non-Propagator Hierarchy Repair - If a non-propagator entity with InheritedRenderGroups is un-parented, then remove InheritedRenderGroups from the entity and its children (stopping at propagators). @@ -72,10 +72,14 @@ entity and its children (stopping at propagators). descendent of an un-parented entity will want to pull inheritance from its parent, but removal of InheritedRenderGroups is deferred so the component it would access would be stale. - If an entity loses a PropagateRenderGroups component, then inherit its parent's propagator entity, and propagate -that to its own children (stopping when a propagator is encountered). If the parent isn't a propagator and doesn't +that to its own children (stopping when a propagator is encountered or if an entity is non-updated and has an +InheritedRenderGroups that matches the propagator). If the parent isn't a propagator and doesn't have InheritedRenderGroups (or there is no parent), then remove InheritedRenderGroups from the entity and its -children (stopping at propagators). Skip already-updated entities that lost PropagateRenderGroups. +children (stopping at propagators and non-updated descendents without InheritedRenderGroups). Skip already-updated +entities that lost PropagateRenderGroups. - SYSTEM: handle_lost_propagator + - The goal of this step is to update the span of entities starting at the entity that lost a + PropagateRenderGroups component, and ending at entities that aren't potentially-invalid. - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator stays the same (see "If a propagator gains non-propagator children") (this can only happen if InheritedRenderGroups was manually inserted by a user, since the entity would not have had InheritedRenderGroups in the previous tick @@ -89,13 +93,26 @@ children (stopping at propagators). Skip already-updated entities that lost Prop the desired policy (regardless of it the entity has InheritedRenderGroups), in case previous deferred updates of this type need to be overwritten. - If a non-propagator entity is parented to a non-propagator, then propagate the parent's InheritedRenderGroups -propagator entity (stopping at propagators). If the parent doesn't have -InheritedRenderGroups, then remove InheritedRenderGroups from the entity and its children (stopping at propagators). -Skip already-updated entities that were parented to a non-propagator. +propagator entity (stopping at propagators and descendents that share the parent's propagator). If the parent doesn't +have InheritedRenderGroups, then remove InheritedRenderGroups from the entity and its children (stopping at propagators +and children that don't have InheritedRenderGroups and aren't updated). Skip already-updated entities that were parented +to a non-propagator. + - SYSTEMS: handle_new_children_nonpropagator, handle_new_parent_nonpropagator + - The goal of this step is to update the span of entities starting at the entity that was reparented, and ending + at entities that aren't potentially-invalid. - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator stays the same (see "If a propagator gains non-propagator children"). In that case, do not mark the entity updated and simply skip it. - Force-update children for the same reason as "If an entity loses a PropagateRenderGroups component". + - The implementation does not iterate non-propagator entities without InheritedRenderGroups that were parented + to entities without InheritedRenderGroups. Issues that can arise from that case, such as other hierarchy moves + below or above an entity, will be handled correctly by this and previous steps. + - Note that the challenging hierarchy reasoning used here is necessary to allow efficient queries. A careless + solution would require iterating all entities with Parent or Children changes, and traversing the hierarchy many + times redundantly. + +### Non-Propagator Targeted Repair + - If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute its InheritedRenderGroups::computed field. Skip already-updated entities. @@ -313,7 +330,7 @@ fn propagate_updated_propagators( } // Get value to propagate. - // Note: This can allocate spuriously if there are no children that need it. + // TODO: This can allocate spuriously if there are no children that need it. let propagated = propagate.get_render_groups( propagator, maybe_render_groups, @@ -435,7 +452,7 @@ fn propagate_to_new_children( } // Get value to propagate. - // Note: This can allocate spuriously if there are no children that need it. + // TODO: This can allocate spuriously if there are no children that need it. let propagated = propagate.get_render_groups( propagator, maybe_render_groups, @@ -617,7 +634,7 @@ fn handle_lost_propagator( // - Policy: remove all // - Subcase 2: Propagator stored in self's InheritedRenderGroups doesn't exist // - Policy: remove all - // - Subcase 3: Recalculate InheritedRenderGroups with stored propagator + // - Subcase 3: Recalculate InheritedRenderGroups with self-stored propagator // - Policy: propagate value derived from propagator // Case 5: parent is a propagator @@ -636,10 +653,10 @@ fn handle_lost_propagator( // Parent was marked updated (but self was not) let Ok((_, maybe_inherited)) = maybe_inherited.get(entity) { let Some(inherited) = maybe_inherited { - // Case 4-1, 4-2 + // Case 4-2, 4-3 Some(inherited.propagator) } else { - // Case 4-3 + // Case 4-1 None } } else { @@ -667,8 +684,8 @@ fn handle_lost_propagator( }; // Propagate if possible - // - Case 3-2, 4-2 - // - Cases 3-1, 4-1 are filtered out here. + // - Case 3-2, 4-3 + // - Cases 3-1, 4-2 are filtered out here. let Some(( propagator, maybe_render_groups, @@ -677,6 +694,7 @@ fn handle_lost_propagator( propagate )) = propagator.filter_map(|p| all_propagators.get(p)) { // Propagation value + // TODO: This can allocate spuriously if there are no children that need it. let propagated = propagate.get_render_groups( propagator, maybe_render_groups, @@ -684,6 +702,13 @@ fn handle_lost_propagator( maybe_camera ); + // Pre-update the entity as a hack for case 4-3. If we don't do this then + // the entity will be caught in "Leave if entity is non-updated and inherits a matching propagator." + // - Note: Case 4-3 is compensating for users manually inserting InheritedRenderGroups + // components, so this could be simplified if that's deemed overkill (we don't fully compensate for + // manual messing with InheritedRenderGroups anyway, so there is no real reliability for doing so). + updated_entities.insert(entity); + apply_full_propagation_force_update( &mut commands, &mut updated_entities, @@ -706,9 +731,7 @@ fn handle_lost_propagator( } } -/// Applies propagation to entities for `handle_lost_propagator`. -// Note: This does not require checking updated_entities because we force-update children regardless of -// previous updates. +/// Applies propagation to entities for `handle_lost_propagator` and `handle_new_children_nonpropagator`. fn apply_full_propagation_force_update( commands: &mut Commands, updated_entities: &mut PropagateRenderGroupsEntityCache, @@ -726,6 +749,13 @@ fn apply_full_propagation_force_update( return; } + // Leave if entity is non-updated and inherits a matching propagator. + if let Some(inherited) = maybe_inherited_groups { + if (inherited.propagator == propagator) && !updated_entities.contains(entity) { + return; + } + } + // Force-update let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); @@ -760,9 +790,8 @@ fn apply_full_propagation_force_update( } } -/// Applies InheritedRenderGroups-removal to entities for `handle_lost_propagator`. -// Note: This does not require checking updated_entities because we force-update children regardless of -// previous updates. +/// Applies InheritedRenderGroups removal to entities for `handle_lost_propagator`, +/// `handle_new_children_nonpropagator`, and `handle_new_parent_nonpropagator`. fn apply_full_propagation_force_remove( commands: &mut Commands, updated_entities: &mut PropagateRenderGroupsEntityCache, @@ -774,7 +803,12 @@ fn apply_full_propagation_force_remove( entity: Entity, ) { // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok(_) = maybe_inherited.get_mut(entity) else { + let Ok((_, maybe_inherited: Option<&mut InheritedRenderGroups>)) = maybe_inherited.get(entity) else { + return; + } + + // Leave if entity is non-updated and doesn't have InheritedRenderGroups. + if maybe_inherited.is_none() && !updated_entities.contains(entity) { return; } @@ -793,202 +827,163 @@ fn apply_full_propagation_force_remove( } } -/* - -fn propagate_render_groups_updated_propagators( - mut updated_entities: ResMut, +/// Handles non-propagator entities with InheritedRenderGroups whose children changed. +fn handle_new_children_nonpropagator( mut commands: Commands, - // Query for entities with PropagateRenderGroups that InheritedRenderGroups needs to be removed from. - dirty_propagators: Query, With)>, - // Query for all propagators with changed PropagateRenderGroups. - // This does a 'full propagation' to push changes all the way down the tree. - changed_propagate_cameras_query: Query< - (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), - Changed - >, - // Query for camera propagator entities with changed CameraView. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - changed_view_cameras_query: Query< - (Entity, &CameraView, Option<&RenderGroups>, &PropagateRenderGroups), - (With, Changed), - >, - // Tracker to identify entities that lost CameraView. - mut removed_cameraview: RemovedComponents, - // Query for all propagators with removed CameraView. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - removed_cameraview_propagator_query: Query< - (Entity, Option<&RenderGroups>, Option<&Camera>, &PropagateRenderGroups), - Without - >, - // Query for all propagator entities with updated RenderGroups. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - changed_rendergroups_propagator_query: Query< - (Entity, &RenderGroups, &PropagateRenderGroups), - (Changed) - >, - // Tracker to identify entities that lost RenderGroups. - mut removed_rendergroups: RemovedComponents, - // Query for propagator entities with removed RenderGroups. - // This does a 'full propagation' (depending on propagation mode) to push changes all the way down the tree. - removed_rendergroups_propagator_query: Query< - (Entity, Option<&CameraView>, Option<&Camera>, &PropagateRenderGroups), - Without - >, - - // Query for getting Children. - children_query: Query<&Children>, - // Query for updating InheritedRenderGroups on entities. - mut maybe_inherited_query: Query<(Entity, Option<&RenderGroups>, Option<&mut InheritedRenderGroups>)>, -) { - // Track updated entities to prevent redundant updates, as `Commands` changes are deferred. - // - // Using this ensures that once mutations to entities with PropagateRenderGroups have been - // propagated, all entities affected by those changes won't be mutated again. This makes it - // safe to read parent InheritedRenderGroups (in the other cases that need to be handled) in - // order to 'pull in' inherited changes when needed. - updated_entities.clear(); -} - -//todo: chain after propagate_render_groups_full -fn propagate_render_groups_targeted( mut updated_entities: ResMut, - mut commands: Commands, - // Query for all propagators with changed children. - // This does a 'propagator enity propagation' to push changes to only descendents with different propagator - // entities. - changed_children_propagator_query: Query< - (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups), - Changed, - >, - // Tracker to identify entities that lost PropagateRenderGroups. - // - Takes into account where the entity has a parent. - // - Takes into account whether the parent is a propagator or non-propagator. - // These entities and their children will be 'repropagated' from the lost entities' parents. - mut removed_propagate: RemovedComponents, - removed_propagate_entities: Query<&Children, Without>, - // Query for entities with InheritedRenderGroups who gained a new parent. - // - Ignores entities whose parents have PropagateRenderGroups, since that case is handled by - // changed_children_propagator_query. - // - If the parent doesn't have InheritedRenderGroups, then the entity and its children will be 'unpropagated'. - // - If the parent does have InheritedRenderGroups, then propagation will be applied to the entity and - // its children. - // This does a 'propagator enity propagation' to push changes to only descendents with different propagator - // entities. - changed_parents_query: Query< - (Entity, Option<&Children>), - (Changed, With, Without) - >, - // Query for entities with InheritedRenderGroups whose children changed. - // - Ignores children with InheritedRenderGroups, those are handled by changed_parents_query. - // This does a 'propagator enity propagation' to push changes to only descendents with different propagator - // entities. - changed_children_query: Query< - (Entity, &Children), - (Changed, With, Without) + // Entities with InheritedRenderGroups that changed children + inherited_with_children: Query< + (Entity, &Children, &InheritedRenderGroups), + (Changed, Without) >, - // Query for non-propagator entities with updated RenderGroups. - // This updates the entity's InheritedRenderGroups. - changed_rendergroups_query: Query< - (Entity, &RenderGroups), - (Changed, Without) - >, - // Tracker to identify entities that lost RenderGroups. - mut removed_rendergroups: RemovedComponents, - // Query for non-propagator entities with InheritedRenderGroups and removed RenderGroups. - // This updates the entity's InheritedRenderGroups. - removed_rendergroups_query: Query< - Entity, - (With, Without) + // Query for accessing propagators + all_propagators: Query< + ( + Entity, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + ) >, - // Query for getting Children. children_query: Query<&Children>, - // Query for entities that propagate. - // Used when pulling inheritance information from the parent. - propagators: Query< - (Entity, Option<&CameraView>, Option<&Camera>, Option<&RenderGroups>, &PropagateRenderGroups) + // Query for updating InheritedRenderGroups on non-propagator entities. + mut maybe_inherited: Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, >, - // Query for entities that inherited and don't propagate. - // Used when pulling inheritance information from the parent. - nonpropagators: Query<(), (With, Without)>, - // Query for updating InheritedRenderGroups on entities. - mut maybe_inherited_query: Query<(Entity, Option<&RenderGroups>, Option<&mut InheritedRenderGroups>)>, ) { + for (entity, children, inherited) in inherited_with_children.iter() { + // Skip entity if already updated, which implies children are already in an accurate state. + if updated_entities.contains(entity) { + continue; + } - let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); + let Some(( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera, + propagate + )) = inherited.propagator.filter_map(|p| all_propagators.get(p)) else { + // Remove InheritedRenderGroups from descendents if the propagator is missing + // - This is either an error caused by manually modifying InheritedRenderGroups, or is caused by a + // reparenting + propagator despawn. + + // Iterate children + for child in children.iter() { + // Skip children that were already updated. + // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. + if updated_entities.contains(child) { + continue; + } - let mut propagated = if let Some(propagate) = maybe_propagate_groups { - Some(propagate.get_from_camera(entity, camera_view)) - } else { - None - }; + // Propagate + apply_full_propagation_force_remove( + commands, + updated_entities, + children_query, + maybe_inherited, + child + ); + } - // Assuming that TargetCamera is manually set on the root node only, - // update root nodes first, since it implies the biggest change - for (root_node, target_camera) in &changed_root_nodes_query { - update_children_render_groups( - root_node, - target_camera, - &node_query, - &children_query, - &mut commands, - &mut updated_entities, - ); - } + continue; + }; - // If the root node TargetCamera was changed, then every child is updated - // by this point, and iteration will be skipped. - // Otherwise, update changed children - for (parent, target_camera) in &changed_children_query { - update_children_render_groups( - parent, - target_camera, - &node_query, - &children_query, - &mut commands, - &mut updated_entities, + // Get value to propagate. + // TODO: This can allocate spuriously if there are no children that need it. + let propagated = propagate.get_render_groups( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera ); + + // Iterate children + for child in children.iter() { + // Skip children that were already updated. We only skip updated children of this initial high-level + // loop, not children within the recursion which need to be force-updated. The 'span' of entities + // we update in this step starts at non-updated children of an entity with InheritedRenderGroups. + // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. + if updated_entities.contains(child) { + continue; + } + + // Propagate + apply_full_propagation_force_update( + commands, + updated_entities, + children_query, + maybe_inherited, + propagator, + propagated, + child + ); + } } } -fn update_children_render_groups( - updated_entities: &mut HashSet, - entity: Entity, - camera_to_set: Option<&TargetCamera>, - node_query: &Query, With>, - children_query: &Query<&Children, With>, - commands: &mut Commands, +/// Handles non-propagator entities with InheritedRenderGroups whose parents changed. +/// - Since handle_new_children_nonpropagator handles all cases where the parent has InheritedRenderGroups, this +/// system just needs to remove InheritedRenderGroups from non-updated entities and their non-updated descendents +/// that have InheritedRenderGroups (stopping at propagators and non-updated descendents without +/// InheritedRenderGroups). +/// - We skip non-updated entities whose parents are updated, because that implies the current InheritedRenderGroups +/// propagator is accurate. +fn handle_new_parent_nonpropagator( + mut commands: Commands, + mut updated_entities: ResMut, + // Entities with InheritedRenderGroups that changed parents + inherited_with_parent: Query< + (Entity, Option<&Children>, &Parent), + (Changed, With, Without) + >, + // Query for Children + children_query: &Query<&Children>, + // Query for updating InheritedRenderGroups on non-propagator entities. + maybe_inherited: &mut Query< + (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), + Without, + >, ) { - let Ok(children) = children_query.get(entity) else { - return; - }; - - for &child in children { - // Skip if the child has already been updated or update is not needed - if updated_entities.contains(&child) - || camera_to_set == node_query.get(child).ok().flatten() - { + for (entity, maybe_children, parent) in inherited_with_parent.iter() { + // Skip entity if already updated + if updated_entities.contains(entity) { continue; } - match camera_to_set { - Some(camera) => { - commands.entity(child).insert(camera.clone()); - } - None => { - commands.entity(child).remove::(); - } + // Skip entity if parent was updated + if updated_entities.contains(*parent) { + continue; } - updated_entities.insert(child); - update_children_render_groups( - child, - camera_to_set, - node_query, - children_query, - commands, - updated_entities, - ); + // Remove from self. + commands.get(entity).remove::(); + + // Mark as updated. + updated_entities.insert(entity); + + // Iterate children. + // - We assume the parent of this entity does NOT have InheritedRenderGroups, so neither should its + // descendents. + let Some(children) = maybe_children else { + continue; + }; + for child in children.iter() { + apply_full_propagation_force_remove( + &mut commands, + &mut updated_entities, + &children_query, + &maybe_inherited, + child + ); + } } } + +/* +- If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute +its InheritedRenderGroups::computed field. Skip already-updated entities. */ From 481a8014c12e98b84e9ce0c51f2deb46f0ed7c9d Mon Sep 17 00:00:00 2001 From: koe Date: Sun, 17 Mar 2024 23:43:52 -0500 Subject: [PATCH 07/54] propagation algorithm early draft --- crates/bevy_render/src/view/visibility/mod.rs | 44 ++++--- .../visibility/propagate_render_groups.rs | 96 ++++++++++++++- .../src/view/visibility/render_groups.rs | 112 +++++++++--------- 3 files changed, 174 insertions(+), 78 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 14fcf2862f837..7761a45e81f42 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -3,7 +3,7 @@ mod render_layers; use bevy_derive::Deref; pub use propagate_render_groups::*; pub use render_groups::*; -//pub use render_layers::*; +pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{Assets, Handle}; @@ -215,7 +215,14 @@ impl Plugin for VisibilityPlugin { fn build(&self, app: &mut bevy_app::App) { use VisibilitySystems::*; - app.add_systems( +/* + app.configure_sets( + PostUpdate, + PropagateRenderGroupsSet + .in_set(VisibilityPropagate), + ) +*/ +app .add_systems( PostUpdate, ( calculate_bounds.in_set(CalculateBounds), @@ -240,11 +247,7 @@ impl Plugin for VisibilityPlugin { .in_set(UpdateProjectionFrusta) .after(camera_system::) .after(TransformSystem::TransformPropagate), - (visibility_propagate_system, propagate_render_groups, reset_view_visibility).in_set(VisibilityPropagate), - // Use apply_deferred to process InheritedRenderGroups component additions/removals. - apply_deferred - .after(VisibilityPropagate) - .before(CheckVisibility), + (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate), check_visibility .in_set(CheckVisibility) .after(CalculateBounds) @@ -378,32 +381,35 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { /// for that view. pub fn check_visibility( mut thread_queues: Local>>, - mut commands: Commands, mut view_query: Query<( - Entity, +// Entity, &mut VisibleEntities, &Frustum, - Option<&CameraView>, + Option<&RenderLayers>, +// Option<&CameraView>, &Camera, )>, mut visible_aabb_query: Query<( Entity, &InheritedVisibility, &mut ViewVisibility, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, +// Option<&RenderGroups>, +// Option<&InheritedRenderGroups>, Option<&Aabb>, &GlobalTransform, Has, )>, deterministic_rendering_config: Res, ) { - for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { +// for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { + for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query { if !camera.is_active { continue; } - let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); + let view_mask = maybe_view_mask.copied().unwrap_or_default(); +// let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); visible_entities.entities.clear(); visible_aabb_query.par_iter_mut().for_each(|query_item| { @@ -411,8 +417,9 @@ pub fn check_visibility( entity, inherited_visibility, mut view_visibility, - maybe_groups, - maybe_inherited_groups, + maybe_entity_mask, +// maybe_groups, +// maybe_inherited_groups, maybe_model_aabb, transform, no_frustum_culling, @@ -424,11 +431,14 @@ pub fn check_visibility( return; } + let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); + if !view_mask.intersects(&entity_mask) { +/* // Check render groups. let entity_groups = maybe_inherited_groups.map(|i| &i.computed) .or(maybe_groups) .unwrap_or(&RenderGroups::default()); - if !camera_view.entity_is_visible(camera_entity, entity_groups) { + if !camera_view.entity_is_visible(camera_entity, entity_groups) { */ return; } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 2f1ea8a00cff1..7fdb11dc48d22 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -115,6 +115,7 @@ to a non-propagator. - If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute its InheritedRenderGroups::computed field. Skip already-updated entities. + - SYSTEM: handle_modified_rendergroups ## Performance @@ -238,7 +239,34 @@ pub struct InheritedRenderGroups pub computed: RenderGroups, }; -//todo: insert resource +/// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on +/// entities. +#[derive(SystemSet, Debug, Hash, Eq, PartialEq)] +pub struct PropagateRenderGroupsSet; + +pub(crate) struct PropagateRenderGroupsPlugin; + +impl Plugin for PropagateRenderGroupsPlugin { + fn build(app: &mut App) { + app.init_resource::() + .add_systems(PostUpdate, + ( + clean_propagators, + propagate_updated_propagators, + propagate_to_new_children, + handle_orphaned_nonpropagators, + handle_lost_propagator, + handle_new_children_nonpropagator, + handle_new_parent_nonpropagator, + apply_deferred, + handle_modified_rendergroups, //does not have deferred commands + ) + .chain() + .in_set(PropagateRenderGroupsSet) + ); + } +} + #[derive(Resource, Default, Deref, DerefMut)] struct PropagateRenderGroupsEntityCache(EntityHashMap); @@ -983,7 +1011,65 @@ fn handle_new_parent_nonpropagator( } } -/* -- If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute -its InheritedRenderGroups::computed field. Skip already-updated entities. -*/ +/// Handles added/removed/changed RenderGroups for entities with existing InheritedRenderGroups. +fn handle_modified_rendergroups( + mut updated_entities: ResMut, + // Entities with InheritedRenderGroups that changed RenderGroups + inherited_changed: Query< + Entity, + (Changed, With, Without) + >, + // RenderGroups removals. + mut removed_rendergroups: RemovedComponents, + // Query for accessing propagators + all_propagators: Query< + ( + Entity, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + ) + >, + // Query for updating InheritedRenderGroups on non-propagator entities. + maybe_inherited: &mut Query< + (Option<&RenderGroups>, &mut InheritedRenderGroups), + Without, + >, +) { + for entity in inherited_changed.iter().zip(removed_rendergroups.read()) { + // Skip entity if already updated. + if updated_entities.contains(entity) { + continue; + } + + // Skip entity if it's a propagator or doesn't exist. + let Ok((maybe_render_groups, mut inherited)) = maybe_inherited.get(entity) else { + continue; + }; + + // Skip entity if propagator is missing. + // - This is an error, hierarchy steps should have marked this entity as updated. + let Some(( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera, + propagate + )) = all_propagators.get(inherited.propagator) else { + error_once!("hierarchy error: propagator missing for {entity} in `handle_modified_rendergroups`"); + continue; + }; + + // Get propagated value. + let propagated = propagate.get_render_groups( + propagator, + maybe_render_groups, + maybe_camera_view, + maybe_camera + ); + + // Update entity value. + inherited.computed.merge(propagated); + } +} diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index bc9cb40b8dd48..54d28d98dd861 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -5,9 +5,9 @@ use smallvec::SmallVec; /// The default [`RenderLayer`]. pub const DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); -/// Wraps a specific render layer that can be stored in [`RenderLayers`]. +/// Wraps a specific render layer that can be stored in [`RenderLayersXX`]. /// -/// Stores an index into the [`RenderLayers`] internal bitmask. +/// Stores an index into the [`RenderLayersXX`] internal bitmask. //todo: Upper limit policy for render layer indices. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub struct RenderLayer(pub usize); @@ -30,25 +30,25 @@ impl Default for RenderLayer { /// are visible to which cameras. /// /// Individual render layers can be defined with [`RenderLayer`], which is an index -/// into the internal `RenderLayers` bitmask. +/// into the internal `RenderLayersXX` bitmask. /// -/// `RenderLayers::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default +/// `RenderLayersXX::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default /// layer. /// /// ### Performance /// -/// `RenderLayers` occupies 24 bytes on the stack. +/// `RenderLayersXX` occupies 24 bytes on the stack. /// -/// `RenderLayers` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte +/// `RenderLayersXX` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. #[derive(Debug, Clone)] -pub struct RenderLayers +pub struct RenderLayersXX { layers: SmallVec<[u64; 1]>, } -impl RenderLayers { - /// Makes a new `RenderLayers` with no layers. +impl RenderLayersXX { + /// Makes a new `RenderLayersXX` with no layers. pub fn empty() -> Self { Self{ layers: SmallVec::default() } } @@ -80,7 +80,7 @@ impl RenderLayers { /// Copies `other` into `Self`. /// - /// This is more efficient than cloning `other` if you want to reuse a `RenderLayers` + /// This is more efficient than cloning `other` if you want to reuse a `RenderLayersXX` /// that is potentially allocated. pub fn set_from(&mut self, other: &Self) { self.layers.clear(); @@ -113,7 +113,7 @@ impl RenderLayers { .flatten() } - /// Returns `true` if the specified render layer is included in this `RenderLayers`. + /// Returns `true` if the specified render layer is included in this `RenderLayersXX`. pub fn contains(&self, layer: RenderLayer) -> bool { let (buffer_index, bit) = Self::layer_info(layer); if buffer_index >= self.layers.len() { @@ -172,7 +172,7 @@ impl RenderLayers { } } -impl From for RenderLayers { +impl From for RenderLayersXX { fn from(layer: RenderLayer) -> Self { let mut layers = Self{ layers: SmallVec::default() }; layers.add(layer); @@ -180,7 +180,7 @@ impl From for RenderLayers { } } -impl From<&[RenderLayer]> for RenderLayers { +impl From<&[RenderLayer]> for RenderLayersXX { fn from(layers: &[RenderLayer]) -> Self { let mut layers = Self{ layers: SmallVec::default() }; for layer in layers { @@ -190,7 +190,7 @@ impl From<&[RenderLayer]> for RenderLayers { } } -impl Default for RenderLayers { +impl Default for RenderLayersXX { fn default() -> Self { Self::from(DEFAULT_RENDER_LAYER) } @@ -199,11 +199,11 @@ impl Default for RenderLayers { /// Component on an entity that controls which cameras can see it. /// /// There are two kinds of render groups: -/// - [`RenderLayers`]: These are grouping categories that many cameras can view (see [`CameraView`]). +/// - [`RenderLayersXX`]: These are grouping categories that many cameras can view (see [`CameraView`]). /// - *Camera entity*: This is a specific camera that the entity is affiliated with. This is especially /// useful for UI in combination with [`PropagateRenderGroups`]. /// -/// An entity can be a member of multiple [`RenderLayers`] in addition to having a camera affiliation. +/// An entity can be a member of multiple [`RenderLayersXX`] in addition to having a camera affiliation. /// /// ### Default behavior /// @@ -239,24 +239,24 @@ entity.insert(groups); #[derive(Component, Debug, Clone)] pub struct RenderGroups { - layers: RenderLayers, + layers: RenderLayersXX, camera: Option, } impl RenderGroups { /// Makes a new `RenderGroups` with no groups. pub fn empty() -> Self { - Self{ layers: RenderLayers::empty(), camera: None } + Self{ layers: RenderLayersXX::empty(), camera: None } } /// Makes a new `RenderGroups` with just a camera. pub fn new_with_camera(camera: Entity) -> Self { - Self{ layers: RenderLayers::empty(), camera: Some(camera) } + Self{ layers: RenderLayersXX::empty(), camera: Some(camera) } } /// Adds a [`RenderLayer`]. /// - /// See [`RenderLayers::add`]. + /// See [`RenderLayersXX::add`]. pub fn add(&mut self, layer: RenderLayer) -> &mut Self { self.layers.add(layer); self @@ -264,7 +264,7 @@ impl RenderGroups { /// Removes a [`RenderLayer`]. /// - /// See [`RenderLayers::remove`]. + /// See [`RenderLayersXX::remove`]. pub fn remove(&mut self, layer: RenderLayer) -> &mut Self { self.layers.remove(layer); self @@ -278,11 +278,11 @@ impl RenderGroups { /// Merges `other` into `Self`. /// - /// After merging, `Self` will include all [`RenderLayers`] from `other` and `Self`. + /// After merging, `Self` will include all [`RenderLayersXX`] from `other` and `Self`. /// If both `Self` and `other` have a camera affiliation, then the `Self` camera /// will be in the merged result. Otherwise the `other` camera will be in the result. /// - /// Will allocate if necessary to include all [`RenderLayers`] of `other`. + /// Will allocate if necessary to include all [`RenderLayersXX`] of `other`. pub fn merge(&mut self, other: &Self) { self.layers.merge(&other.layers); self.camera = self.camera.or(other.camera); @@ -324,7 +324,7 @@ impl RenderGroups { /// Returns `true` if `Self` intersects with `other`. /// - /// Checks both camera affiliation and [`RenderLayers`] intersection. + /// Checks both camera affiliation and [`RenderLayersXX`] intersection. pub fn intersects(&self, other: &Self) -> bool { if let (Some(a), Some(b)) = (self.camera, other.camera) { if a == b { @@ -343,13 +343,13 @@ impl RenderGroups { impl From for RenderGroups { /// Makes a new `RenderGroups` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { - Self{ layers: RenderLayers::from(layer), camera: None } + Self{ layers: RenderLayersXX::from(layer), camera: None } } } -impl From for RenderGroups { - /// Makes a new `RenderGroups` from a [`RenderLayers`]. - fn from(layers: RenderLayers) -> Self { +impl From for RenderGroups { + /// Makes a new `RenderGroups` from a [`RenderLayersXX`]. + fn from(layers: RenderLayersXX) -> Self { Self{ layers, camera: None } } } @@ -361,7 +361,7 @@ impl Default for RenderGroups { } } -/// Component on camera entities that controls which [`RenderLayers`] are visible to +/// Component on camera entities that controls which [`RenderLayersXX`] are visible to /// the camera. /// /// A camera will see any entity that satisfies either of these conditions: @@ -380,18 +380,18 @@ impl Default for RenderGroups { #[derive(Component, Debug, Clone)] pub struct CameraView { - layers: RenderLayers, + layers: RenderLayersXX, } impl CameraView { /// Makes a new `CameraView` with no visible [`RenderLayer`]. pub fn empty() -> Self { - Self{ layers: RenderLayers::empty() } + Self{ layers: RenderLayersXX::empty() } } /// Adds a [`RenderLayer`]. /// - /// See [`RenderLayers::add`]. + /// See [`RenderLayersXX::add`]. pub fn add(&mut self, layer: RenderLayer) -> &mut Self { self.layers.add(layer); self @@ -399,7 +399,7 @@ impl CameraView { /// Removes a [`RenderLayer`]. /// - /// See [`RenderLayers::remove`]. + /// See [`RenderLayersXX::remove`]. pub fn remove(&mut self, layer: RenderLayer) -> &mut Self { self.layers.remove(layer); self @@ -423,7 +423,7 @@ impl CameraView { /// Returns `true` if the entity with the specified [`RenderGroups`] is visible /// to the `camera` that has this `CameraView`. /// - /// Checks both camera affiliation and [`RenderLayers`] intersection. + /// Checks both camera affiliation and [`RenderLayersXX`] intersection. pub fn entity_is_visible(&self, camera: Entity, groups: &RenderGroups) -> bool { if Some(camera) == groups.camera { return true; @@ -431,7 +431,7 @@ impl CameraView { self.layers.intersects(&groups.layers) } - /// Converts the internal [`RenderLayers`] into a [`RenderGroups`] affiliated + /// Converts the internal [`RenderLayersXX`] into a [`RenderGroups`] affiliated /// with the camera that has this `CameraView`. pub fn get_groups(&self, camera: Entity) -> RenderGroups { let mut groups = RenderGroups::from(self.layers.clone()); @@ -443,7 +443,7 @@ impl CameraView { impl From for CameraView { /// Makes a new `CameraView` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { - Self{ layers: RenderLayers::from(layer) } + Self{ layers: RenderLayersXX::from(layer) } } } @@ -456,68 +456,68 @@ impl Default for CameraView { #[cfg(test)] mod rendering_mask_tests { - use super::{RenderLayer, RenderLayers}; + use super::{RenderLayer, RenderLayersXX}; #[test] fn rendering_mask_sanity() { - assert_eq!(RenderLayers::default().len(), 1, "default layer contains only one layer"); - assert!(RenderLayers::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default"); - assert_eq!(RenderLayers::from(RenderLayer(1)).len(), 1, "from contains 1 layer"); - assert!(RenderLayers::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate"); - assert!(!RenderLayers::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected"); + assert_eq!(RenderLayersXX::default().len(), 1, "default layer contains only one layer"); + assert!(RenderLayersXX::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default"); + assert_eq!(RenderLayersXX::from(RenderLayer(1)).len(), 1, "from contains 1 layer"); + assert!(RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate"); + assert!(!RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected"); - assert_eq!(RenderLayers::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3"); + assert_eq!(RenderLayersXX::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3"); assert_eq!( - RenderLayers::layer(0).with(1).without(0).0, + RenderLayersXX::layer(0).with(1).without(0).0, 2, "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderLayers::layer(1).intersects(&RenderLayers::layer(1)), + RenderLayersXX::layer(1).intersects(&RenderLayersXX::layer(1)), "layers match like layers" ); assert!( - RenderLayers::layer(0).intersects(&RenderLayers(1)), + RenderLayersXX::layer(0).intersects(&RenderLayersXX(1)), "a layer of 0 means the mask is just 1 bit" ); assert!( - RenderLayers::layer(0) + RenderLayersXX::layer(0) .with(3) - .intersects(&RenderLayers::layer(3)), + .intersects(&RenderLayersXX::layer(3)), "a mask will match another mask containing any similar layers" ); assert!( - RenderLayers::default().intersects(&RenderLayers::default()), + RenderLayersXX::default().intersects(&RenderLayersXX::default()), "default masks match each other" ); assert!( - !RenderLayers::layer(0).intersects(&RenderLayers::layer(1)), + !RenderLayersXX::layer(0).intersects(&RenderLayersXX::layer(1)), "masks with differing layers do not match" ); assert!( - !RenderLayers(0).intersects(&RenderLayers(0)), + !RenderLayersXX(0).intersects(&RenderLayersXX(0)), "empty masks don't match" ); assert_eq!( - RenderLayers::from_layers(&[0, 2, 16, 30]) + RenderLayersXX::from_layers(&[0, 2, 16, 30]) .iter() .collect::>(), vec![0, 2, 16, 30], "from_layers and get_layers should roundtrip" ); assert_eq!( - format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(), - "RenderLayers([0, 1, 2, 3])", + format!("{:?}", RenderLayersXX::from_layers(&[0, 1, 2, 3])).as_str(), + "RenderLayersXX([0, 1, 2, 3])", "Debug instance shows layers" ); assert_eq!( - RenderLayers::from_layers(&[0, 1, 2]), - >::from_iter(vec![0, 1, 2]), + RenderLayersXX::from_layers(&[0, 1, 2]), + >::from_iter(vec![0, 1, 2]), "from_layers and from_iter are equivalent" ); } From 7702347a07a67ba5ab5c49599689ceedbeb040bd Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 00:50:06 -0500 Subject: [PATCH 08/54] render groups compiles --- crates/bevy_render/src/view/visibility/mod.rs | 4 +- .../src/view/visibility/render_groups.rs | 126 ++++++++++-------- 2 files changed, 74 insertions(+), 56 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 7761a45e81f42..d42567cbc5226 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,7 +1,9 @@ +//mod propagate_render_groups; +mod render_groups; mod render_layers; use bevy_derive::Deref; -pub use propagate_render_groups::*; +//pub use propagate_render_groups::*; pub use render_groups::*; pub use render_layers::*; diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 54d28d98dd861..ed61de193b588 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -1,4 +1,5 @@ -use bevy_ecs::Entity; +use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::prelude::{Component, Entity}; use smallvec::SmallVec; @@ -9,7 +10,7 @@ pub const DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); /// /// Stores an index into the [`RenderLayersXX`] internal bitmask. //todo: Upper limit policy for render layer indices. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deref, DerefMut)] pub struct RenderLayer(pub usize); impl RenderLayer @@ -20,9 +21,15 @@ impl RenderLayer } } +impl From for RenderLayer { + fn from(layer: usize) -> Self { + Self(layer) + } +} + impl Default for RenderLayer { fn default() -> Self { - Self(DEFAULT_RENDER_LAYER) + DEFAULT_RENDER_LAYER } } @@ -41,7 +48,7 @@ impl Default for RenderLayer { /// /// `RenderLayersXX` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct RenderLayersXX { layers: SmallVec<[u64; 1]>, @@ -53,11 +60,17 @@ impl RenderLayersXX { Self{ layers: SmallVec::default() } } + /// Makes a new `RenderLayersXX` from a slice. + pub fn from_layers + Copy>(layers: &[T]) -> Self { + layers.iter().map(|l| (*l).into()).collect() + } + /// Adds a [`RenderLayer`]. - pub fn add(&mut self, layer: RenderLayer) { - let (buffer_index, bit) = Self::layer_info(layer); + pub fn add(&mut self, layer: impl Into) -> &mut Self { + let (buffer_index, bit) = Self::layer_info(*(layer.into())); self.extend_buffer(buffer_index + 1); self.layers[buffer_index] |= bit; + self } /// Removes a [`RenderLayer`]. @@ -65,12 +78,13 @@ impl RenderLayersXX { /// Does not shrink the internal buffer even if doing so is possible after /// removing the layer. We assume if you added a large layer then it is /// possible you may re-add another large layer. - pub fn remove(&mut self, layer: RenderLayer) { - let (buffer_index, bit) = Self::layer_info(layer); + pub fn remove(&mut self, layer: impl Into) -> &mut Self { + let (buffer_index, bit) = Self::layer_info(*(layer.into())); if buffer_index >= self.layers.len() { - return; + return self; } - self.layers[buffer_index] &= ~bit; + self.layers[buffer_index] &= !bit; + self } /// Clears all stored render layers without deallocating. @@ -84,7 +98,7 @@ impl RenderLayersXX { /// that is potentially allocated. pub fn set_from(&mut self, other: &Self) { self.layers.clear(); - self.layers.reserve_exact(other.len()); + self.layers.reserve_exact(other.layers.len()); self.layers.extend_from_slice(other.layers.as_slice()); } @@ -94,7 +108,7 @@ impl RenderLayersXX { /// /// Will allocate if necessary to include all set bits of `other`. pub fn merge(&mut self, other: &Self) { - self.extend_buffer(other.len()); + self.extend_buffer(other.layers.len()); for (self_layer, other_layer) in self.layers .iter_mut() @@ -104,6 +118,13 @@ impl RenderLayersXX { } } + /// Gets the number of stored layers. + /// + /// Equivalent to `self.iter().count()`. + pub fn num_layers(&self) -> usize { + self.iter().count() + } + /// Iterates the internal render layers. pub fn iter(&self) -> impl Iterator + '_ { self.layers @@ -114,8 +135,8 @@ impl RenderLayersXX { } /// Returns `true` if the specified render layer is included in this `RenderLayersXX`. - pub fn contains(&self, layer: RenderLayer) -> bool { - let (buffer_index, bit) = Self::layer_info(layer); + pub fn contains(&self, layer: impl Into) -> bool { + let (buffer_index, bit) = Self::layer_info(*(layer.into())); if buffer_index >= self.layers.len() { return false; } @@ -156,37 +177,33 @@ impl RenderLayersXX { self.layers.resize(new_size, 0u64); } - fn iter_layers(mut buffer: u64) -> impl Iterator + '_ { - let mut layer = 0; + fn iter_layers(mut buffer: u64) -> impl Iterator + 'static { + let mut layer: usize = 0; std::iter::from_fn( - move { + move || { if buffer == 0 { return None; } - let next = buffer.trailing_zeroes() + 1; + let next = buffer.trailing_zeros() + 1; buffer >>= next; - layer += next; - Some(layer - 1) + layer += next as usize; + Some(RenderLayer(layer - 1)) } ) } } -impl From for RenderLayersXX { - fn from(layer: RenderLayer) -> Self { +impl> From for RenderLayersXX { + fn from(layer: T) -> Self { let mut layers = Self{ layers: SmallVec::default() }; layers.add(layer); layers } } -impl From<&[RenderLayer]> for RenderLayersXX { - fn from(layers: &[RenderLayer]) -> Self { - let mut layers = Self{ layers: SmallVec::default() }; - for layer in layers { - layers.add(layer); - } - layers +impl> FromIterator for RenderLayersXX { + fn from_iter>(i: T) -> Self { + i.into_iter().fold(Self::empty(), |mut mask, g| { mask.add(g); mask }) } } @@ -257,7 +274,7 @@ impl RenderGroups { /// Adds a [`RenderLayer`]. /// /// See [`RenderLayersXX::add`]. - pub fn add(&mut self, layer: RenderLayer) -> &mut Self { + pub fn add(&mut self, layer: impl Into) -> &mut Self { self.layers.add(layer); self } @@ -265,7 +282,7 @@ impl RenderGroups { /// Removes a [`RenderLayer`]. /// /// See [`RenderLayersXX::remove`]. - pub fn remove(&mut self, layer: RenderLayer) -> &mut Self { + pub fn remove(&mut self, layer: impl Into) -> &mut Self { self.layers.remove(layer); self } @@ -301,7 +318,7 @@ impl RenderGroups { /// /// Returns the previous camera. pub fn set_camera(&mut self, camera: Entity) -> Option { - self.camera.replace(Some(camera)) + self.camera.replace(camera) } /// Removes the current camera affiliation. @@ -312,13 +329,13 @@ impl RenderGroups { } /// Returns an iterator over [`RenderLayer`]. - pub fn iter_layers(&self) -> Impl Iterator + '_ { + pub fn iter_layers(&self) -> impl Iterator + '_ { self.layers.iter() } /// Returns `true` if the specified render layer is included in this /// `RenderGroups`. - pub fn contains_layer(&self, layer: RenderLayer) -> bool { + pub fn contains_layer(&self, layer: impl Into) -> bool { self.layers.contains(layer) } @@ -392,7 +409,7 @@ impl CameraView { /// Adds a [`RenderLayer`]. /// /// See [`RenderLayersXX::add`]. - pub fn add(&mut self, layer: RenderLayer) -> &mut Self { + pub fn add(&mut self, layer: impl Into) -> &mut Self { self.layers.add(layer); self } @@ -400,7 +417,7 @@ impl CameraView { /// Removes a [`RenderLayer`]. /// /// See [`RenderLayersXX::remove`]. - pub fn remove(&mut self, layer: RenderLayer) -> &mut Self { + pub fn remove(&mut self, layer: impl Into) -> &mut Self { self.layers.remove(layer); self } @@ -411,12 +428,12 @@ impl CameraView { } /// Returns an iterator over [`RenderLayer`]. - pub fn iter_layers(&self) -> Impl Iterator + '_ { + pub fn iter_layers(&self) -> impl Iterator + '_ { self.layers.iter() } /// Returns `true` if the specified render layer is included in this `CameraView`. - pub fn contains_layer(&self, layer: RenderLayer) -> bool { + pub fn contains_layer(&self, layer: impl Into) -> bool { self.layers.contains(layer) } @@ -456,37 +473,36 @@ impl Default for CameraView { #[cfg(test)] mod rendering_mask_tests { - use super::{RenderLayer, RenderLayersXX}; + use smallvec::SmallVec; + use super::{DEFAULT_RENDER_LAYER, RenderLayer, RenderLayersXX}; #[test] fn rendering_mask_sanity() { - assert_eq!(RenderLayersXX::default().len(), 1, "default layer contains only one layer"); + assert_eq!(RenderLayersXX::default().num_layers(), 1, "default layer contains only one layer"); assert!(RenderLayersXX::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default"); - assert_eq!(RenderLayersXX::from(RenderLayer(1)).len(), 1, "from contains 1 layer"); + assert_eq!(RenderLayersXX::from(RenderLayer(1)).num_layers(), 1, "from contains 1 layer"); assert!(RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate"); assert!(!RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected"); - - - assert_eq!(RenderLayersXX::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3"); + assert_eq!(RenderLayersXX::from(RenderLayer(0)).add(1).layers[0], 3, "layer 0 + 1 is mask 3"); assert_eq!( - RenderLayersXX::layer(0).with(1).without(0).0, + RenderLayersXX::from(RenderLayer(0)).add(1).remove(0).layers[0], 2, "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderLayersXX::layer(1).intersects(&RenderLayersXX::layer(1)), + RenderLayersXX::from(RenderLayer(1)).intersects(&RenderLayersXX::from(RenderLayer(1))), "layers match like layers" ); assert!( - RenderLayersXX::layer(0).intersects(&RenderLayersXX(1)), + RenderLayersXX::from(RenderLayer(0)).intersects(&RenderLayersXX{ layers: SmallVec::from_slice(&[1]) }), "a layer of 0 means the mask is just 1 bit" ); assert!( - RenderLayersXX::layer(0) - .with(3) - .intersects(&RenderLayersXX::layer(3)), + RenderLayersXX::from(RenderLayer(0)) + .add(3) + .intersects(&RenderLayersXX::from(RenderLayer(3))), "a mask will match another mask containing any similar layers" ); @@ -496,19 +512,19 @@ mod rendering_mask_tests { ); assert!( - !RenderLayersXX::layer(0).intersects(&RenderLayersXX::layer(1)), + !RenderLayersXX::from(RenderLayer(0)).intersects(&RenderLayersXX::from(RenderLayer(1))), "masks with differing layers do not match" ); assert!( - !RenderLayersXX(0).intersects(&RenderLayersXX(0)), + !RenderLayersXX::empty().intersects(&RenderLayersXX::empty()), "empty masks don't match" ); assert_eq!( RenderLayersXX::from_layers(&[0, 2, 16, 30]) .iter() .collect::>(), - vec![0, 2, 16, 30], - "from_layers and get_layers should roundtrip" + vec![RenderLayer(0), RenderLayer(2), RenderLayer(16), RenderLayer(30)], + "from and get_layers should roundtrip" ); assert_eq!( format!("{:?}", RenderLayersXX::from_layers(&[0, 1, 2, 3])).as_str(), @@ -517,7 +533,7 @@ mod rendering_mask_tests { ); assert_eq!( RenderLayersXX::from_layers(&[0, 1, 2]), - >::from_iter(vec![0, 1, 2]), + >::from_iter(vec![0, 1, 2]), "from_layers and from_iter are equivalent" ); } From 646c53b59bfdf82b1fb1eabe4b72e8f57c2ddd9c Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 01:28:54 -0500 Subject: [PATCH 09/54] propagate render groups compiles --- crates/bevy_render/src/view/visibility/mod.rs | 4 +- .../visibility/propagate_render_groups.rs | 205 ++++++++++-------- 2 files changed, 117 insertions(+), 92 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index d42567cbc5226..9e95345b02563 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,9 +1,9 @@ -//mod propagate_render_groups; +mod propagate_render_groups; mod render_groups; mod render_layers; use bevy_derive::Deref; -//pub use propagate_render_groups::*; +pub use propagate_render_groups::*; pub use render_groups::*; pub use render_layers::*; diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 7fdb11dc48d22..d18e2cb752c42 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -128,6 +128,17 @@ The base-line performance cost of this algorithm comes from iterating in order t - `RemovedComponents` is iterated once. */ +use crate::view::{CameraView, RenderGroups}; + +use bevy_app::{App, PostUpdate, Plugin}; +use bevy_derive::{Deref, DerefMut}; +use bevy_ecs::entity::EntityHashSet; +use bevy_ecs::prelude::*; +use bevy_hierarchy::{Children, Parent}; +use bevy_utils::error_once; +use bevy_utils::tracing::warn; +use crate::prelude::Camera; + /// Returned by [`PropagateRenderGroups::get_render_groups`]. pub enum PropagatingRenderGroups<'a> { Ref(&'a RenderGroups), @@ -171,13 +182,13 @@ pub enum PropagateRenderGroups } impl PropagateRenderGroups { - pub fn get_render_groups( - &self, + pub fn get_render_groups<'a>( + &'a self, entity: Entity, - groups: Option<&RenderGroups>, + groups: Option<&'a RenderGroups>, view: Option<&CameraView>, is_camera: bool, - ) -> PropagatingRenderGroups<'_> { + ) -> PropagatingRenderGroups<'a> { match self { Self::Auto => { @@ -200,7 +211,8 @@ impl PropagateRenderGroups { warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); PropagatingRenderGroups::Val(RenderGroups::empty()); }; - let view = view.unwrap_or(&CameraView::empty()); + let empty_view = CameraView::empty(); + let view = view.unwrap_or(&empty_view); PropagatingRenderGroups::Val(view.get_groups(entity)) } Self::Custom(groups) => @@ -231,23 +243,29 @@ pub struct InheritedRenderGroups /// /// This is cached so children of this entity can update themselves without needing to traverse the /// entire hierarchy. - pub propagater: Entity, - /// The [`RenderGroups`] computed by merging the [`RenderGroups`] of the `Self::propagater` entity into + pub propagator: Entity, + /// The [`RenderGroups`] computed by merging the [`RenderGroups`] of the `Self::propagator` entity into /// the node's [`RenderGroups`] component. /// /// This is cached for efficient access in the [`check_visibility`] system. pub computed: RenderGroups, -}; +} + +impl InheritedRenderGroups { + fn empty() -> Self { + Self{ propagator: Entity::PLACEHOLDER, computed: RenderGroups::empty() } + } +} /// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on /// entities. -#[derive(SystemSet, Debug, Hash, Eq, PartialEq)] +#[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] pub struct PropagateRenderGroupsSet; pub(crate) struct PropagateRenderGroupsPlugin; impl Plugin for PropagateRenderGroupsPlugin { - fn build(app: &mut App) { + fn build(&self, app: &mut App) { app.init_resource::() .add_systems(PostUpdate, ( @@ -268,7 +286,7 @@ impl Plugin for PropagateRenderGroupsPlugin { } #[derive(Resource, Default, Deref, DerefMut)] -struct PropagateRenderGroupsEntityCache(EntityHashMap); +struct PropagateRenderGroupsEntityCache(EntityHashSet); /// Removes InheritedRenderGroups from entities with PropagateRenderGroups. fn clean_propagators( @@ -276,7 +294,7 @@ fn clean_propagators( dirty_propagators: Query, With)> ){ for dirty in dirty_propagators.iter() { - commands.get_entity(dirty).map(|e| e.remove::()) + commands.get_entity(dirty).map(|mut e| { e.remove::(); }); } } @@ -295,12 +313,12 @@ fn propagate_updated_propagators( Has, &PropagateRenderGroups, ), - (Or<( + Or<( Changed, Changed, Added, Changed, - )>) + )> >, // Detect removed: RenderGroups, CameraView, Camera. mut removed_rendergroups: RemovedComponents, @@ -333,14 +351,14 @@ fn propagate_updated_propagators( updated_entities.clear(); // Collect aggregate iterator for all propagators that need to propagate. - let mut propagators = changed_propagators.iter() + let propagators = changed_propagators.iter() .chain( // IMPORTANT: Removals should be ordered first if propagate_to_new_children is merged // into changed_propagators. removed_rendergroups.read() .chain(removed_cameraview.read()) .chain(removed_camera.read()) - .filter_map(|e| all_propagators.get(*e).ok()) + .filter_map(|e| all_propagators.get(e).ok()) ); // Propagate each propagator. @@ -359,7 +377,7 @@ fn propagate_updated_propagators( // Get value to propagate. // TODO: This can allocate spuriously if there are no children that need it. - let propagated = propagate.get_render_groups( + let propagated: PropagatingRenderGroups<'_> = propagate.get_render_groups( propagator, maybe_render_groups, maybe_camera_view, @@ -369,14 +387,14 @@ fn propagate_updated_propagators( // Propagate updated_entities.insert(propagator); - for child in children.iter() { + for child in children.iter().copied() { apply_full_propagation( &mut commands, &mut updated_entities, &children_query, &mut maybe_inherited, propagator, - propagated, + propagated.get(), child ); } @@ -398,12 +416,13 @@ fn apply_full_propagation( entity: Entity, ) { // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((maybe_render_groups, mut maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + let Ok((maybe_render_groups, maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { return; - } + }; // Update inherited value or insert a new one. - let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); + let empty_render_groups = RenderGroups::empty(); + let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); let apply_changes = |groups: &mut InheritedRenderGroups| { groups.propagator = propagator; groups.computed.set_from(initial_groups); @@ -415,7 +434,7 @@ fn apply_full_propagation( } else { let mut new = InheritedRenderGroups::empty(); apply_changes(&mut new); - commands.get(entity).insert(new); + commands.entity(entity).insert(new); } // Mark as updated. @@ -425,7 +444,7 @@ fn apply_full_propagation( let Ok(children) = children_query.get(entity) else { return; }; - for child in children.iter() { + for child in children.iter().copied() { apply_full_propagation( commands, updated_entities, @@ -491,14 +510,14 @@ fn propagate_to_new_children( // Propagate updated_entities.insert(propagator); - for child in children.iter() { + for child in children.iter().copied() { apply_new_children_propagation( &mut commands, &mut updated_entities, &children_query, &mut maybe_inherited, propagator, - propagated, + propagated.get(), child ); } @@ -520,17 +539,18 @@ fn apply_new_children_propagation( entity: Entity, ) { // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((maybe_render_groups, mut maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + let Ok((maybe_render_groups, maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { return; - } + }; // Leave if the propagator is already known (implying this is a pre-existing child). - if maybe_inherited_groups.map(|i| i.propagator == propagator).unwrap_or(false) { + if maybe_inherited_groups.as_ref().map(|i| i.propagator == propagator).unwrap_or(false) { return; } // Update inherited value or insert a new one. - let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); + let empty_render_groups = RenderGroups::empty(); + let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); let apply_changes = |groups: &mut InheritedRenderGroups| { groups.propagator = propagator; groups.computed.set_from(initial_groups); @@ -542,7 +562,7 @@ fn apply_new_children_propagation( } else { let mut new = InheritedRenderGroups::empty(); apply_changes(&mut new); - commands.get(entity).insert(new); + commands.entity(entity).insert(new); } // Mark as updated. @@ -552,7 +572,7 @@ fn apply_new_children_propagation( let Ok(children) = children_query.get(entity) else { return; }; - for child in children.iter() { + for child in children.iter().copied() { apply_new_children_propagation( commands, updated_entities, @@ -570,7 +590,7 @@ fn handle_orphaned_nonpropagators( mut commands: Commands, mut updated_entities: ResMut, // Orphaned non-propagator entities that previously had InheritedRenderGroups. - mut removed_parents: RemovedComponents, + mut removed_parents: RemovedComponents, orphaned: Query< (Entity, Option<&Children>), (Without, With, Without), @@ -578,7 +598,7 @@ fn handle_orphaned_nonpropagators( // Query for getting non-propagator children. nonpropagators: Query, Without>, ) { - for (orphan, maybe_children) in removed_parents.read().filter_map(|r| orphaned.get(r)) { + for (orphan, maybe_children) in removed_parents.read().filter_map(|r| orphaned.get(r).ok()) { apply_orphan_cleanup(&mut commands, &mut updated_entities, &nonpropagators, orphan, maybe_children); } } @@ -592,16 +612,16 @@ fn apply_orphan_cleanup( maybe_children: Option<&Children>, ){ // Remove InheritedRenderGroups. - commands.get_entity(entity).map(|e| e.remove::()); + commands.get_entity(entity).map(|mut e| { e.remove::(); }); // Mark as updated. updated_entities.insert(entity); // Update non-propagator children. let Some(children) = maybe_children else { - continue; + return; }; - for child in children.iter() { + for child in children.iter().copied() { // Ignore children that have PropagateRenderGroups. let Ok(maybe_children) = nonpropagators.get(child) else { continue; @@ -636,7 +656,7 @@ fn handle_lost_propagator( Without, >, ) { - for (entity, maybe_parent) in removed_propagate.read().filter_map(|r| unpropagated.get(r)) { + for (entity, maybe_parent) in removed_propagate.read().filter_map(|r| unpropagated.get(r).ok()) { // Skip already-updated entities. if updated_entities.contains(&entity) { continue; @@ -674,13 +694,13 @@ fn handle_lost_propagator( // Check cases where a value should be propagated. let propagator = if let Some(parent) = maybe_parent { - if let Ok((propagator, ..)) = all_propagators.get(parent) { + if let Ok((propagator, ..)) = all_propagators.get(**parent) { // Case 5 Some(propagator) - } else if updated_entities.contains(parent){ + } else if updated_entities.contains(&**parent){ // Parent was marked updated (but self was not) - let Ok((_, maybe_inherited)) = maybe_inherited.get(entity) { - let Some(inherited) = maybe_inherited { + if let Ok((_, maybe_inherited)) = maybe_inherited.get(entity) { + if let Some(inherited) = maybe_inherited { // Case 4-2, 4-3 Some(inherited.propagator) } else { @@ -693,8 +713,8 @@ fn handle_lost_propagator( } } else { // Parent was not marked updated - let Ok((_, maybe_inherited)) = maybe_inherited.get(parent) { - let Some(inherited) = maybe_inherited { + if let Ok((_, maybe_inherited)) = maybe_inherited.get(**parent) { + if let Some(inherited) = maybe_inherited { // Case 3-1, 3-2 Some(inherited.propagator) } else { @@ -714,13 +734,13 @@ fn handle_lost_propagator( // Propagate if possible // - Case 3-2, 4-3 // - Cases 3-1, 4-2 are filtered out here. - let Some(( + if let Some(( propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate - )) = propagator.filter_map(|p| all_propagators.get(p)) { + )) = propagator.and_then(|p| all_propagators.get(p).ok()) { // Propagation value // TODO: This can allocate spuriously if there are no children that need it. let propagated = propagate.get_render_groups( @@ -743,7 +763,7 @@ fn handle_lost_propagator( &children_query, &mut maybe_inherited, propagator, - propagated, + propagated.get(), entity ); // In all other cases, remove all InheritedRenderGroups. @@ -752,7 +772,7 @@ fn handle_lost_propagator( &mut commands, &mut updated_entities, &children_query, - &mut maybe_inherited, + &maybe_inherited, entity ); } @@ -773,30 +793,31 @@ fn apply_full_propagation_force_update( entity: Entity, ) { // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((maybe_render_groups, mut maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + let Ok((maybe_render_groups, maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { return; - } + }; // Leave if entity is non-updated and inherits a matching propagator. - if let Some(inherited) = maybe_inherited_groups { - if (inherited.propagator == propagator) && !updated_entities.contains(entity) { + if let Some(inherited) = maybe_inherited_groups.as_ref() { + if (inherited.propagator == propagator) && !updated_entities.contains(&entity) { return; } } // Force-update - let initial_groups = maybe_render_groups.unwrap_or(&RenderGroups::empty()); + let empty_render_groups = RenderGroups::empty(); + let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); let mut new = InheritedRenderGroups::empty(); if let Some(mut inherited) = maybe_inherited_groups { // Steal existing allocation if possible. - std::mem::swap(&mut inherited, &mut new); + std::mem::swap(&mut *inherited, &mut new); } new.propagator = propagator; new.computed.set_from(initial_groups); new.computed.merge(propagated); - commands.get(entity).insert(new); + commands.entity(entity).insert(new); // Mark as updated. updated_entities.insert(entity); @@ -805,7 +826,7 @@ fn apply_full_propagation_force_update( let Ok(children) = children_query.get(entity) else { return; }; - for child in children.iter() { + for child in children.iter().copied() { apply_full_propagation_force_update( commands, updated_entities, @@ -824,24 +845,24 @@ fn apply_full_propagation_force_remove( commands: &mut Commands, updated_entities: &mut PropagateRenderGroupsEntityCache, children_query: &Query<&Children>, - maybe_inherited: &mut Query< + maybe_inherited: &Query< (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), Without, >, entity: Entity, ) { // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((_, maybe_inherited: Option<&mut InheritedRenderGroups>)) = maybe_inherited.get(entity) else { + let Ok((_, maybe_inherited_inner)) = maybe_inherited.get(entity) else { return; - } + }; // Leave if entity is non-updated and doesn't have InheritedRenderGroups. - if maybe_inherited.is_none() && !updated_entities.contains(entity) { + if maybe_inherited_inner.is_none() && !updated_entities.contains(&entity) { return; } // Force-remove InheritedRenderGroups - commands.get(entity).remove::(); + commands.entity(entity).remove::(); // Mark as updated. updated_entities.insert(entity); @@ -850,7 +871,7 @@ fn apply_full_propagation_force_remove( let Ok(children) = children_query.get(entity) else { return; }; - for child in children.iter() { + for child in children.iter().copied() { apply_full_propagation_force_remove(commands, updated_entities, children_query, maybe_inherited, child); } } @@ -884,35 +905,35 @@ fn handle_new_children_nonpropagator( ) { for (entity, children, inherited) in inherited_with_children.iter() { // Skip entity if already updated, which implies children are already in an accurate state. - if updated_entities.contains(entity) { + if updated_entities.contains(&entity) { continue; } - let Some(( + let Ok(( propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate - )) = inherited.propagator.filter_map(|p| all_propagators.get(p)) else { + )) = all_propagators.get(inherited.propagator) else { // Remove InheritedRenderGroups from descendents if the propagator is missing // - This is either an error caused by manually modifying InheritedRenderGroups, or is caused by a // reparenting + propagator despawn. // Iterate children - for child in children.iter() { + for child in children.iter().copied() { // Skip children that were already updated. // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. - if updated_entities.contains(child) { + if updated_entities.contains(&child) { continue; } // Propagate apply_full_propagation_force_remove( - commands, - updated_entities, - children_query, - maybe_inherited, + &mut commands, + &mut updated_entities, + &children_query, + &maybe_inherited, child ); } @@ -930,23 +951,23 @@ fn handle_new_children_nonpropagator( ); // Iterate children - for child in children.iter() { + for child in children.iter().copied() { // Skip children that were already updated. We only skip updated children of this initial high-level // loop, not children within the recursion which need to be force-updated. The 'span' of entities // we update in this step starts at non-updated children of an entity with InheritedRenderGroups. // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. - if updated_entities.contains(child) { + if updated_entities.contains(&child) { continue; } // Propagate apply_full_propagation_force_update( - commands, - updated_entities, - children_query, - maybe_inherited, + &mut commands, + &mut updated_entities, + &children_query, + &mut maybe_inherited, propagator, - propagated, + propagated.get(), child ); } @@ -969,26 +990,26 @@ fn handle_new_parent_nonpropagator( (Changed, With, Without) >, // Query for Children - children_query: &Query<&Children>, + children_query: Query<&Children>, // Query for updating InheritedRenderGroups on non-propagator entities. - maybe_inherited: &mut Query< + maybe_inherited: Query< (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), Without, >, ) { for (entity, maybe_children, parent) in inherited_with_parent.iter() { // Skip entity if already updated - if updated_entities.contains(entity) { + if updated_entities.contains(&entity) { continue; } // Skip entity if parent was updated - if updated_entities.contains(*parent) { + if updated_entities.contains(&**parent) { continue; } // Remove from self. - commands.get(entity).remove::(); + commands.entity(entity).remove::(); // Mark as updated. updated_entities.insert(entity); @@ -999,7 +1020,7 @@ fn handle_new_parent_nonpropagator( let Some(children) = maybe_children else { continue; }; - for child in children.iter() { + for child in children.iter().copied() { apply_full_propagation_force_remove( &mut commands, &mut updated_entities, @@ -1032,25 +1053,25 @@ fn handle_modified_rendergroups( ) >, // Query for updating InheritedRenderGroups on non-propagator entities. - maybe_inherited: &mut Query< + mut maybe_inherited: Query< (Option<&RenderGroups>, &mut InheritedRenderGroups), Without, >, ) { - for entity in inherited_changed.iter().zip(removed_rendergroups.read()) { + for entity in inherited_changed.iter().chain(removed_rendergroups.read()) { // Skip entity if already updated. - if updated_entities.contains(entity) { + if updated_entities.contains(&entity) { continue; } // Skip entity if it's a propagator or doesn't exist. - let Ok((maybe_render_groups, mut inherited)) = maybe_inherited.get(entity) else { + let Ok((entity_render_groups, mut inherited)) = maybe_inherited.get_mut(entity) else { continue; }; // Skip entity if propagator is missing. // - This is an error, hierarchy steps should have marked this entity as updated. - let Some(( + let Ok(( propagator, maybe_render_groups, maybe_camera_view, @@ -1070,6 +1091,10 @@ fn handle_modified_rendergroups( ); // Update entity value. - inherited.computed.merge(propagated); + inherited.computed.set_from(entity_render_groups.unwrap_or(&RenderGroups::default())); + inherited.computed.merge(propagated.get()); + + // Mark updated (in case of duplicates due to removals). + updated_entities.insert(entity); } } From f07b7a57f81a5181d64283182cea1efc425afb64 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 01:31:09 -0500 Subject: [PATCH 10/54] fmt --- crates/bevy_render/src/view/visibility/mod.rs | 46 +-- .../visibility/propagate_render_groups.rs | 279 +++++++++--------- .../src/view/visibility/render_groups.rs | 133 ++++++--- 3 files changed, 251 insertions(+), 207 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 9e95345b02563..914ab923161e2 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -217,14 +217,14 @@ impl Plugin for VisibilityPlugin { fn build(&self, app: &mut bevy_app::App) { use VisibilitySystems::*; -/* - app.configure_sets( - PostUpdate, - PropagateRenderGroupsSet - .in_set(VisibilityPropagate), - ) -*/ -app .add_systems( + /* + app.configure_sets( + PostUpdate, + PropagateRenderGroupsSet + .in_set(VisibilityPropagate), + ) + */ + app.add_systems( PostUpdate, ( calculate_bounds.in_set(CalculateBounds), @@ -384,11 +384,11 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { pub fn check_visibility( mut thread_queues: Local>>, mut view_query: Query<( -// Entity, + // Entity, &mut VisibleEntities, &Frustum, Option<&RenderLayers>, -// Option<&CameraView>, + // Option<&CameraView>, &Camera, )>, mut visible_aabb_query: Query<( @@ -396,22 +396,22 @@ pub fn check_visibility( &InheritedVisibility, &mut ViewVisibility, Option<&RenderLayers>, -// Option<&RenderGroups>, -// Option<&InheritedRenderGroups>, + // Option<&RenderGroups>, + // Option<&InheritedRenderGroups>, Option<&Aabb>, &GlobalTransform, Has, )>, deterministic_rendering_config: Res, ) { -// for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { - for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query { + // for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { + 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(); -// let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); + // let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); visible_entities.entities.clear(); visible_aabb_query.par_iter_mut().for_each(|query_item| { @@ -420,8 +420,8 @@ pub fn check_visibility( inherited_visibility, mut view_visibility, maybe_entity_mask, -// maybe_groups, -// maybe_inherited_groups, + // maybe_groups, + // maybe_inherited_groups, maybe_model_aabb, transform, no_frustum_culling, @@ -435,12 +435,12 @@ pub fn check_visibility( let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); if !view_mask.intersects(&entity_mask) { -/* - // Check render groups. - let entity_groups = maybe_inherited_groups.map(|i| &i.computed) - .or(maybe_groups) - .unwrap_or(&RenderGroups::default()); - if !camera_view.entity_is_visible(camera_entity, entity_groups) { */ + /* + // Check render groups. + let entity_groups = maybe_inherited_groups.map(|i| &i.computed) + .or(maybe_groups) + .unwrap_or(&RenderGroups::default()); + if !camera_view.entity_is_visible(camera_entity, entity_groups) { */ return; } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index d18e2cb752c42..df75ef2aea147 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -130,14 +130,14 @@ The base-line performance cost of this algorithm comes from iterating in order t use crate::view::{CameraView, RenderGroups}; -use bevy_app::{App, PostUpdate, Plugin}; +use crate::prelude::Camera; +use bevy_app::{App, Plugin, PostUpdate}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::entity::EntityHashSet; use bevy_ecs::prelude::*; use bevy_hierarchy::{Children, Parent}; use bevy_utils::error_once; use bevy_utils::tracing::warn; -use crate::prelude::Camera; /// Returned by [`PropagateRenderGroups::get_render_groups`]. pub enum PropagatingRenderGroups<'a> { @@ -161,8 +161,7 @@ impl<'a> PropagatingRenderGroups<'a> { /// /// See [`RenderGroups`] and [`CameraView`]. #[derive(Component)] -pub enum PropagateRenderGroups -{ +pub enum PropagateRenderGroups { /// If the entity has a [`RenderGroups`] component, that value is propagated, otherwise a default /// [`RenderGroups`] is propagated. /// @@ -190,23 +189,20 @@ impl PropagateRenderGroups { is_camera: bool, ) -> PropagatingRenderGroups<'a> { match self { - Self::Auto => - { + Self::Auto => { let Some(groups) = groups else { return PropagatingRenderGroups::Val(RenderGroups::default()); }; PropagatingRenderGroups::Ref(groups) } - Self::Camera => - { + Self::Camera => { if !is_camera { warn!("failed propagating PropagateRenderGroups::Camera, {entity} doesn't have a camera"); PropagatingRenderGroups::Val(RenderGroups::empty()); }; PropagatingRenderGroups::Val(RenderGroups::new_with_camera(entity)) } - Self::CameraWithView => - { + Self::CameraWithView => { if !is_camera { warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); PropagatingRenderGroups::Val(RenderGroups::empty()); @@ -215,10 +211,7 @@ impl PropagateRenderGroups { let view = view.unwrap_or(&empty_view); PropagatingRenderGroups::Val(view.get_groups(entity)) } - Self::Custom(groups) => - { - PropagatingRenderGroups::Ref(groups) - } + Self::Custom(groups) => PropagatingRenderGroups::Ref(groups), } } } @@ -237,8 +230,7 @@ impl PropagateRenderGroups { /// (see [`RenderGroups::merge`]). /// This means the entity's affiliated camera will be prioritized over the propagated affiliated camera. #[derive(Component, Debug, Clone)] -pub struct InheritedRenderGroups -{ +pub struct InheritedRenderGroups { /// The entity that propagated a [`RenderGroups`] to this entity. /// /// This is cached so children of this entity can update themselves without needing to traverse the @@ -253,7 +245,10 @@ pub struct InheritedRenderGroups impl InheritedRenderGroups { fn empty() -> Self { - Self{ propagator: Entity::PLACEHOLDER, computed: RenderGroups::empty() } + Self { + propagator: Entity::PLACEHOLDER, + computed: RenderGroups::empty(), + } } } @@ -267,7 +262,8 @@ pub(crate) struct PropagateRenderGroupsPlugin; impl Plugin for PropagateRenderGroupsPlugin { fn build(&self, app: &mut App) { app.init_resource::() - .add_systems(PostUpdate, + .add_systems( + PostUpdate, ( clean_propagators, propagate_updated_propagators, @@ -277,10 +273,10 @@ impl Plugin for PropagateRenderGroupsPlugin { handle_new_children_nonpropagator, handle_new_parent_nonpropagator, apply_deferred, - handle_modified_rendergroups, //does not have deferred commands + handle_modified_rendergroups, //does not have deferred commands ) .chain() - .in_set(PropagateRenderGroupsSet) + .in_set(PropagateRenderGroupsSet), ); } } @@ -291,10 +287,12 @@ struct PropagateRenderGroupsEntityCache(EntityHashSet); /// Removes InheritedRenderGroups from entities with PropagateRenderGroups. fn clean_propagators( mut commands: Commands, - dirty_propagators: Query, With)> -){ + dirty_propagators: Query, With)>, +) { for dirty in dirty_propagators.iter() { - commands.get_entity(dirty).map(|mut e| { e.remove::(); }); + commands.get_entity(dirty).map(|mut e| { + e.remove::(); + }); } } @@ -318,22 +316,20 @@ fn propagate_updated_propagators( Changed, Added, Changed, - )> + )>, >, // Detect removed: RenderGroups, CameraView, Camera. mut removed_rendergroups: RemovedComponents, mut removed_cameraview: RemovedComponents, mut removed_camera: RemovedComponents, - all_propagators: Query< - ( - Entity, - &Children, - Option<&RenderGroups>, - Option<&CameraView>, - Has, - &PropagateRenderGroups, - ) - >, + all_propagators: Query<( + Entity, + &Children, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + )>, // Query for getting Children. children_query: Query<&Children>, // Query for updating InheritedRenderGroups on non-propagator entities. @@ -351,25 +347,20 @@ fn propagate_updated_propagators( updated_entities.clear(); // Collect aggregate iterator for all propagators that need to propagate. - let propagators = changed_propagators.iter() - .chain( - // IMPORTANT: Removals should be ordered first if propagate_to_new_children is merged - // into changed_propagators. - removed_rendergroups.read() - .chain(removed_cameraview.read()) - .chain(removed_camera.read()) - .filter_map(|e| all_propagators.get(e).ok()) - ); + let propagators = changed_propagators.iter().chain( + // IMPORTANT: Removals should be ordered first if propagate_to_new_children is merged + // into changed_propagators. + removed_rendergroups + .read() + .chain(removed_cameraview.read()) + .chain(removed_camera.read()) + .filter_map(|e| all_propagators.get(e).ok()), + ); // Propagate each propagator. - for ( - propagator, - children, - maybe_render_groups, - maybe_camera_view, - maybe_camera, - propagate, - ) in propagators { + for (propagator, children, maybe_render_groups, maybe_camera_view, maybe_camera, propagate) in + propagators + { // There can be duplicates due to component removals. if updated_entities.contains(&propagator) { continue; @@ -381,7 +372,7 @@ fn propagate_updated_propagators( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera + maybe_camera, ); // Propagate @@ -395,7 +386,7 @@ fn propagate_updated_propagators( &mut maybe_inherited, propagator, propagated.get(), - child + child, ); } } @@ -452,7 +443,7 @@ fn apply_full_propagation( maybe_inherited, propagator, propagated, - child + child, ); } } @@ -485,14 +476,9 @@ fn propagate_to_new_children( >, ) { // Propagate each propagator that has new children. - for ( - propagator, - children, - maybe_render_groups, - maybe_camera_view, - maybe_camera, - propagate, - ) in changed_children.iter() { + for (propagator, children, maybe_render_groups, maybe_camera_view, maybe_camera, propagate) in + changed_children.iter() + { // The propagator could have been updated in a previous step. if updated_entities.contains(&propagator) { continue; @@ -504,7 +490,7 @@ fn propagate_to_new_children( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera + maybe_camera, ); // Propagate @@ -518,7 +504,7 @@ fn propagate_to_new_children( &mut maybe_inherited, propagator, propagated.get(), - child + child, ); } } @@ -544,7 +530,11 @@ fn apply_new_children_propagation( }; // Leave if the propagator is already known (implying this is a pre-existing child). - if maybe_inherited_groups.as_ref().map(|i| i.propagator == propagator).unwrap_or(false) { + if maybe_inherited_groups + .as_ref() + .map(|i| i.propagator == propagator) + .unwrap_or(false) + { return; } @@ -580,7 +570,7 @@ fn apply_new_children_propagation( maybe_inherited, propagator, propagated, - child + child, ); } } @@ -593,13 +583,23 @@ fn handle_orphaned_nonpropagators( mut removed_parents: RemovedComponents, orphaned: Query< (Entity, Option<&Children>), - (Without, With, Without), + ( + Without, + With, + Without, + ), >, // Query for getting non-propagator children. nonpropagators: Query, Without>, ) { for (orphan, maybe_children) in removed_parents.read().filter_map(|r| orphaned.get(r).ok()) { - apply_orphan_cleanup(&mut commands, &mut updated_entities, &nonpropagators, orphan, maybe_children); + apply_orphan_cleanup( + &mut commands, + &mut updated_entities, + &nonpropagators, + orphan, + maybe_children, + ); } } @@ -610,9 +610,11 @@ fn apply_orphan_cleanup( nonpropagators: &Query, Without>, entity: Entity, maybe_children: Option<&Children>, -){ +) { // Remove InheritedRenderGroups. - commands.get_entity(entity).map(|mut e| { e.remove::(); }); + commands.get_entity(entity).map(|mut e| { + e.remove::(); + }); // Mark as updated. updated_entities.insert(entity); @@ -627,7 +629,13 @@ fn apply_orphan_cleanup( continue; }; - apply_orphan_cleanup(commands, updated_entities, nonpropagators, child, maybe_children); + apply_orphan_cleanup( + commands, + updated_entities, + nonpropagators, + child, + maybe_children, + ); } } @@ -639,15 +647,13 @@ fn handle_lost_propagator( mut removed_propagate: RemovedComponents, unpropagated: Query<(Entity, Option<&Parent>), Without>, // Query for accessing propagators - all_propagators: Query< - ( - Entity, - Option<&RenderGroups>, - Option<&CameraView>, - Has, - &PropagateRenderGroups, - ) - >, + all_propagators: Query<( + Entity, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + )>, // Query for getting Children. children_query: Query<&Children>, // Query for updating InheritedRenderGroups on non-propagator entities. @@ -656,7 +662,10 @@ fn handle_lost_propagator( Without, >, ) { - for (entity, maybe_parent) in removed_propagate.read().filter_map(|r| unpropagated.get(r).ok()) { + for (entity, maybe_parent) in removed_propagate + .read() + .filter_map(|r| unpropagated.get(r).ok()) + { // Skip already-updated entities. if updated_entities.contains(&entity) { continue; @@ -697,7 +706,7 @@ fn handle_lost_propagator( if let Ok((propagator, ..)) = all_propagators.get(**parent) { // Case 5 Some(propagator) - } else if updated_entities.contains(&**parent){ + } else if updated_entities.contains(&**parent) { // Parent was marked updated (but self was not) if let Ok((_, maybe_inherited)) = maybe_inherited.get(entity) { if let Some(inherited) = maybe_inherited { @@ -734,20 +743,16 @@ fn handle_lost_propagator( // Propagate if possible // - Case 3-2, 4-3 // - Cases 3-1, 4-2 are filtered out here. - if let Some(( - propagator, - maybe_render_groups, - maybe_camera_view, - maybe_camera, - propagate - )) = propagator.and_then(|p| all_propagators.get(p).ok()) { + if let Some((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = + propagator.and_then(|p| all_propagators.get(p).ok()) + { // Propagation value // TODO: This can allocate spuriously if there are no children that need it. let propagated = propagate.get_render_groups( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera + maybe_camera, ); // Pre-update the entity as a hack for case 4-3. If we don't do this then @@ -764,7 +769,7 @@ fn handle_lost_propagator( &mut maybe_inherited, propagator, propagated.get(), - entity + entity, ); // In all other cases, remove all InheritedRenderGroups. } else { @@ -773,7 +778,7 @@ fn handle_lost_propagator( &mut updated_entities, &children_query, &maybe_inherited, - entity + entity, ); } } @@ -834,7 +839,7 @@ fn apply_full_propagation_force_update( maybe_inherited, propagator, propagated, - child + child, ); } } @@ -872,7 +877,13 @@ fn apply_full_propagation_force_remove( return; }; for child in children.iter().copied() { - apply_full_propagation_force_remove(commands, updated_entities, children_query, maybe_inherited, child); + apply_full_propagation_force_remove( + commands, + updated_entities, + children_query, + maybe_inherited, + child, + ); } } @@ -883,18 +894,16 @@ fn handle_new_children_nonpropagator( // Entities with InheritedRenderGroups that changed children inherited_with_children: Query< (Entity, &Children, &InheritedRenderGroups), - (Changed, Without) + (Changed, Without), >, // Query for accessing propagators - all_propagators: Query< - ( - Entity, - Option<&RenderGroups>, - Option<&CameraView>, - Has, - &PropagateRenderGroups, - ) - >, + all_propagators: Query<( + Entity, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + )>, // Query for getting Children. children_query: Query<&Children>, // Query for updating InheritedRenderGroups on non-propagator entities. @@ -909,13 +918,9 @@ fn handle_new_children_nonpropagator( continue; } - let Ok(( - propagator, - maybe_render_groups, - maybe_camera_view, - maybe_camera, - propagate - )) = all_propagators.get(inherited.propagator) else { + let Ok((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = + all_propagators.get(inherited.propagator) + else { // Remove InheritedRenderGroups from descendents if the propagator is missing // - This is either an error caused by manually modifying InheritedRenderGroups, or is caused by a // reparenting + propagator despawn. @@ -934,7 +939,7 @@ fn handle_new_children_nonpropagator( &mut updated_entities, &children_query, &maybe_inherited, - child + child, ); } @@ -947,7 +952,7 @@ fn handle_new_children_nonpropagator( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera + maybe_camera, ); // Iterate children @@ -968,7 +973,7 @@ fn handle_new_children_nonpropagator( &mut maybe_inherited, propagator, propagated.get(), - child + child, ); } } @@ -987,7 +992,11 @@ fn handle_new_parent_nonpropagator( // Entities with InheritedRenderGroups that changed parents inherited_with_parent: Query< (Entity, Option<&Children>, &Parent), - (Changed, With, Without) + ( + Changed, + With, + Without, + ), >, // Query for Children children_query: Query<&Children>, @@ -1026,7 +1035,7 @@ fn handle_new_parent_nonpropagator( &mut updated_entities, &children_query, &maybe_inherited, - child + child, ); } } @@ -1038,20 +1047,22 @@ fn handle_modified_rendergroups( // Entities with InheritedRenderGroups that changed RenderGroups inherited_changed: Query< Entity, - (Changed, With, Without) + ( + Changed, + With, + Without, + ), >, // RenderGroups removals. mut removed_rendergroups: RemovedComponents, // Query for accessing propagators - all_propagators: Query< - ( - Entity, - Option<&RenderGroups>, - Option<&CameraView>, - Has, - &PropagateRenderGroups, - ) - >, + all_propagators: Query<( + Entity, + Option<&RenderGroups>, + Option<&CameraView>, + Has, + &PropagateRenderGroups, + )>, // Query for updating InheritedRenderGroups on non-propagator entities. mut maybe_inherited: Query< (Option<&RenderGroups>, &mut InheritedRenderGroups), @@ -1071,13 +1082,9 @@ fn handle_modified_rendergroups( // Skip entity if propagator is missing. // - This is an error, hierarchy steps should have marked this entity as updated. - let Ok(( - propagator, - maybe_render_groups, - maybe_camera_view, - maybe_camera, - propagate - )) = all_propagators.get(inherited.propagator) else { + let Ok((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = + all_propagators.get(inherited.propagator) + else { error_once!("hierarchy error: propagator missing for {entity} in `handle_modified_rendergroups`"); continue; }; @@ -1087,11 +1094,13 @@ fn handle_modified_rendergroups( propagator, maybe_render_groups, maybe_camera_view, - maybe_camera + maybe_camera, ); // Update entity value. - inherited.computed.set_from(entity_render_groups.unwrap_or(&RenderGroups::default())); + inherited + .computed + .set_from(entity_render_groups.unwrap_or(&RenderGroups::default())); inherited.computed.merge(propagated.get()); // Mark updated (in case of duplicates due to removals). diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index ed61de193b588..fde94150c071d 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -13,8 +13,7 @@ pub const DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deref, DerefMut)] pub struct RenderLayer(pub usize); -impl RenderLayer -{ +impl RenderLayer { /// Returns `true` if equal to [`DEFAULT_RENDER_LAYER`]. pub fn is_default(&self) -> bool { *self == DEFAULT_RENDER_LAYER @@ -49,15 +48,16 @@ impl Default for RenderLayer { /// `RenderLayersXX` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. #[derive(Debug, Clone, PartialEq)] -pub struct RenderLayersXX -{ +pub struct RenderLayersXX { layers: SmallVec<[u64; 1]>, } impl RenderLayersXX { /// Makes a new `RenderLayersXX` with no layers. pub fn empty() -> Self { - Self{ layers: SmallVec::default() } + Self { + layers: SmallVec::default(), + } } /// Makes a new `RenderLayersXX` from a slice. @@ -110,10 +110,7 @@ impl RenderLayersXX { pub fn merge(&mut self, other: &Self) { self.extend_buffer(other.layers.len()); - for (self_layer, other_layer) in self.layers - .iter_mut() - .zip(other.layers.iter()) - { + for (self_layer, other_layer) in self.layers.iter_mut().zip(other.layers.iter()) { *self_layer |= *other_layer; } } @@ -127,11 +124,7 @@ impl RenderLayersXX { /// Iterates the internal render layers. pub fn iter(&self) -> impl Iterator + '_ { - self.layers - .iter() - .copied() - .map(Self::iter_layers) - .flatten() + self.layers.iter().copied().map(Self::iter_layers).flatten() } /// Returns `true` if the specified render layer is included in this `RenderLayersXX`. @@ -145,10 +138,7 @@ impl RenderLayersXX { /// Returns `true` if `Self` and `other` contain any matching layers. pub fn intersects(&self, other: &Self) -> bool { - for (self_layer, other_layer) in self.layers - .iter() - .zip(other.layers.iter()) - { + for (self_layer, other_layer) in self.layers.iter().zip(other.layers.iter()) { if (*self_layer & *other_layer) != 0 { return true; } @@ -179,23 +169,23 @@ impl RenderLayersXX { fn iter_layers(mut buffer: u64) -> impl Iterator + 'static { let mut layer: usize = 0; - std::iter::from_fn( - move || { - if buffer == 0 { - return None; - } - let next = buffer.trailing_zeros() + 1; - buffer >>= next; - layer += next as usize; - Some(RenderLayer(layer - 1)) + std::iter::from_fn(move || { + if buffer == 0 { + return None; } - ) + let next = buffer.trailing_zeros() + 1; + buffer >>= next; + layer += next as usize; + Some(RenderLayer(layer - 1)) + }) } } impl> From for RenderLayersXX { fn from(layer: T) -> Self { - let mut layers = Self{ layers: SmallVec::default() }; + let mut layers = Self { + layers: SmallVec::default(), + }; layers.add(layer); layers } @@ -203,7 +193,10 @@ impl> From for RenderLayersXX { impl> FromIterator for RenderLayersXX { fn from_iter>(i: T) -> Self { - i.into_iter().fold(Self::empty(), |mut mask, g| { mask.add(g); mask }) + i.into_iter().fold(Self::empty(), |mut mask, g| { + mask.add(g); + mask + }) } } @@ -254,8 +247,7 @@ entity.insert(groups); /// entity won't be visible to layer 0. */ #[derive(Component, Debug, Clone)] -pub struct RenderGroups -{ +pub struct RenderGroups { layers: RenderLayersXX, camera: Option, } @@ -263,12 +255,18 @@ pub struct RenderGroups impl RenderGroups { /// Makes a new `RenderGroups` with no groups. pub fn empty() -> Self { - Self{ layers: RenderLayersXX::empty(), camera: None } + Self { + layers: RenderLayersXX::empty(), + camera: None, + } } /// Makes a new `RenderGroups` with just a camera. pub fn new_with_camera(camera: Entity) -> Self { - Self{ layers: RenderLayersXX::empty(), camera: Some(camera) } + Self { + layers: RenderLayersXX::empty(), + camera: Some(camera), + } } /// Adds a [`RenderLayer`]. @@ -360,14 +358,20 @@ impl RenderGroups { impl From for RenderGroups { /// Makes a new `RenderGroups` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { - Self{ layers: RenderLayersXX::from(layer), camera: None } + Self { + layers: RenderLayersXX::from(layer), + camera: None, + } } } impl From for RenderGroups { /// Makes a new `RenderGroups` from a [`RenderLayersXX`]. fn from(layers: RenderLayersXX) -> Self { - Self{ layers, camera: None } + Self { + layers, + camera: None, + } } } @@ -395,15 +399,16 @@ impl Default for RenderGroups { /// /// A default `CameraView` will include the [`DEFAULT_RENDER_LAYER`]. #[derive(Component, Debug, Clone)] -pub struct CameraView -{ +pub struct CameraView { layers: RenderLayersXX, } impl CameraView { /// Makes a new `CameraView` with no visible [`RenderLayer`]. pub fn empty() -> Self { - Self{ layers: RenderLayersXX::empty() } + Self { + layers: RenderLayersXX::empty(), + } } /// Adds a [`RenderLayer`]. @@ -460,7 +465,9 @@ impl CameraView { impl From for CameraView { /// Makes a new `CameraView` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { - Self{ layers: RenderLayersXX::from(layer) } + Self { + layers: RenderLayersXX::from(layer), + } } } @@ -473,18 +480,39 @@ impl Default for CameraView { #[cfg(test)] mod rendering_mask_tests { + use super::{RenderLayer, RenderLayersXX, DEFAULT_RENDER_LAYER}; use smallvec::SmallVec; - use super::{DEFAULT_RENDER_LAYER, RenderLayer, RenderLayersXX}; #[test] fn rendering_mask_sanity() { - assert_eq!(RenderLayersXX::default().num_layers(), 1, "default layer contains only one layer"); - assert!(RenderLayersXX::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default"); - assert_eq!(RenderLayersXX::from(RenderLayer(1)).num_layers(), 1, "from contains 1 layer"); - assert!(RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate"); - assert!(!RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected"); + assert_eq!( + RenderLayersXX::default().num_layers(), + 1, + "default layer contains only one layer" + ); + assert!( + RenderLayersXX::default().contains(DEFAULT_RENDER_LAYER), + "default layer contains default" + ); + assert_eq!( + RenderLayersXX::from(RenderLayer(1)).num_layers(), + 1, + "from contains 1 layer" + ); + assert!( + RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), + "contains is accurate" + ); + assert!( + !RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), + "contains fails when expected" + ); - assert_eq!(RenderLayersXX::from(RenderLayer(0)).add(1).layers[0], 3, "layer 0 + 1 is mask 3"); + assert_eq!( + RenderLayersXX::from(RenderLayer(0)).add(1).layers[0], + 3, + "layer 0 + 1 is mask 3" + ); assert_eq!( RenderLayersXX::from(RenderLayer(0)).add(1).remove(0).layers[0], 2, @@ -495,7 +523,9 @@ mod rendering_mask_tests { "layers match like layers" ); assert!( - RenderLayersXX::from(RenderLayer(0)).intersects(&RenderLayersXX{ layers: SmallVec::from_slice(&[1]) }), + RenderLayersXX::from(RenderLayer(0)).intersects(&RenderLayersXX { + layers: SmallVec::from_slice(&[1]) + }), "a layer of 0 means the mask is just 1 bit" ); @@ -523,7 +553,12 @@ mod rendering_mask_tests { RenderLayersXX::from_layers(&[0, 2, 16, 30]) .iter() .collect::>(), - vec![RenderLayer(0), RenderLayer(2), RenderLayer(16), RenderLayer(30)], + vec![ + RenderLayer(0), + RenderLayer(2), + RenderLayer(16), + RenderLayer(30) + ], "from and get_layers should roundtrip" ); assert_eq!( From 36d7aebc6d27436eff67805c42b945a3f342610b Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 03:12:23 -0500 Subject: [PATCH 11/54] integration (WIP) compiles --- crates/bevy_gizmos/src/config.rs | 10 +- crates/bevy_gizmos/src/pipeline_2d.rs | 20 +-- crates/bevy_gizmos/src/pipeline_3d.rs | 20 +-- crates/bevy_pbr/src/light/mod.rs | 48 ++++--- crates/bevy_pbr/src/render/light.rs | 14 +- .../bevy_pbr/src/render/mesh_view_types.wgsl | 2 +- crates/bevy_pbr/src/render/pbr_functions.wgsl | 2 +- crates/bevy_render/src/camera/camera.rs | 10 +- crates/bevy_render/src/view/mod.rs | 10 +- crates/bevy_render/src/view/view.wgsl | 2 +- crates/bevy_render/src/view/visibility/mod.rs | 56 ++++---- .../visibility/propagate_render_groups.rs | 18 +-- .../src/view/visibility/render_groups.rs | 126 +++++++++--------- examples/2d/pixel_grid_snap.rs | 28 ++-- examples/3d/render_to_texture.rs | 18 +-- 15 files changed, 195 insertions(+), 189 deletions(-) diff --git a/crates/bevy_gizmos/src/config.rs b/crates/bevy_gizmos/src/config.rs index 92f6962c19384..285bbc1cb41b0 100644 --- a/crates/bevy_gizmos/src/config.rs +++ b/crates/bevy_gizmos/src/config.rs @@ -5,7 +5,7 @@ pub use bevy_gizmos_macros::GizmoConfigGroup; use bevy_ecs::{component::Component, reflect::ReflectResource, system::Resource}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; -use bevy_render::view::RenderLayers; +use bevy_render::view::RenderGroups; use bevy_utils::TypeIdMap; use core::panic; use std::{ @@ -164,7 +164,7 @@ pub struct GizmoConfig { /// Describes which rendering layers gizmos will be rendered to. /// /// Gizmos will only be rendered to cameras with intersecting layers. - pub render_layers: RenderLayers, + pub render_groups: RenderGroups, /// Describe how lines should join pub line_joints: GizmoLineJoint, @@ -178,7 +178,7 @@ impl Default for GizmoConfig { line_perspective: false, line_style: GizmoLineStyle::Solid, depth_bias: 0., - render_layers: Default::default(), + render_groups: Default::default(), line_joints: GizmoLineJoint::None, } @@ -189,7 +189,7 @@ impl Default for GizmoConfig { pub(crate) struct GizmoMeshConfig { pub line_perspective: bool, pub line_style: GizmoLineStyle, - pub render_layers: RenderLayers, + pub render_groups: RenderGroups, } impl From<&GizmoConfig> for GizmoMeshConfig { @@ -197,7 +197,7 @@ impl From<&GizmoConfig> for GizmoMeshConfig { GizmoMeshConfig { line_perspective: item.line_perspective, line_style: item.line_style, - render_layers: item.render_layers, + render_groups: item.render_groups.clone(), } } } diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index 5110779078cbf..f43b44c66bf17 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -20,7 +20,7 @@ use bevy_render::{ render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase}, render_resource::*, texture::BevyDefault, - view::{ExtractedView, Msaa, RenderLayers, ViewTarget}, + view::{ExtractedView, Msaa, RenderGroups, ViewTarget}, Render, RenderApp, RenderSet, }; use bevy_sprite::{Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup}; @@ -257,18 +257,19 @@ fn queue_line_gizmos_2d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderLayers>, + Option<&RenderGroups>, )>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); + let default_render_groups = RenderGroups::default(); - for (view, mut transparent_phase, render_layers) in &mut views { + for (view, mut transparent_phase, render_groups) in &mut views { let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) | Mesh2dPipelineKey::from_hdr(view.hdr); for (entity, handle, config) in &line_gizmos { - let render_layers = render_layers.copied().unwrap_or_default(); - if !config.render_layers.intersects(&render_layers) { + let render_groups = render_groups.unwrap_or(&default_render_groups); + if !config.render_groups.intersects(&render_groups) { continue; } @@ -310,21 +311,22 @@ fn queue_line_joint_gizmos_2d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderLayers>, + Option<&RenderGroups>, )>, ) { let draw_function = draw_functions .read() .get_id::() .unwrap(); + let default_render_groups = RenderGroups::default(); - for (view, mut transparent_phase, render_layers) in &mut views { + for (view, mut transparent_phase, render_groups) in &mut views { let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) | Mesh2dPipelineKey::from_hdr(view.hdr); for (entity, handle, config) in &line_gizmos { - let render_layers = render_layers.copied().unwrap_or_default(); - if !config.render_layers.intersects(&render_layers) { + let render_groups = render_groups.unwrap_or(&default_render_groups); + if !config.render_groups.intersects(&render_groups) { continue; } diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index ca53a1cdf2d54..c5111b9cb16d8 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -24,7 +24,7 @@ use bevy_render::{ render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase}, render_resource::*, texture::BevyDefault, - view::{ExtractedView, Msaa, RenderLayers, ViewTarget}, + view::{ExtractedView, Msaa, RenderGroups, ViewTarget}, Render, RenderApp, RenderSet, }; use bevy_utils::tracing::error; @@ -282,7 +282,7 @@ fn queue_line_gizmos_3d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderLayers>, + Option<&RenderGroups>, ( Has, Has, @@ -292,15 +292,16 @@ fn queue_line_gizmos_3d( )>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); + let default_render_groups = RenderGroups::default(); for ( view, mut transparent_phase, - render_layers, + render_groups, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut views { - let render_layers = render_layers.copied().unwrap_or_default(); + let render_groups = render_groups.unwrap_or(&default_render_groups); let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()) | MeshPipelineKey::from_hdr(view.hdr); @@ -322,7 +323,7 @@ fn queue_line_gizmos_3d( } for (entity, handle, config) in &line_gizmos { - if !config.render_layers.intersects(&render_layers) { + if !config.render_groups.intersects(render_groups) { continue; } @@ -365,7 +366,7 @@ fn queue_line_joint_gizmos_3d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderLayers>, + Option<&RenderGroups>, ( Has, Has, @@ -378,15 +379,16 @@ fn queue_line_joint_gizmos_3d( .read() .get_id::() .unwrap(); + let default_render_groups = RenderGroups::default(); for ( view, mut transparent_phase, - render_layers, + render_groups, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut views { - let render_layers = render_layers.copied().unwrap_or_default(); + let render_groups = render_groups.unwrap_or(&default_render_groups); let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()) | MeshPipelineKey::from_hdr(view.hdr); @@ -408,7 +410,7 @@ fn queue_line_joint_gizmos_3d( } for (entity, handle, config) in &line_gizmos { - if !config.render_layers.intersects(&render_layers) { + if !config.render_groups.intersects(render_groups) { continue; } diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index c73dcaf525b2d..cc43facc0f334 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -13,7 +13,7 @@ use bevy_render::{ primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere}, render_resource::BufferBindingType, renderer::RenderDevice, - view::{InheritedVisibility, RenderLayers, ViewVisibility, VisibleEntities}, + view::{InheritedVisibility, RenderGroups, ViewVisibility, VisibleEntities}, }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::tracing::warn; @@ -1011,7 +1011,7 @@ pub(crate) struct PointLightAssignmentData { range: f32, shadows_enabled: bool, spot_light_angle: Option, - render_layers: RenderLayers, + render_groups: u32, } impl PointLightAssignmentData { @@ -1052,21 +1052,21 @@ pub(crate) fn assign_lights_to_clusters( &Frustum, &ClusterConfig, &mut Clusters, - Option<&RenderLayers>, + Option<&RenderGroups>, Option<&mut VisiblePointLights>, )>, point_lights_query: Query<( Entity, &GlobalTransform, &PointLight, - Option<&RenderLayers>, + Option<&RenderGroups>, &ViewVisibility, )>, spot_lights_query: Query<( Entity, &GlobalTransform, &SpotLight, - Option<&RenderLayers>, + Option<&RenderGroups>, &ViewVisibility, )>, mut lights: Local>, @@ -1086,14 +1086,14 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, point_light, maybe_layers, _visibility)| { + |(entity, transform, point_light, _maybe_groups, _visibility)| { PointLightAssignmentData { entity, transform: GlobalTransform::from_translation(transform.translation()), shadows_enabled: point_light.shadows_enabled, range: point_light.range, spot_light_angle: None, - render_layers: maybe_layers.copied().unwrap_or_default(), + render_groups: 0u32//maybe_groups.cloned().unwrap_or(RenderGroups::default()), } }, ), @@ -1103,14 +1103,14 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, spot_light, maybe_layers, _visibility)| { + |(entity, transform, spot_light, _maybe_groups, _visibility)| { PointLightAssignmentData { entity, transform: *transform, shadows_enabled: spot_light.shadows_enabled, range: spot_light.range, spot_light_angle: Some(spot_light.outer_angle), - render_layers: maybe_layers.copied().unwrap_or_default(), + render_groups: 0u32//maybe_groups.cloned().unwrap_or(RenderGroups::default()), } }, ), @@ -1180,11 +1180,13 @@ pub(crate) fn assign_lights_to_clusters( frustum, config, clusters, - maybe_layers, + maybe_groups, mut visible_lights, ) in &mut views { - let view_layers = maybe_layers.copied().unwrap_or_default(); + let default_render_groups = RenderGroups::default(); + let view_groups = maybe_groups.unwrap_or(&default_render_groups); + let clusters = clusters.into_inner(); if matches!(config, ClusterConfig::None) { @@ -1407,7 +1409,8 @@ pub(crate) fn assign_lights_to_clusters( let mut update_from_light_intersections = |visible_lights: &mut Vec| { for light in &lights { // check if the light layers overlap the view layers - if !view_layers.intersects(&light.render_layers) { + let default_render_groups = RenderGroups::default(); + if !view_groups.intersects(&default_render_groups) {//&light.render_groups) { continue; } @@ -1824,21 +1827,21 @@ pub fn check_light_mesh_visibility( &GlobalTransform, &CubemapFrusta, &mut CubemapVisibleEntities, - Option<&RenderLayers>, + Option<&RenderGroups>, )>, mut spot_lights: Query<( &SpotLight, &GlobalTransform, &Frustum, &mut VisibleEntities, - Option<&RenderLayers>, + Option<&RenderGroups>, )>, mut directional_lights: Query< ( &DirectionalLight, &CascadesFrusta, &mut CascadesVisibleEntities, - Option<&RenderLayers>, + Option<&RenderGroups>, &mut ViewVisibility, ), Without, @@ -1848,7 +1851,7 @@ pub fn check_light_mesh_visibility( Entity, &InheritedVisibility, &mut ViewVisibility, - Option<&RenderLayers>, + Option<&RenderGroups>, Option<&Aabb>, Option<&GlobalTransform>, ), @@ -1872,6 +1875,7 @@ pub fn check_light_mesh_visibility( } // Directional lights + let default_render_groups = RenderGroups::default(); for (directional_light, frusta, mut visible_entities, maybe_view_mask, light_view_visibility) in &mut directional_lights { @@ -1903,7 +1907,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = maybe_view_mask.copied().unwrap_or_default(); + let view_mask = maybe_view_mask.unwrap_or(&default_render_groups); for ( entity, @@ -1918,7 +1922,7 @@ pub fn check_light_mesh_visibility( continue; } - let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); + let entity_mask = maybe_entity_mask.unwrap_or(&default_render_groups); if !view_mask.intersects(&entity_mask) { continue; } @@ -1983,7 +1987,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = maybe_view_mask.copied().unwrap_or_default(); + let view_mask = maybe_view_mask.unwrap_or(&default_render_groups); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2002,7 +2006,7 @@ pub fn check_light_mesh_visibility( continue; } - let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); + let entity_mask = maybe_entity_mask.unwrap_or(&default_render_groups); if !view_mask.intersects(&entity_mask) { continue; } @@ -2048,7 +2052,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = maybe_view_mask.copied().unwrap_or_default(); + let view_mask = maybe_view_mask.unwrap_or(&default_render_groups); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2067,7 +2071,7 @@ pub fn check_light_mesh_visibility( continue; } - let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); + let entity_mask = maybe_entity_mask.unwrap_or(&default_render_groups); if !view_mask.intersects(&entity_mask) { continue; } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 2dacc9943c57d..b0a3e09301f34 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -14,7 +14,7 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ExtractedView, RenderLayers, ViewVisibility, VisibleEntities}, + view::{ExtractedView, RenderGroups, ViewVisibility, VisibleEntities}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -51,7 +51,7 @@ pub struct ExtractedDirectionalLight { pub cascade_shadow_config: CascadeShadowConfig, pub cascades: EntityHashMap>, pub frusta: EntityHashMap>, - pub render_layers: RenderLayers, + pub render_groups: RenderGroups, } #[derive(Copy, Clone, ShaderType, Default, Debug)] @@ -173,7 +173,7 @@ pub struct GpuDirectionalLight { num_cascades: u32, cascades_overlap_proportion: f32, depth_texture_base_index: u32, - render_layers: u32, + render_groups: u32, } // NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl! @@ -345,7 +345,7 @@ pub fn extract_lights( &CascadesFrusta, &GlobalTransform, &ViewVisibility, - Option<&RenderLayers>, + Option<&RenderGroups>, ), Without, >, @@ -467,7 +467,7 @@ pub fn extract_lights( frusta, transform, view_visibility, - maybe_layers, + maybe_groups, ) in &directional_lights { if !view_visibility.get() { @@ -488,7 +488,7 @@ pub fn extract_lights( cascade_shadow_config: cascade_config.clone(), cascades: cascades.cascades.clone(), frusta: frusta.frusta.clone(), - render_layers: maybe_layers.copied().unwrap_or_default(), + render_groups: maybe_groups.cloned().unwrap_or(RenderGroups::default()), }, render_visible_entities, )); @@ -917,7 +917,7 @@ pub fn prepare_lights( num_cascades: num_cascades as u32, cascades_overlap_proportion: light.cascade_shadow_config.overlap_proportion, depth_texture_base_index: num_directional_cascades_enabled as u32, - render_layers: light.render_layers.bits(), + render_groups: 0u32//light.render_groups.bits(), }; if index < directional_shadow_enabled_count { num_directional_cascades_enabled += num_cascades; diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index 76e43eed2d541..1d9a5b59b44e6 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -33,7 +33,7 @@ struct DirectionalLight { num_cascades: u32, cascades_overlap_proportion: f32, depth_texture_base_index: u32, - render_layers: u32, + render_groups: u32, }; const DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index 1c602944662c5..a2f0f7ac87f71 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -280,7 +280,7 @@ fn apply_pbr_lighting( // check the directional light render layers intersect the view render layers // note this is not necessary for point and spot lights, as the relevant lights are filtered in `assign_lights_to_clusters` let light = &view_bindings::lights.directional_lights[i]; - if ((*light).render_layers & view_bindings::view.render_layers) == 0u { + if ((*light).render_groups & view_bindings::view.render_groups) == 0u { continue; } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 728a863b703cf..e3b05908d0a33 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -6,7 +6,7 @@ use crate::{ render_asset::RenderAssets, render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_resource::TextureView, - view::{ColorGrading, ExtractedView, ExtractedWindows, RenderLayers, VisibleEntities}, + view::{ColorGrading, ExtractedView, ExtractedWindows, RenderGroups, VisibleEntities}, Extract, }; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; @@ -811,7 +811,7 @@ pub fn extract_cameras( Option<&ColorGrading>, Option<&Exposure>, Option<&TemporalJitter>, - Option<&RenderLayers>, + Option<&RenderGroups>, Option<&Projection>, )>, >, @@ -828,7 +828,7 @@ pub fn extract_cameras( color_grading, exposure, temporal_jitter, - render_layers, + render_groups, projection, ) in query.iter() { @@ -894,8 +894,8 @@ pub fn extract_cameras( commands.insert(temporal_jitter.clone()); } - if let Some(render_layers) = render_layers { - commands.insert(*render_layers); + if let Some(render_groups) = render_groups { + commands.insert(render_groups.clone()); } if let Some(perspective) = projection { diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 0d1958bf7d4de..ba3c018edc942 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -47,7 +47,7 @@ impl Plugin for ViewPlugin { .register_type::() .register_type::() .register_type::() - .register_type::() + //.register_type::() .register_type::() .register_type::() .register_type::() @@ -177,7 +177,7 @@ pub struct ViewUniform { frustum: [Vec4; 6], color_grading: ColorGrading, mip_bias: f32, - render_layers: u32, + render_groups: u32, } #[derive(Resource, Default)] @@ -375,7 +375,7 @@ pub fn prepare_view_uniforms( Option<&Frustum>, Option<&TemporalJitter>, Option<&MipBias>, - Option<&RenderLayers>, + Option<&RenderGroups>, )>, ) { let view_iter = views.iter(); @@ -394,7 +394,7 @@ pub fn prepare_view_uniforms( frustum, temporal_jitter, mip_bias, - maybe_layers, + _maybe_groups, ) in &views { let viewport = extracted_view.viewport.as_vec4(); @@ -439,7 +439,7 @@ pub fn prepare_view_uniforms( frustum, color_grading: extracted_view.color_grading, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, - render_layers: maybe_layers.copied().unwrap_or_default().bits(), + render_groups: 0u32//maybe_groups.cloned().unwrap_or(RenderGroups::default()), }), }; diff --git a/crates/bevy_render/src/view/view.wgsl b/crates/bevy_render/src/view/view.wgsl index 237113b713a0d..59218996f4db0 100644 --- a/crates/bevy_render/src/view/view.wgsl +++ b/crates/bevy_render/src/view/view.wgsl @@ -22,5 +22,5 @@ struct View { frustum: array, 6>, color_grading: ColorGrading, mip_bias: f32, - render_layers: u32, + render_group: u32, }; diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 914ab923161e2..68463e7d69997 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,11 +1,11 @@ mod propagate_render_groups; mod render_groups; -mod render_layers; +//mod render_layers; use bevy_derive::Deref; pub use propagate_render_groups::*; pub use render_groups::*; -pub use render_layers::*; +//pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{Assets, Handle}; @@ -217,14 +217,12 @@ impl Plugin for VisibilityPlugin { fn build(&self, app: &mut bevy_app::App) { use VisibilitySystems::*; - /* - app.configure_sets( - PostUpdate, - PropagateRenderGroupsSet - .in_set(VisibilityPropagate), - ) - */ - app.add_systems( + app.configure_sets( + PostUpdate, + PropagateRenderGroupsSet + .in_set(VisibilityPropagate), + ) + .add_systems( PostUpdate, ( calculate_bounds.in_set(CalculateBounds), @@ -384,34 +382,31 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { pub fn check_visibility( mut thread_queues: Local>>, mut view_query: Query<( - // Entity, + Entity, &mut VisibleEntities, &Frustum, - Option<&RenderLayers>, - // Option<&CameraView>, + Option<&CameraView>, &Camera, )>, mut visible_aabb_query: Query<( Entity, &InheritedVisibility, &mut ViewVisibility, - Option<&RenderLayers>, - // Option<&RenderGroups>, - // Option<&InheritedRenderGroups>, + Option<&RenderGroups>, + Option<&InheritedRenderGroups>, Option<&Aabb>, &GlobalTransform, Has, )>, deterministic_rendering_config: Res, ) { - // for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { - for (mut visible_entities, frustum, maybe_view_mask, camera) in &mut view_query { + for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { if !camera.is_active { continue; } - let view_mask = maybe_view_mask.copied().unwrap_or_default(); - // let camera_view = maybe_camera_view.unwrap_or(&CameraView::default()); + let default_camera_view = CameraView::default(); + let camera_view = maybe_camera_view.unwrap_or(&default_camera_view); visible_entities.entities.clear(); visible_aabb_query.par_iter_mut().for_each(|query_item| { @@ -419,9 +414,8 @@ pub fn check_visibility( entity, inherited_visibility, mut view_visibility, - maybe_entity_mask, - // maybe_groups, - // maybe_inherited_groups, + maybe_groups, + maybe_inherited_groups, maybe_model_aabb, transform, no_frustum_culling, @@ -433,14 +427,14 @@ pub fn check_visibility( return; } - let entity_mask = maybe_entity_mask.copied().unwrap_or_default(); - if !view_mask.intersects(&entity_mask) { - /* - // Check render groups. - let entity_groups = maybe_inherited_groups.map(|i| &i.computed) - .or(maybe_groups) - .unwrap_or(&RenderGroups::default()); - if !camera_view.entity_is_visible(camera_entity, entity_groups) { */ + // Check render groups. + // - If there is no RenderGroups in the entity, use the *default* value because the + // entity is in the DEFAULT_RENDER_LAYER. + let default_render_groups = RenderGroups::default(); + let entity_groups = maybe_inherited_groups.map(|i| &i.computed) + .or(maybe_groups) + .unwrap_or(&default_render_groups); + if !camera_view.entity_is_visible(camera_entity, entity_groups) { return; } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index df75ef2aea147..531ee1d2e6414 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -128,7 +128,7 @@ The base-line performance cost of this algorithm comes from iterating in order t - `RemovedComponents` is iterated once. */ -use crate::view::{CameraView, RenderGroups}; +use crate::view::*; use crate::prelude::Camera; use bevy_app::{App, Plugin, PostUpdate}; @@ -207,8 +207,8 @@ impl PropagateRenderGroups { warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); PropagatingRenderGroups::Val(RenderGroups::empty()); }; - let empty_view = CameraView::empty(); - let view = view.unwrap_or(&empty_view); + let empty_camera_view = CameraView::empty(); + let view = view.unwrap_or(&empty_camera_view); PropagatingRenderGroups::Val(view.get_groups(entity)) } Self::Custom(groups) => PropagatingRenderGroups::Ref(groups), @@ -412,8 +412,8 @@ fn apply_full_propagation( }; // Update inherited value or insert a new one. - let empty_render_groups = RenderGroups::empty(); - let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); + let default_render_groups = RenderGroups::default(); + let initial_groups = maybe_render_groups.unwrap_or(&default_render_groups); let apply_changes = |groups: &mut InheritedRenderGroups| { groups.propagator = propagator; groups.computed.set_from(initial_groups); @@ -539,8 +539,8 @@ fn apply_new_children_propagation( } // Update inherited value or insert a new one. - let empty_render_groups = RenderGroups::empty(); - let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); + let default_render_groups = RenderGroups::default(); + let initial_groups = maybe_render_groups.unwrap_or(&default_render_groups); let apply_changes = |groups: &mut InheritedRenderGroups| { groups.propagator = propagator; groups.computed.set_from(initial_groups); @@ -810,8 +810,8 @@ fn apply_full_propagation_force_update( } // Force-update - let empty_render_groups = RenderGroups::empty(); - let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); + let default_render_groups = RenderGroups::default(); + let initial_groups = maybe_render_groups.unwrap_or(&default_render_groups); let mut new = InheritedRenderGroups::empty(); if let Some(mut inherited) = maybe_inherited_groups { diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index fde94150c071d..e6f4c3f1bb594 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -1,14 +1,16 @@ use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::prelude::{Component, Entity}; +use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; +use bevy_reflect::Reflect; +use bevy_reflect::prelude::ReflectDefault; use smallvec::SmallVec; /// The default [`RenderLayer`]. -pub const DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); +pub static DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); -/// Wraps a specific render layer that can be stored in [`RenderLayersXX`]. +/// Wraps a specific render layer that can be stored in [`RenderXXLayersXX`]. /// -/// Stores an index into the [`RenderLayersXX`] internal bitmask. +/// Stores an index into the [`RenderXXLayersXX`] internal bitmask. //todo: Upper limit policy for render layer indices. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deref, DerefMut)] pub struct RenderLayer(pub usize); @@ -36,31 +38,32 @@ impl Default for RenderLayer { /// are visible to which cameras. /// /// Individual render layers can be defined with [`RenderLayer`], which is an index -/// into the internal `RenderLayersXX` bitmask. +/// into the internal `RenderXXLayersXX` bitmask. /// -/// `RenderLayersXX::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default +/// `RenderXXLayersXX::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default /// layer. /// /// ### Performance /// -/// `RenderLayersXX` occupies 24 bytes on the stack. +/// `RenderXXLayersXX` occupies 24 bytes on the stack. /// -/// `RenderLayersXX` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte +/// `RenderXXLayersXX` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. -#[derive(Debug, Clone, PartialEq)] -pub struct RenderLayersXX { +#[derive(Debug, Clone, PartialEq, Reflect)] +#[reflect(Default, PartialEq)] +pub struct RenderXXLayersXX { layers: SmallVec<[u64; 1]>, } -impl RenderLayersXX { - /// Makes a new `RenderLayersXX` with no layers. +impl RenderXXLayersXX { + /// Makes a new `RenderXXLayersXX` with no layers. pub fn empty() -> Self { Self { layers: SmallVec::default(), } } - /// Makes a new `RenderLayersXX` from a slice. + /// Makes a new `RenderXXLayersXX` from a slice. pub fn from_layers + Copy>(layers: &[T]) -> Self { layers.iter().map(|l| (*l).into()).collect() } @@ -94,7 +97,7 @@ impl RenderLayersXX { /// Copies `other` into `Self`. /// - /// This is more efficient than cloning `other` if you want to reuse a `RenderLayersXX` + /// This is more efficient than cloning `other` if you want to reuse a `RenderXXLayersXX` /// that is potentially allocated. pub fn set_from(&mut self, other: &Self) { self.layers.clear(); @@ -127,7 +130,7 @@ impl RenderLayersXX { self.layers.iter().copied().map(Self::iter_layers).flatten() } - /// Returns `true` if the specified render layer is included in this `RenderLayersXX`. + /// Returns `true` if the specified render layer is included in this `RenderXXLayersXX`. pub fn contains(&self, layer: impl Into) -> bool { let (buffer_index, bit) = Self::layer_info(*(layer.into())); if buffer_index >= self.layers.len() { @@ -181,7 +184,7 @@ impl RenderLayersXX { } } -impl> From for RenderLayersXX { +impl> From for RenderXXLayersXX { fn from(layer: T) -> Self { let mut layers = Self { layers: SmallVec::default(), @@ -191,7 +194,7 @@ impl> From for RenderLayersXX { } } -impl> FromIterator for RenderLayersXX { +impl> FromIterator for RenderXXLayersXX { fn from_iter>(i: T) -> Self { i.into_iter().fold(Self::empty(), |mut mask, g| { mask.add(g); @@ -200,7 +203,7 @@ impl> FromIterator for RenderLayersXX { } } -impl Default for RenderLayersXX { +impl Default for RenderXXLayersXX { fn default() -> Self { Self::from(DEFAULT_RENDER_LAYER) } @@ -209,11 +212,11 @@ impl Default for RenderLayersXX { /// Component on an entity that controls which cameras can see it. /// /// There are two kinds of render groups: -/// - [`RenderLayersXX`]: These are grouping categories that many cameras can view (see [`CameraView`]). +/// - [`RenderXXLayersXX`]: These are grouping categories that many cameras can view (see [`CameraView`]). /// - *Camera entity*: This is a specific camera that the entity is affiliated with. This is especially /// useful for UI in combination with [`PropagateRenderGroups`]. /// -/// An entity can be a member of multiple [`RenderLayersXX`] in addition to having a camera affiliation. +/// An entity can be a member of multiple [`RenderXXLayersXX`] in addition to having a camera affiliation. /// /// ### Default behavior /// @@ -246,9 +249,10 @@ entity.insert(groups); /// doesn't propagate layer 0, then the entity's computed [`InheritedRenderGroups`] won't have layer 0 and the /// entity won't be visible to layer 0. */ -#[derive(Component, Debug, Clone)] +#[derive(Component, Debug, Clone, Reflect, PartialEq)] +#[reflect(Component, Default, PartialEq)] pub struct RenderGroups { - layers: RenderLayersXX, + layers: RenderXXLayersXX, camera: Option, } @@ -256,7 +260,7 @@ impl RenderGroups { /// Makes a new `RenderGroups` with no groups. pub fn empty() -> Self { Self { - layers: RenderLayersXX::empty(), + layers: RenderXXLayersXX::empty(), camera: None, } } @@ -264,14 +268,14 @@ impl RenderGroups { /// Makes a new `RenderGroups` with just a camera. pub fn new_with_camera(camera: Entity) -> Self { Self { - layers: RenderLayersXX::empty(), + layers: RenderXXLayersXX::empty(), camera: Some(camera), } } /// Adds a [`RenderLayer`]. /// - /// See [`RenderLayersXX::add`]. + /// See [`RenderXXLayersXX::add`]. pub fn add(&mut self, layer: impl Into) -> &mut Self { self.layers.add(layer); self @@ -279,7 +283,7 @@ impl RenderGroups { /// Removes a [`RenderLayer`]. /// - /// See [`RenderLayersXX::remove`]. + /// See [`RenderXXLayersXX::remove`]. pub fn remove(&mut self, layer: impl Into) -> &mut Self { self.layers.remove(layer); self @@ -293,11 +297,11 @@ impl RenderGroups { /// Merges `other` into `Self`. /// - /// After merging, `Self` will include all [`RenderLayersXX`] from `other` and `Self`. + /// After merging, `Self` will include all [`RenderXXLayersXX`] from `other` and `Self`. /// If both `Self` and `other` have a camera affiliation, then the `Self` camera /// will be in the merged result. Otherwise the `other` camera will be in the result. /// - /// Will allocate if necessary to include all [`RenderLayersXX`] of `other`. + /// Will allocate if necessary to include all [`RenderXXLayersXX`] of `other`. pub fn merge(&mut self, other: &Self) { self.layers.merge(&other.layers); self.camera = self.camera.or(other.camera); @@ -339,7 +343,7 @@ impl RenderGroups { /// Returns `true` if `Self` intersects with `other`. /// - /// Checks both camera affiliation and [`RenderLayersXX`] intersection. + /// Checks both camera affiliation and [`RenderXXLayersXX`] intersection. pub fn intersects(&self, other: &Self) -> bool { if let (Some(a), Some(b)) = (self.camera, other.camera) { if a == b { @@ -359,15 +363,15 @@ impl From for RenderGroups { /// Makes a new `RenderGroups` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { Self { - layers: RenderLayersXX::from(layer), + layers: RenderXXLayersXX::from(layer), camera: None, } } } -impl From for RenderGroups { - /// Makes a new `RenderGroups` from a [`RenderLayersXX`]. - fn from(layers: RenderLayersXX) -> Self { +impl From for RenderGroups { + /// Makes a new `RenderGroups` from a [`RenderXXLayersXX`]. + fn from(layers: RenderXXLayersXX) -> Self { Self { layers, camera: None, @@ -382,7 +386,7 @@ impl Default for RenderGroups { } } -/// Component on camera entities that controls which [`RenderLayersXX`] are visible to +/// Component on camera entities that controls which [`RenderXXLayersXX`] are visible to /// the camera. /// /// A camera will see any entity that satisfies either of these conditions: @@ -400,20 +404,20 @@ impl Default for RenderGroups { /// A default `CameraView` will include the [`DEFAULT_RENDER_LAYER`]. #[derive(Component, Debug, Clone)] pub struct CameraView { - layers: RenderLayersXX, + layers: RenderXXLayersXX, } impl CameraView { /// Makes a new `CameraView` with no visible [`RenderLayer`]. pub fn empty() -> Self { Self { - layers: RenderLayersXX::empty(), + layers: RenderXXLayersXX::empty(), } } /// Adds a [`RenderLayer`]. /// - /// See [`RenderLayersXX::add`]. + /// See [`RenderXXLayersXX::add`]. pub fn add(&mut self, layer: impl Into) -> &mut Self { self.layers.add(layer); self @@ -421,7 +425,7 @@ impl CameraView { /// Removes a [`RenderLayer`]. /// - /// See [`RenderLayersXX::remove`]. + /// See [`RenderXXLayersXX::remove`]. pub fn remove(&mut self, layer: impl Into) -> &mut Self { self.layers.remove(layer); self @@ -445,7 +449,7 @@ impl CameraView { /// Returns `true` if the entity with the specified [`RenderGroups`] is visible /// to the `camera` that has this `CameraView`. /// - /// Checks both camera affiliation and [`RenderLayersXX`] intersection. + /// Checks both camera affiliation and [`RenderXXLayersXX`] intersection. pub fn entity_is_visible(&self, camera: Entity, groups: &RenderGroups) -> bool { if Some(camera) == groups.camera { return true; @@ -453,7 +457,7 @@ impl CameraView { self.layers.intersects(&groups.layers) } - /// Converts the internal [`RenderLayersXX`] into a [`RenderGroups`] affiliated + /// Converts the internal [`RenderXXLayersXX`] into a [`RenderGroups`] affiliated /// with the camera that has this `CameraView`. pub fn get_groups(&self, camera: Entity) -> RenderGroups { let mut groups = RenderGroups::from(self.layers.clone()); @@ -466,7 +470,7 @@ impl From for CameraView { /// Makes a new `CameraView` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { Self { - layers: RenderLayersXX::from(layer), + layers: RenderXXLayersXX::from(layer), } } } @@ -480,77 +484,77 @@ impl Default for CameraView { #[cfg(test)] mod rendering_mask_tests { - use super::{RenderLayer, RenderLayersXX, DEFAULT_RENDER_LAYER}; + use super::{RenderLayer, RenderXXLayersXX, DEFAULT_RENDER_LAYER}; use smallvec::SmallVec; #[test] fn rendering_mask_sanity() { assert_eq!( - RenderLayersXX::default().num_layers(), + RenderXXLayersXX::default().num_layers(), 1, "default layer contains only one layer" ); assert!( - RenderLayersXX::default().contains(DEFAULT_RENDER_LAYER), + RenderXXLayersXX::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default" ); assert_eq!( - RenderLayersXX::from(RenderLayer(1)).num_layers(), + RenderXXLayersXX::from(RenderLayer(1)).num_layers(), 1, "from contains 1 layer" ); assert!( - RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), + RenderXXLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate" ); assert!( - !RenderLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), + !RenderXXLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected" ); assert_eq!( - RenderLayersXX::from(RenderLayer(0)).add(1).layers[0], + RenderXXLayersXX::from(RenderLayer(0)).add(1).layers[0], 3, "layer 0 + 1 is mask 3" ); assert_eq!( - RenderLayersXX::from(RenderLayer(0)).add(1).remove(0).layers[0], + RenderXXLayersXX::from(RenderLayer(0)).add(1).remove(0).layers[0], 2, "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderLayersXX::from(RenderLayer(1)).intersects(&RenderLayersXX::from(RenderLayer(1))), + RenderXXLayersXX::from(RenderLayer(1)).intersects(&RenderXXLayersXX::from(RenderLayer(1))), "layers match like layers" ); assert!( - RenderLayersXX::from(RenderLayer(0)).intersects(&RenderLayersXX { + RenderXXLayersXX::from(RenderLayer(0)).intersects(&RenderXXLayersXX { layers: SmallVec::from_slice(&[1]) }), "a layer of 0 means the mask is just 1 bit" ); assert!( - RenderLayersXX::from(RenderLayer(0)) + RenderXXLayersXX::from(RenderLayer(0)) .add(3) - .intersects(&RenderLayersXX::from(RenderLayer(3))), + .intersects(&RenderXXLayersXX::from(RenderLayer(3))), "a mask will match another mask containing any similar layers" ); assert!( - RenderLayersXX::default().intersects(&RenderLayersXX::default()), + RenderXXLayersXX::default().intersects(&RenderXXLayersXX::default()), "default masks match each other" ); assert!( - !RenderLayersXX::from(RenderLayer(0)).intersects(&RenderLayersXX::from(RenderLayer(1))), + !RenderXXLayersXX::from(RenderLayer(0)).intersects(&RenderXXLayersXX::from(RenderLayer(1))), "masks with differing layers do not match" ); assert!( - !RenderLayersXX::empty().intersects(&RenderLayersXX::empty()), + !RenderXXLayersXX::empty().intersects(&RenderXXLayersXX::empty()), "empty masks don't match" ); assert_eq!( - RenderLayersXX::from_layers(&[0, 2, 16, 30]) + RenderXXLayersXX::from_layers(&[0, 2, 16, 30]) .iter() .collect::>(), vec![ @@ -562,13 +566,13 @@ mod rendering_mask_tests { "from and get_layers should roundtrip" ); assert_eq!( - format!("{:?}", RenderLayersXX::from_layers(&[0, 1, 2, 3])).as_str(), - "RenderLayersXX([0, 1, 2, 3])", + format!("{:?}", RenderXXLayersXX::from_layers(&[0, 1, 2, 3])).as_str(), + "RenderXXLayersXX([0, 1, 2, 3])", "Debug instance shows layers" ); assert_eq!( - RenderLayersXX::from_layers(&[0, 1, 2]), - >::from_iter(vec![0, 1, 2]), + RenderXXLayersXX::from_layers(&[0, 1, 2]), + >::from_iter(vec![0, 1, 2]), "from_layers and from_iter are equivalent" ); } diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 075dc06818f39..8faf01916f5e7 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -7,7 +7,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::RenderLayers, + view::{CameraView, RenderLayer, RenderGroups}, }, sprite::MaterialMesh2dBundle, window::WindowResized, @@ -19,12 +19,12 @@ const RES_WIDTH: u32 = 160; /// In-game resolution height. const RES_HEIGHT: u32 = 90; -/// Default render layers for pixel-perfect rendering. +/// Default render groups for pixel-perfect rendering. /// You can skip adding this component, as this is the default. -const PIXEL_PERFECT_LAYERS: RenderLayers = RenderLayers::layer(0); +const PIXEL_PERFECT_LAYER: RenderLayer = RenderLayer(0); -/// Render layers for high-resolution rendering. -const HIGH_RES_LAYERS: RenderLayers = RenderLayers::layer(1); +/// Render groups for high-resolution rendering. +const HIGH_RES_LAYER: RenderLayer = RenderLayer(1); fn main() { App::new() @@ -44,7 +44,7 @@ struct Canvas; #[derive(Component)] struct InGameCamera; -/// Camera that renders the [`Canvas`] (and other graphics on [`HIGH_RES_LAYERS`]) to the screen. +/// Camera that renders the [`Canvas`] (and other graphics on [`HIGH_RES_LAYER`]) to the screen. #[derive(Component)] struct OuterCamera; @@ -60,7 +60,7 @@ fn setup_sprite(mut commands: Commands, asset_server: Res) { ..default() }, Rotate, - PIXEL_PERFECT_LAYERS, + RenderGroups::from(PIXEL_PERFECT_LAYER), )); // the sample sprite that will be rendered to the high-res "outer world" @@ -71,7 +71,7 @@ fn setup_sprite(mut commands: Commands, asset_server: Res) { ..default() }, Rotate, - HIGH_RES_LAYERS, + RenderGroups::from(HIGH_RES_LAYER), )); } @@ -89,7 +89,7 @@ fn setup_mesh( ..default() }, Rotate, - PIXEL_PERFECT_LAYERS, + RenderGroups::from(PIXEL_PERFECT_LAYER), )); } @@ -122,7 +122,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { let image_handle = images.add(canvas); - // this camera renders whatever is on `PIXEL_PERFECT_LAYERS` to the canvas + // this camera renders whatever is on `PIXEL_PERFECT_LAYER` to the canvas commands.spawn(( Camera2dBundle { camera: Camera { @@ -134,7 +134,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { ..default() }, InGameCamera, - PIXEL_PERFECT_LAYERS, + CameraView::from(PIXEL_PERFECT_LAYER), )); // spawn the canvas @@ -144,12 +144,12 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { ..default() }, Canvas, - HIGH_RES_LAYERS, + RenderGroups::from(HIGH_RES_LAYER), )); - // the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen. + // the "outer" camera renders whatever is on `HIGH_RES_LAYER` to the screen. // here, the canvas and one of the sample sprites will be rendered by this camera - commands.spawn((Camera2dBundle::default(), OuterCamera, HIGH_RES_LAYERS)); + commands.spawn((Camera2dBundle::default(), OuterCamera, CameraView::from(HIGH_RES_LAYER))); } /// Rotates entities to demonstrate grid snapping. diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index b468347831ca4..c2b1ba2bbc53f 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -8,7 +8,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::RenderLayers, + view::{RenderLayer, RenderXXLayersXX, RenderGroups}, }, }; @@ -70,8 +70,8 @@ fn setup( ..default() }); - // This specifies the layer used for the first pass, which will be attached to the first pass camera and cube. - let first_pass_layer = RenderLayers::layer(1); + // This specifies the group used for the first pass, which will be attached to the first pass camera and cube. + let first_pass_group = RenderGroups::from(RenderLayer(1)); // The cube that will be rendered to the texture. commands.spawn(( @@ -82,19 +82,19 @@ fn setup( ..default() }, FirstPassCube, - first_pass_layer, + first_pass_group.clone(), )); // Light - // NOTE: we add the light to all layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture - // Setting the layer to RenderLayers::layer(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. - // Setting the layer to RenderLayers::layer(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. + // NOTE: we add the light to all groups so it affects both the rendered-to-texture cube, and the cube on which we display the texture + // Setting the group to RenderGroups::from(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. + // Setting the group to RenderGroups::from(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. commands.spawn(( PointLightBundle { transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }, - RenderLayers::all(), + RenderGroups::from(RenderXXLayersXX::from_layers(&[0, 1])), )); commands.spawn(( @@ -110,7 +110,7 @@ fn setup( .looking_at(Vec3::ZERO, Vec3::Y), ..default() }, - first_pass_layer, + first_pass_group, )); let cube_size = 4.0; From a4563acb8ee94c8818a598f2678846ca71eb0bfe Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 15:53:37 -0500 Subject: [PATCH 12/54] fixes for render groups usage --- crates/bevy_gizmos/src/pipeline_2d.rs | 14 +-- crates/bevy_gizmos/src/pipeline_3d.rs | 16 +--- crates/bevy_pbr/src/light/mod.rs | 53 ++++++----- crates/bevy_pbr/src/render/light.rs | 10 +- crates/bevy_render/src/camera/camera.rs | 11 +-- crates/bevy_render/src/view/mod.rs | 2 +- .../visibility/propagate_render_groups.rs | 94 +++++++++++++++---- .../src/view/visibility/render_groups.rs | 19 ++++ 8 files changed, 150 insertions(+), 69 deletions(-) diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index f43b44c66bf17..055bbb9e1ed5e 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -20,7 +20,7 @@ use bevy_render::{ render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase}, render_resource::*, texture::BevyDefault, - view::{ExtractedView, Msaa, RenderGroups, ViewTarget}, + view::{ExtractedRenderGroups, ExtractedView, Msaa, ViewTarget}, Render, RenderApp, RenderSet, }; use bevy_sprite::{Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup}; @@ -257,19 +257,17 @@ fn queue_line_gizmos_2d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderGroups>, + Option<&ExtractedRenderGroups>, )>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); - let default_render_groups = RenderGroups::default(); for (view, mut transparent_phase, render_groups) in &mut views { let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) | Mesh2dPipelineKey::from_hdr(view.hdr); for (entity, handle, config) in &line_gizmos { - let render_groups = render_groups.unwrap_or(&default_render_groups); - if !config.render_groups.intersects(&render_groups) { + if !config.render_groups.intersects_extracted(render_groups) { continue; } @@ -311,22 +309,20 @@ fn queue_line_joint_gizmos_2d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderGroups>, + Option<&ExtractedRenderGroups>, )>, ) { let draw_function = draw_functions .read() .get_id::() .unwrap(); - let default_render_groups = RenderGroups::default(); for (view, mut transparent_phase, render_groups) in &mut views { let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) | Mesh2dPipelineKey::from_hdr(view.hdr); for (entity, handle, config) in &line_gizmos { - let render_groups = render_groups.unwrap_or(&default_render_groups); - if !config.render_groups.intersects(&render_groups) { + if !config.render_groups.intersects_extracted(render_groups) { continue; } diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index c5111b9cb16d8..1a06c8d6ed012 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -24,7 +24,7 @@ use bevy_render::{ render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase}, render_resource::*, texture::BevyDefault, - view::{ExtractedView, Msaa, RenderGroups, ViewTarget}, + view::{ExtractedRenderGroups, ExtractedView, Msaa, ViewTarget}, Render, RenderApp, RenderSet, }; use bevy_utils::tracing::error; @@ -282,7 +282,7 @@ fn queue_line_gizmos_3d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderGroups>, + Option<&ExtractedRenderGroups>, ( Has, Has, @@ -292,7 +292,6 @@ fn queue_line_gizmos_3d( )>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); - let default_render_groups = RenderGroups::default(); for ( view, @@ -301,8 +300,6 @@ fn queue_line_gizmos_3d( (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut views { - let render_groups = render_groups.unwrap_or(&default_render_groups); - let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()) | MeshPipelineKey::from_hdr(view.hdr); @@ -323,7 +320,7 @@ fn queue_line_gizmos_3d( } for (entity, handle, config) in &line_gizmos { - if !config.render_groups.intersects(render_groups) { + if !config.render_groups.intersects_extracted(render_groups) { continue; } @@ -366,7 +363,7 @@ fn queue_line_joint_gizmos_3d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&RenderGroups>, + Option<&ExtractedRenderGroups>, ( Has, Has, @@ -379,7 +376,6 @@ fn queue_line_joint_gizmos_3d( .read() .get_id::() .unwrap(); - let default_render_groups = RenderGroups::default(); for ( view, @@ -388,8 +384,6 @@ fn queue_line_joint_gizmos_3d( (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut views { - let render_groups = render_groups.unwrap_or(&default_render_groups); - let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples()) | MeshPipelineKey::from_hdr(view.hdr); @@ -410,7 +404,7 @@ fn queue_line_joint_gizmos_3d( } for (entity, handle, config) in &line_gizmos { - if !config.render_groups.intersects(render_groups) { + if !config.render_groups.intersects_extracted(render_groups) { continue; } diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index cc43facc0f334..22077305f7c76 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -13,7 +13,10 @@ use bevy_render::{ primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere}, render_resource::BufferBindingType, renderer::RenderDevice, - view::{InheritedVisibility, RenderGroups, ViewVisibility, VisibleEntities}, + view::{ + extract_render_groups, InheritedRenderGroups, InheritedVisibility, render_groups_asref, RenderGroups, + ViewVisibility, VisibleEntities + }, }; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::tracing::warn; @@ -1053,6 +1056,7 @@ pub(crate) fn assign_lights_to_clusters( &ClusterConfig, &mut Clusters, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, Option<&mut VisiblePointLights>, )>, point_lights_query: Query<( @@ -1060,6 +1064,7 @@ pub(crate) fn assign_lights_to_clusters( &GlobalTransform, &PointLight, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, &ViewVisibility, )>, spot_lights_query: Query<( @@ -1067,6 +1072,7 @@ pub(crate) fn assign_lights_to_clusters( &GlobalTransform, &SpotLight, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, &ViewVisibility, )>, mut lights: Local>, @@ -1086,14 +1092,14 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, point_light, _maybe_groups, _visibility)| { + |(entity, transform, point_light, _maybe_groups, _maybe_inherited, _visibility)| { PointLightAssignmentData { entity, transform: GlobalTransform::from_translation(transform.translation()), shadows_enabled: point_light.shadows_enabled, range: point_light.range, spot_light_angle: None, - render_groups: 0u32//maybe_groups.cloned().unwrap_or(RenderGroups::default()), + render_groups: 0u32//extract_render_groups(maybe_inherited, maybe_groups), } }, ), @@ -1103,14 +1109,14 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, spot_light, _maybe_groups, _visibility)| { + |(entity, transform, spot_light, _maybe_groups, _maybe_inherited, _visibility)| { PointLightAssignmentData { entity, transform: *transform, shadows_enabled: spot_light.shadows_enabled, range: spot_light.range, spot_light_angle: Some(spot_light.outer_angle), - render_groups: 0u32//maybe_groups.cloned().unwrap_or(RenderGroups::default()), + render_groups: 0u32//extract_render_groups(maybe_inherited, maybe_groups), } }, ), @@ -1141,7 +1147,7 @@ pub(crate) fn assign_lights_to_clusters( // check each light against each view's frustum, keep only those that affect at least one of our views let frusta: Vec<_> = views .iter() - .map(|(_, _, _, frustum, _, _, _, _)| *frustum) + .map(|(_, _, _, frustum, _, _, _, _, _)| *frustum) .collect(); let mut lights_in_view_count = 0; lights.retain(|light| { @@ -1181,11 +1187,11 @@ pub(crate) fn assign_lights_to_clusters( config, clusters, maybe_groups, + maybe_inherited, mut visible_lights, ) in &mut views { - let default_render_groups = RenderGroups::default(); - let view_groups = maybe_groups.unwrap_or(&default_render_groups); + let view_groups = extract_render_groups(maybe_inherited, maybe_groups); let clusters = clusters.into_inner(); @@ -1409,8 +1415,7 @@ pub(crate) fn assign_lights_to_clusters( let mut update_from_light_intersections = |visible_lights: &mut Vec| { for light in &lights { // check if the light layers overlap the view layers - let default_render_groups = RenderGroups::default(); - if !view_groups.intersects(&default_render_groups) {//&light.render_groups) { + if !view_groups.intersects(&RenderGroups::default()) {//&light.render_groups) { continue; } @@ -1828,6 +1833,7 @@ pub fn check_light_mesh_visibility( &CubemapFrusta, &mut CubemapVisibleEntities, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, )>, mut spot_lights: Query<( &SpotLight, @@ -1835,6 +1841,7 @@ pub fn check_light_mesh_visibility( &Frustum, &mut VisibleEntities, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, )>, mut directional_lights: Query< ( @@ -1842,6 +1849,7 @@ pub fn check_light_mesh_visibility( &CascadesFrusta, &mut CascadesVisibleEntities, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, &mut ViewVisibility, ), Without, @@ -1852,6 +1860,7 @@ pub fn check_light_mesh_visibility( &InheritedVisibility, &mut ViewVisibility, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, Option<&Aabb>, Option<&GlobalTransform>, ), @@ -1875,8 +1884,7 @@ pub fn check_light_mesh_visibility( } // Directional lights - let default_render_groups = RenderGroups::default(); - for (directional_light, frusta, mut visible_entities, maybe_view_mask, light_view_visibility) in + for (directional_light, frusta, mut visible_entities, maybe_view_mask, maybe_vm_inherited, light_view_visibility) in &mut directional_lights { // Re-use already allocated entries where possible. @@ -1907,13 +1915,14 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = maybe_view_mask.unwrap_or(&default_render_groups); + let view_mask = render_groups_asref(maybe_vm_inherited, maybe_view_mask); for ( entity, inherited_visibility, mut view_visibility, maybe_entity_mask, + maybe_em_inherited, maybe_aabb, maybe_transform, ) in &mut visible_entity_query @@ -1922,8 +1931,7 @@ pub fn check_light_mesh_visibility( continue; } - let entity_mask = maybe_entity_mask.unwrap_or(&default_render_groups); - if !view_mask.intersects(&entity_mask) { + if !view_mask.intersects(&render_groups_asref(maybe_em_inherited, maybe_entity_mask)) { continue; } @@ -1976,6 +1984,7 @@ pub fn check_light_mesh_visibility( cubemap_frusta, mut cubemap_visible_entities, maybe_view_mask, + maybe_vm_inherited, )) = point_lights.get_mut(light_entity) { for visible_entities in cubemap_visible_entities.iter_mut() { @@ -1987,7 +1996,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = maybe_view_mask.unwrap_or(&default_render_groups); + let view_mask = render_groups_asref(maybe_vm_inherited, maybe_view_mask); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -1998,6 +2007,7 @@ pub fn check_light_mesh_visibility( inherited_visibility, mut view_visibility, maybe_entity_mask, + maybe_em_inherited, maybe_aabb, maybe_transform, ) in &mut visible_entity_query @@ -2006,8 +2016,7 @@ pub fn check_light_mesh_visibility( continue; } - let entity_mask = maybe_entity_mask.unwrap_or(&default_render_groups); - if !view_mask.intersects(&entity_mask) { + if !view_mask.intersects(&render_groups_asref(maybe_em_inherited, maybe_entity_mask)) { continue; } @@ -2042,7 +2051,7 @@ pub fn check_light_mesh_visibility( } // Spot lights - if let Ok((point_light, transform, frustum, mut visible_entities, maybe_view_mask)) = + if let Ok((point_light, transform, frustum, mut visible_entities, maybe_view_mask, maybe_vm_inherited)) = spot_lights.get_mut(light_entity) { visible_entities.entities.clear(); @@ -2052,7 +2061,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = maybe_view_mask.unwrap_or(&default_render_groups); + let view_mask = render_groups_asref(maybe_vm_inherited, maybe_view_mask); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2063,6 +2072,7 @@ pub fn check_light_mesh_visibility( inherited_visibility, mut view_visibility, maybe_entity_mask, + maybe_em_inherited, maybe_aabb, maybe_transform, ) in &mut visible_entity_query @@ -2071,8 +2081,7 @@ pub fn check_light_mesh_visibility( continue; } - let entity_mask = maybe_entity_mask.unwrap_or(&default_render_groups); - if !view_mask.intersects(&entity_mask) { + if !view_mask.intersects(&render_groups_asref(maybe_em_inherited, maybe_entity_mask)) { continue; } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index b0a3e09301f34..d6bbf534c1740 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -14,7 +14,9 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ExtractedView, RenderGroups, ViewVisibility, VisibleEntities}, + view::{ + extract_render_groups, ExtractedView, ExtractedRenderGroups, InheritedRenderGroups, RenderGroups, + ViewVisibility, VisibleEntities}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -51,7 +53,7 @@ pub struct ExtractedDirectionalLight { pub cascade_shadow_config: CascadeShadowConfig, pub cascades: EntityHashMap>, pub frusta: EntityHashMap>, - pub render_groups: RenderGroups, + pub render_groups: ExtractedRenderGroups, } #[derive(Copy, Clone, ShaderType, Default, Debug)] @@ -346,6 +348,7 @@ pub fn extract_lights( &GlobalTransform, &ViewVisibility, Option<&RenderGroups>, + Option<&InheritedRenderGroups>, ), Without, >, @@ -468,6 +471,7 @@ pub fn extract_lights( transform, view_visibility, maybe_groups, + maybe_inherited, ) in &directional_lights { if !view_visibility.get() { @@ -488,7 +492,7 @@ pub fn extract_lights( cascade_shadow_config: cascade_config.clone(), cascades: cascades.cascades.clone(), frusta: frusta.frusta.clone(), - render_groups: maybe_groups.cloned().unwrap_or(RenderGroups::default()), + render_groups: extract_render_groups(maybe_inherited, maybe_groups), }, render_visible_entities, )); diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index e3b05908d0a33..4152bb19742bb 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -6,7 +6,7 @@ use crate::{ render_asset::RenderAssets, render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_resource::TextureView, - view::{ColorGrading, ExtractedView, ExtractedWindows, RenderGroups, VisibleEntities}, + view::{CameraView, ColorGrading, extract_camera_view, ExtractedView, ExtractedWindows, VisibleEntities}, Extract, }; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; @@ -811,7 +811,7 @@ pub fn extract_cameras( Option<&ColorGrading>, Option<&Exposure>, Option<&TemporalJitter>, - Option<&RenderGroups>, + Option<&CameraView>, Option<&Projection>, )>, >, @@ -828,7 +828,7 @@ pub fn extract_cameras( color_grading, exposure, temporal_jitter, - render_groups, + camera_view, projection, ) in query.iter() { @@ -888,16 +888,13 @@ pub fn extract_cameras( }, visible_entities.clone(), *frustum, + extract_camera_view(entity, camera_view), )); if let Some(temporal_jitter) = temporal_jitter { commands.insert(temporal_jitter.clone()); } - if let Some(render_groups) = render_groups { - commands.insert(render_groups.clone()); - } - if let Some(perspective) = projection { commands.insert(perspective.clone()); } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index ba3c018edc942..db7c443cdf751 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -375,7 +375,7 @@ pub fn prepare_view_uniforms( Option<&Frustum>, Option<&TemporalJitter>, Option<&MipBias>, - Option<&RenderGroups>, + Option<&ExtractedRenderGroups>, )>, ) { let view_iter = views.iter(); diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 531ee1d2e6414..5e50446817ad1 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -139,13 +139,23 @@ use bevy_hierarchy::{Children, Parent}; use bevy_utils::error_once; use bevy_utils::tracing::warn; +use std::ops::Deref; + /// Returned by [`PropagateRenderGroups::get_render_groups`]. -pub enum PropagatingRenderGroups<'a> { +pub enum RenderGroupsRef<'a> { Ref(&'a RenderGroups), Val(RenderGroups), } -impl<'a> PropagatingRenderGroups<'a> { +impl<'a> Deref for RenderGroupsRef<'a> { + type Target = RenderGroups; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl<'a> RenderGroupsRef<'a> { /// Gets a reference to the internal [`RenderGroups`]. pub fn get(&self) -> &RenderGroups { match self { @@ -187,31 +197,31 @@ impl PropagateRenderGroups { groups: Option<&'a RenderGroups>, view: Option<&CameraView>, is_camera: bool, - ) -> PropagatingRenderGroups<'a> { + ) -> RenderGroupsRef<'a> { match self { Self::Auto => { let Some(groups) = groups else { - return PropagatingRenderGroups::Val(RenderGroups::default()); + return RenderGroupsRef::Val(RenderGroups::default()); }; - PropagatingRenderGroups::Ref(groups) + RenderGroupsRef::Ref(groups) } Self::Camera => { if !is_camera { warn!("failed propagating PropagateRenderGroups::Camera, {entity} doesn't have a camera"); - PropagatingRenderGroups::Val(RenderGroups::empty()); + RenderGroupsRef::Val(RenderGroups::empty()); }; - PropagatingRenderGroups::Val(RenderGroups::new_with_camera(entity)) + RenderGroupsRef::Val(RenderGroups::new_with_camera(entity)) } Self::CameraWithView => { if !is_camera { warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); - PropagatingRenderGroups::Val(RenderGroups::empty()); + RenderGroupsRef::Val(RenderGroups::empty()); }; let empty_camera_view = CameraView::empty(); let view = view.unwrap_or(&empty_camera_view); - PropagatingRenderGroups::Val(view.get_groups(entity)) + RenderGroupsRef::Val(view.get_groups(entity)) } - Self::Custom(groups) => PropagatingRenderGroups::Ref(groups), + Self::Custom(groups) => RenderGroupsRef::Ref(groups), } } } @@ -252,6 +262,58 @@ impl InheritedRenderGroups { } } +/// Contains the final [`RenderGroups`] of an entity for extraction to the render world. +#[derive(Component, Debug, Deref)] +pub struct ExtractedRenderGroups(RenderGroups); + +/// Evaluates an entity's possible `RenderGroups` and `InheritedRenderGroups` components to get a +/// final [`ExtractedRenderGroups`] for the render world. +/// +/// Potentially allocates if [`InheritedRenderGroups`] or [`RenderGroups`] is allocated. +pub fn extract_render_groups( + inherited: Option<&InheritedRenderGroups>, + render_groups: Option<&RenderGroups>, +) -> ExtractedRenderGroups { + ExtractedRenderGroups( + inherited + .map(|i| &i.computed) + .or(render_groups) + .cloned() + .unwrap_or(RenderGroups::default()) + ) +} + +/// Evaluates a camera's possible `CameraView` and `InheritedRenderGroups` components to get a +/// final [`ExtractedRenderGroups`] for the render world. +/// +/// Potentially allocates if [`InheritedRenderGroups`] or [`RenderGroups`] is allocated. +pub fn extract_camera_view( + camera: Entity, + camera_view: Option<&CameraView>, +) -> ExtractedRenderGroups { + ExtractedRenderGroups( + camera_view + .map(|i| i.get_groups(camera)) + .unwrap_or(RenderGroups::default_with_camera(camera)) + ) +} + +/// Converts an optional [`InheritedRenderGroups`] and [`RenderGroups`] into [`RenderGroupsRef`]. +/// +/// Returns [`RenderGroups::default`] if both optionals are `None`. +pub fn render_groups_asref<'a>( + inherited: Option<&'a InheritedRenderGroups>, + render_groups: Option<&'a RenderGroups>, +) -> RenderGroupsRef<'a> { + if let Some(inherited) = inherited { + RenderGroupsRef::Ref(&inherited.computed) + } else if let Some(render_groups) = render_groups { + RenderGroupsRef::Ref(render_groups) + } else { + RenderGroupsRef::Val(RenderGroups::default()) + } +} + /// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on /// entities. #[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] @@ -368,7 +430,7 @@ fn propagate_updated_propagators( // Get value to propagate. // TODO: This can allocate spuriously if there are no children that need it. - let propagated: PropagatingRenderGroups<'_> = propagate.get_render_groups( + let propagated: RenderGroupsRef<'_> = propagate.get_render_groups( propagator, maybe_render_groups, maybe_camera_view, @@ -385,7 +447,7 @@ fn propagate_updated_propagators( &children_query, &mut maybe_inherited, propagator, - propagated.get(), + &propagated, child, ); } @@ -503,7 +565,7 @@ fn propagate_to_new_children( &children_query, &mut maybe_inherited, propagator, - propagated.get(), + &propagated, child, ); } @@ -768,7 +830,7 @@ fn handle_lost_propagator( &children_query, &mut maybe_inherited, propagator, - propagated.get(), + &propagated, entity, ); // In all other cases, remove all InheritedRenderGroups. @@ -972,7 +1034,7 @@ fn handle_new_children_nonpropagator( &children_query, &mut maybe_inherited, propagator, - propagated.get(), + &propagated, child, ); } @@ -1101,7 +1163,7 @@ fn handle_modified_rendergroups( inherited .computed .set_from(entity_render_groups.unwrap_or(&RenderGroups::default())); - inherited.computed.merge(propagated.get()); + inherited.computed.merge(&propagated); // Mark updated (in case of duplicates due to removals). updated_entities.insert(entity); diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index e6f4c3f1bb594..068d344e3afbf 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -1,3 +1,5 @@ +use crate::view::ExtractedRenderGroups; + use bevy_derive::{Deref, DerefMut}; use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; use bevy_reflect::Reflect; @@ -273,6 +275,14 @@ impl RenderGroups { } } + /// Makes a new `RenderGroups` with a camera and the [`DEFAULT_RENDER_LAYER`]. + pub fn default_with_camera(camera: Entity) -> Self { + Self { + layers: RenderXXLayersXX::default(), + camera: Some(camera), + } + } + /// Adds a [`RenderLayer`]. /// /// See [`RenderXXLayersXX::add`]. @@ -353,6 +363,15 @@ impl RenderGroups { self.layers.intersects(&other.layers) } + /// Returns `true` if `Self` intersects with an [`ExtractedRenderGroups`]. + /// + /// If `extracted` is `None`, then intersections is tested using [`RenderGroups::default`]. + pub fn intersects_extracted(&self, extracted: Option<&ExtractedRenderGroups>) -> bool { + let default_render_groups = RenderGroups::default(); + let render_groups = extracted.map(|i| &**i).unwrap_or(&default_render_groups); + self.intersects(render_groups) + } + /// Gets the camera affiliation. pub fn camera(&self) -> Option { self.camera From d6224896208bc847b8136f055b968ba720d3a22e Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 15:56:52 -0500 Subject: [PATCH 13/54] rearrange --- .../visibility/propagate_render_groups.rs | 26 ------------------ .../src/view/visibility/render_groups.rs | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 5e50446817ad1..caec5c25afc74 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -139,32 +139,6 @@ use bevy_hierarchy::{Children, Parent}; use bevy_utils::error_once; use bevy_utils::tracing::warn; -use std::ops::Deref; - -/// Returned by [`PropagateRenderGroups::get_render_groups`]. -pub enum RenderGroupsRef<'a> { - Ref(&'a RenderGroups), - Val(RenderGroups), -} - -impl<'a> Deref for RenderGroupsRef<'a> { - type Target = RenderGroups; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl<'a> RenderGroupsRef<'a> { - /// Gets a reference to the internal [`RenderGroups`]. - pub fn get(&self) -> &RenderGroups { - match self { - Self::Ref(groups) => groups, - Self::Val(groups) => &groups, - } - } -} - /// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. /// /// Entities with this component will ignore [`RenderGroups`] propagated by parents. diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 068d344e3afbf..a1708c43b983f 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -5,6 +5,7 @@ use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; use bevy_reflect::Reflect; use bevy_reflect::prelude::ReflectDefault; +use std::ops::Deref; use smallvec::SmallVec; /// The default [`RenderLayer`]. @@ -405,6 +406,32 @@ impl Default for RenderGroups { } } +/// Stores a [`RenderGroups`] reference or value. +/// +/// Useful for unwrapping an optional reference with fallback to a default value. +pub enum RenderGroupsRef<'a> { + Ref(&'a RenderGroups), + Val(RenderGroups), +} + +impl<'a> Deref for RenderGroupsRef<'a> { + type Target = RenderGroups; + + fn deref(&self) -> &Self::Target { + self.get() + } +} + +impl<'a> RenderGroupsRef<'a> { + /// Gets a reference to the internal [`RenderGroups`]. + pub fn get(&self) -> &RenderGroups { + match self { + Self::Ref(groups) => groups, + Self::Val(groups) => &groups, + } + } +} + /// Component on camera entities that controls which [`RenderXXLayersXX`] are visible to /// the camera. /// From 4b4bed34d3601e0c530a162326972b1392b10778 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 16:41:45 -0500 Subject: [PATCH 14/54] cleanup --- crates/bevy_pbr/src/light/mod.rs | 14 +++++++------- .../src/view/visibility/propagate_render_groups.rs | 9 ++++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 22077305f7c76..5dde6de6026c2 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -14,7 +14,7 @@ use bevy_render::{ render_resource::BufferBindingType, renderer::RenderDevice, view::{ - extract_render_groups, InheritedRenderGroups, InheritedVisibility, render_groups_asref, RenderGroups, + extract_render_groups, InheritedRenderGroups, InheritedVisibility, derive_render_groups, RenderGroups, ViewVisibility, VisibleEntities }, }; @@ -1915,7 +1915,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = render_groups_asref(maybe_vm_inherited, maybe_view_mask); + let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); for ( entity, @@ -1931,7 +1931,7 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask.intersects(&render_groups_asref(maybe_em_inherited, maybe_entity_mask)) { + if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { continue; } @@ -1996,7 +1996,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = render_groups_asref(maybe_vm_inherited, maybe_view_mask); + let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2016,7 +2016,7 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask.intersects(&render_groups_asref(maybe_em_inherited, maybe_entity_mask)) { + if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { continue; } @@ -2061,7 +2061,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = render_groups_asref(maybe_vm_inherited, maybe_view_mask); + let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2081,7 +2081,7 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask.intersects(&render_groups_asref(maybe_em_inherited, maybe_entity_mask)) { + if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { continue; } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index caec5c25afc74..1602eac9acfad 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -272,10 +272,13 @@ pub fn extract_camera_view( ) } -/// Converts an optional [`InheritedRenderGroups`] and [`RenderGroups`] into [`RenderGroupsRef`]. +/// Derives a [`RenderGroupsRef`] from an optional [`InheritedRenderGroups`] and [`RenderGroups`]. /// -/// Returns [`RenderGroups::default`] if both optionals are `None`. -pub fn render_groups_asref<'a>( +/// Returns in order of priority: +/// - [`InheritedRenderGroups::computed`] +/// - [`RenderGroups`] +/// - [`RenderGroups::default`] +pub fn derive_render_groups<'a>( inherited: Option<&'a InheritedRenderGroups>, render_groups: Option<&'a RenderGroups>, ) -> RenderGroupsRef<'a> { From 829be8a5d95d8930b5e295be8ad42b6bef2b5e57 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 16:41:58 -0500 Subject: [PATCH 15/54] fmt --- crates/bevy_pbr/src/light/mod.rs | 39 +++++++++++++------ crates/bevy_pbr/src/render/light.rs | 7 ++-- crates/bevy_render/src/camera/camera.rs | 5 ++- crates/bevy_render/src/view/mod.rs | 2 +- crates/bevy_render/src/view/visibility/mod.rs | 15 +++---- .../visibility/propagate_render_groups.rs | 4 +- .../src/view/visibility/render_groups.rs | 15 ++++--- examples/2d/pixel_grid_snap.rs | 8 +++- examples/3d/render_to_texture.rs | 2 +- 9 files changed, 64 insertions(+), 33 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 5dde6de6026c2..5e405c0cf8e32 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -14,8 +14,8 @@ use bevy_render::{ render_resource::BufferBindingType, renderer::RenderDevice, view::{ - extract_render_groups, InheritedRenderGroups, InheritedVisibility, derive_render_groups, RenderGroups, - ViewVisibility, VisibleEntities + derive_render_groups, extract_render_groups, InheritedRenderGroups, InheritedVisibility, + RenderGroups, ViewVisibility, VisibleEntities, }, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -1099,7 +1099,7 @@ pub(crate) fn assign_lights_to_clusters( shadows_enabled: point_light.shadows_enabled, range: point_light.range, spot_light_angle: None, - render_groups: 0u32//extract_render_groups(maybe_inherited, maybe_groups), + render_groups: 0u32, //extract_render_groups(maybe_inherited, maybe_groups), } }, ), @@ -1116,7 +1116,7 @@ pub(crate) fn assign_lights_to_clusters( shadows_enabled: spot_light.shadows_enabled, range: spot_light.range, spot_light_angle: Some(spot_light.outer_angle), - render_groups: 0u32//extract_render_groups(maybe_inherited, maybe_groups), + render_groups: 0u32, //extract_render_groups(maybe_inherited, maybe_groups), } }, ), @@ -1415,7 +1415,8 @@ pub(crate) fn assign_lights_to_clusters( let mut update_from_light_intersections = |visible_lights: &mut Vec| { for light in &lights { // check if the light layers overlap the view layers - if !view_groups.intersects(&RenderGroups::default()) {//&light.render_groups) { + if !view_groups.intersects(&RenderGroups::default()) { + //&light.render_groups) { continue; } @@ -1884,8 +1885,14 @@ pub fn check_light_mesh_visibility( } // Directional lights - for (directional_light, frusta, mut visible_entities, maybe_view_mask, maybe_vm_inherited, light_view_visibility) in - &mut directional_lights + for ( + directional_light, + frusta, + mut visible_entities, + maybe_view_mask, + maybe_vm_inherited, + light_view_visibility, + ) in &mut directional_lights { // Re-use already allocated entries where possible. let mut views_to_remove = Vec::new(); @@ -2016,7 +2023,9 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { + if !view_mask + .intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) + { continue; } @@ -2051,8 +2060,14 @@ pub fn check_light_mesh_visibility( } // Spot lights - if let Ok((point_light, transform, frustum, mut visible_entities, maybe_view_mask, maybe_vm_inherited)) = - spot_lights.get_mut(light_entity) + if let Ok(( + point_light, + transform, + frustum, + mut visible_entities, + maybe_view_mask, + maybe_vm_inherited, + )) = spot_lights.get_mut(light_entity) { visible_entities.entities.clear(); @@ -2081,7 +2096,9 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { + if !view_mask + .intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) + { continue; } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index d6bbf534c1740..7ec7c7ffe66ea 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -15,8 +15,9 @@ use bevy_render::{ renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, view::{ - extract_render_groups, ExtractedView, ExtractedRenderGroups, InheritedRenderGroups, RenderGroups, - ViewVisibility, VisibleEntities}, + extract_render_groups, ExtractedRenderGroups, ExtractedView, InheritedRenderGroups, + RenderGroups, ViewVisibility, VisibleEntities, + }, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -921,7 +922,7 @@ pub fn prepare_lights( num_cascades: num_cascades as u32, cascades_overlap_proportion: light.cascade_shadow_config.overlap_proportion, depth_texture_base_index: num_directional_cascades_enabled as u32, - render_groups: 0u32//light.render_groups.bits(), + render_groups: 0u32, //light.render_groups.bits(), }; if index < directional_shadow_enabled_count { num_directional_cascades_enabled += num_cascades; diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 4152bb19742bb..13394715ee454 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -6,7 +6,10 @@ use crate::{ render_asset::RenderAssets, render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_resource::TextureView, - view::{CameraView, ColorGrading, extract_camera_view, ExtractedView, ExtractedWindows, VisibleEntities}, + view::{ + extract_camera_view, CameraView, ColorGrading, ExtractedView, ExtractedWindows, + VisibleEntities, + }, Extract, }; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index db7c443cdf751..73be0e50ee1d1 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -439,7 +439,7 @@ pub fn prepare_view_uniforms( frustum, color_grading: extracted_view.color_grading, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, - render_groups: 0u32//maybe_groups.cloned().unwrap_or(RenderGroups::default()), + render_groups: 0u32, //maybe_groups.cloned().unwrap_or(RenderGroups::default()), }), }; diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 68463e7d69997..720404159e30d 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -218,11 +218,10 @@ impl Plugin for VisibilityPlugin { use VisibilitySystems::*; app.configure_sets( - PostUpdate, - PropagateRenderGroupsSet - .in_set(VisibilityPropagate), - ) - .add_systems( + PostUpdate, + PropagateRenderGroupsSet.in_set(VisibilityPropagate), + ) + .add_systems( PostUpdate, ( calculate_bounds.in_set(CalculateBounds), @@ -400,7 +399,8 @@ pub fn check_visibility( )>, deterministic_rendering_config: Res, ) { - for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { + for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query + { if !camera.is_active { continue; } @@ -431,7 +431,8 @@ pub fn check_visibility( // - If there is no RenderGroups in the entity, use the *default* value because the // entity is in the DEFAULT_RENDER_LAYER. let default_render_groups = RenderGroups::default(); - let entity_groups = maybe_inherited_groups.map(|i| &i.computed) + let entity_groups = maybe_inherited_groups + .map(|i| &i.computed) .or(maybe_groups) .unwrap_or(&default_render_groups); if !camera_view.entity_is_visible(camera_entity, entity_groups) { diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 1602eac9acfad..7c36eb0ae64a6 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -253,7 +253,7 @@ pub fn extract_render_groups( .map(|i| &i.computed) .or(render_groups) .cloned() - .unwrap_or(RenderGroups::default()) + .unwrap_or(RenderGroups::default()), ) } @@ -268,7 +268,7 @@ pub fn extract_camera_view( ExtractedRenderGroups( camera_view .map(|i| i.get_groups(camera)) - .unwrap_or(RenderGroups::default_with_camera(camera)) + .unwrap_or(RenderGroups::default_with_camera(camera)), ) } diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index a1708c43b983f..cc6089bff47fc 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -2,11 +2,11 @@ use crate::view::ExtractedRenderGroups; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; -use bevy_reflect::Reflect; use bevy_reflect::prelude::ReflectDefault; +use bevy_reflect::Reflect; -use std::ops::Deref; use smallvec::SmallVec; +use std::ops::Deref; /// The default [`RenderLayer`]. pub static DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); @@ -564,12 +564,16 @@ mod rendering_mask_tests { "layer 0 + 1 is mask 3" ); assert_eq!( - RenderXXLayersXX::from(RenderLayer(0)).add(1).remove(0).layers[0], + RenderXXLayersXX::from(RenderLayer(0)) + .add(1) + .remove(0) + .layers[0], 2, "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderXXLayersXX::from(RenderLayer(1)).intersects(&RenderXXLayersXX::from(RenderLayer(1))), + RenderXXLayersXX::from(RenderLayer(1)) + .intersects(&RenderXXLayersXX::from(RenderLayer(1))), "layers match like layers" ); assert!( @@ -592,7 +596,8 @@ mod rendering_mask_tests { ); assert!( - !RenderXXLayersXX::from(RenderLayer(0)).intersects(&RenderXXLayersXX::from(RenderLayer(1))), + !RenderXXLayersXX::from(RenderLayer(0)) + .intersects(&RenderXXLayersXX::from(RenderLayer(1))), "masks with differing layers do not match" ); assert!( diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 8faf01916f5e7..73eb81a37532f 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -7,7 +7,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::{CameraView, RenderLayer, RenderGroups}, + view::{CameraView, RenderGroups, RenderLayer}, }, sprite::MaterialMesh2dBundle, window::WindowResized, @@ -149,7 +149,11 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { // the "outer" camera renders whatever is on `HIGH_RES_LAYER` to the screen. // here, the canvas and one of the sample sprites will be rendered by this camera - commands.spawn((Camera2dBundle::default(), OuterCamera, CameraView::from(HIGH_RES_LAYER))); + commands.spawn(( + Camera2dBundle::default(), + OuterCamera, + CameraView::from(HIGH_RES_LAYER), + )); } /// Rotates entities to demonstrate grid snapping. diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index c2b1ba2bbc53f..f9bd9a4592b2f 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -8,7 +8,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::{RenderLayer, RenderXXLayersXX, RenderGroups}, + view::{RenderGroups, RenderLayer, RenderXXLayersXX}, }, }; From 064d2768c45682529262b593c8a491a00916d9e9 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 17:17:05 -0500 Subject: [PATCH 16/54] eliminate spurious allocations --- .../visibility/propagate_render_groups.rs | 106 +++++++++++++----- .../src/view/visibility/render_groups.rs | 51 +++++++++ 2 files changed, 130 insertions(+), 27 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 7c36eb0ae64a6..d16c5733ee926 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -132,7 +132,7 @@ use crate::view::*; use crate::prelude::Camera; use bevy_app::{App, Plugin, PostUpdate}; -use bevy_derive::{Deref, DerefMut}; +use bevy_derive::Deref; use bevy_ecs::entity::EntityHashSet; use bevy_ecs::prelude::*; use bevy_hierarchy::{Children, Parent}; @@ -167,6 +167,7 @@ pub enum PropagateRenderGroups { impl PropagateRenderGroups { pub fn get_render_groups<'a>( &'a self, + saved: &mut RenderGroups, entity: Entity, groups: Option<&'a RenderGroups>, view: Option<&CameraView>, @@ -193,7 +194,17 @@ impl PropagateRenderGroups { }; let empty_camera_view = CameraView::empty(); let view = view.unwrap_or(&empty_camera_view); - RenderGroupsRef::Val(view.get_groups(entity)) + + if view.is_allocated() && saved.is_allocated() { + // Reuse saved allocation. + let mut temp = RenderGroups::default(); + std::mem::swap(&mut temp, saved); + temp.set_from_parts(Some(entity), &view.layers()); + RenderGroupsRef::Val(temp) + } else { + // Allocate a new RenderGroups + RenderGroupsRef::Val(view.get_groups(entity)) + } } Self::Custom(groups) => RenderGroupsRef::Ref(groups), } @@ -320,8 +331,31 @@ impl Plugin for PropagateRenderGroupsPlugin { } } -#[derive(Resource, Default, Deref, DerefMut)] -struct PropagateRenderGroupsEntityCache(EntityHashSet); +#[derive(Resource, Default)] +struct PropagateRenderGroupsEntityCache { + /// Buffered to absorb spurious allocations during traversals. + saved: RenderGroups, + /// Updated entities. + entities: EntityHashSet, +} + +impl PropagateRenderGroupsEntityCache { + fn insert(&mut self, entity: Entity) { + self.entities.insert(entity); + } + + fn contains(&self, entity: Entity) -> bool { + self.entities.contains(&entity) + } + + fn clear(&mut self) { + self.entities.clear(); + } + + fn saved(&mut self) -> &mut RenderGroups { + &mut self.saved + } +} /// Removes InheritedRenderGroups from entities with PropagateRenderGroups. fn clean_propagators( @@ -401,13 +435,13 @@ fn propagate_updated_propagators( propagators { // There can be duplicates due to component removals. - if updated_entities.contains(&propagator) { + if updated_entities.contains(propagator) { continue; } // Get value to propagate. - // TODO: This can allocate spuriously if there are no children that need it. - let propagated: RenderGroupsRef<'_> = propagate.get_render_groups( + let mut propagated = propagate.get_render_groups( + updated_entities.saved(), propagator, maybe_render_groups, maybe_camera_view, @@ -428,6 +462,9 @@ fn propagate_updated_propagators( child, ); } + + // Reclaim memory + propagated.reclaim(updated_entities.saved()); } } @@ -519,13 +556,13 @@ fn propagate_to_new_children( changed_children.iter() { // The propagator could have been updated in a previous step. - if updated_entities.contains(&propagator) { + if updated_entities.contains(propagator) { continue; } // Get value to propagate. - // TODO: This can allocate spuriously if there are no children that need it. - let propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_groups( + updated_entities.saved(), propagator, maybe_render_groups, maybe_camera_view, @@ -546,6 +583,9 @@ fn propagate_to_new_children( child, ); } + + // Reclaim memory + propagated.reclaim(updated_entities.saved()); } } @@ -706,7 +746,7 @@ fn handle_lost_propagator( .filter_map(|r| unpropagated.get(r).ok()) { // Skip already-updated entities. - if updated_entities.contains(&entity) { + if updated_entities.contains(entity) { continue; } @@ -745,7 +785,7 @@ fn handle_lost_propagator( if let Ok((propagator, ..)) = all_propagators.get(**parent) { // Case 5 Some(propagator) - } else if updated_entities.contains(&**parent) { + } else if updated_entities.contains(**parent) { // Parent was marked updated (but self was not) if let Ok((_, maybe_inherited)) = maybe_inherited.get(entity) { if let Some(inherited) = maybe_inherited { @@ -786,8 +826,8 @@ fn handle_lost_propagator( propagator.and_then(|p| all_propagators.get(p).ok()) { // Propagation value - // TODO: This can allocate spuriously if there are no children that need it. - let propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_groups( + updated_entities.saved(), propagator, maybe_render_groups, maybe_camera_view, @@ -810,6 +850,9 @@ fn handle_lost_propagator( &propagated, entity, ); + + // Reclaim memory + propagated.reclaim(updated_entities.saved()); // In all other cases, remove all InheritedRenderGroups. } else { apply_full_propagation_force_remove( @@ -843,7 +886,7 @@ fn apply_full_propagation_force_update( // Leave if entity is non-updated and inherits a matching propagator. if let Some(inherited) = maybe_inherited_groups.as_ref() { - if (inherited.propagator == propagator) && !updated_entities.contains(&entity) { + if (inherited.propagator == propagator) && !updated_entities.contains(entity) { return; } } @@ -854,8 +897,10 @@ fn apply_full_propagation_force_update( let mut new = InheritedRenderGroups::empty(); if let Some(mut inherited) = maybe_inherited_groups { - // Steal existing allocation if possible. - std::mem::swap(&mut *inherited, &mut new); + // Steal existing allocation if useful. + if inherited.computed.is_allocated() { + std::mem::swap(&mut inherited.computed, &mut new.computed); + } } new.propagator = propagator; @@ -901,7 +946,7 @@ fn apply_full_propagation_force_remove( }; // Leave if entity is non-updated and doesn't have InheritedRenderGroups. - if maybe_inherited_inner.is_none() && !updated_entities.contains(&entity) { + if maybe_inherited_inner.is_none() && !updated_entities.contains(entity) { return; } @@ -953,7 +998,7 @@ fn handle_new_children_nonpropagator( ) { for (entity, children, inherited) in inherited_with_children.iter() { // Skip entity if already updated, which implies children are already in an accurate state. - if updated_entities.contains(&entity) { + if updated_entities.contains(entity) { continue; } @@ -968,7 +1013,7 @@ fn handle_new_children_nonpropagator( for child in children.iter().copied() { // Skip children that were already updated. // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. - if updated_entities.contains(&child) { + if updated_entities.contains(child) { continue; } @@ -986,8 +1031,8 @@ fn handle_new_children_nonpropagator( }; // Get value to propagate. - // TODO: This can allocate spuriously if there are no children that need it. - let propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_groups( + updated_entities.saved(), propagator, maybe_render_groups, maybe_camera_view, @@ -1000,7 +1045,7 @@ fn handle_new_children_nonpropagator( // loop, not children within the recursion which need to be force-updated. The 'span' of entities // we update in this step starts at non-updated children of an entity with InheritedRenderGroups. // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. - if updated_entities.contains(&child) { + if updated_entities.contains(child) { continue; } @@ -1015,6 +1060,9 @@ fn handle_new_children_nonpropagator( child, ); } + + // Reclaim memory + propagated.reclaim(updated_entities.saved()); } } @@ -1047,12 +1095,12 @@ fn handle_new_parent_nonpropagator( ) { for (entity, maybe_children, parent) in inherited_with_parent.iter() { // Skip entity if already updated - if updated_entities.contains(&entity) { + if updated_entities.contains(entity) { continue; } // Skip entity if parent was updated - if updated_entities.contains(&**parent) { + if updated_entities.contains(**parent) { continue; } @@ -1110,7 +1158,7 @@ fn handle_modified_rendergroups( ) { for entity in inherited_changed.iter().chain(removed_rendergroups.read()) { // Skip entity if already updated. - if updated_entities.contains(&entity) { + if updated_entities.contains(entity) { continue; } @@ -1129,7 +1177,8 @@ fn handle_modified_rendergroups( }; // Get propagated value. - let propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_groups( + updated_entities.saved(), propagator, maybe_render_groups, maybe_camera_view, @@ -1144,5 +1193,8 @@ fn handle_modified_rendergroups( // Mark updated (in case of duplicates due to removals). updated_entities.insert(entity); + + // Reclaim memory + propagated.reclaim(updated_entities.saved()); } } diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index cc6089bff47fc..280538cf20ff8 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -159,6 +159,11 @@ impl RenderXXLayersXX { self.layers.as_slice() } + /// Returns `true` if the internal bitmask is on the heap. + pub fn is_allocated(&self) -> bool { + self.layers.spilled() + } + fn layer_info(layer: usize) -> (usize, u64) { let buffer_index = layer / 64; let bit_index = layer % 64; @@ -327,6 +332,15 @@ impl RenderGroups { self.camera = other.camera; } + /// Overwrites self with internal parts. + /// + /// This is more efficient than cloning `other` if you want to reuse a `RenderGroups` + /// that is potentially allocated. + pub fn set_from_parts(&mut self, camera: Option, layers: &RenderXXLayersXX) { + self.layers.set_from(layers); + self.camera = camera; + } + /// Sets the camera affiliation. /// /// Returns the previous camera. @@ -377,6 +391,11 @@ impl RenderGroups { pub fn camera(&self) -> Option { self.camera } + + /// Returns `true` if the internal [`RenderLayers`] is on the heap. + pub fn is_allocated(&self) -> bool { + self.layers.is_allocated() + } } impl From for RenderGroups { @@ -410,10 +429,29 @@ impl Default for RenderGroups { /// /// Useful for unwrapping an optional reference with fallback to a default value. pub enum RenderGroupsRef<'a> { + None, Ref(&'a RenderGroups), Val(RenderGroups), } +impl<'a> RenderGroupsRef<'a> { + /// Passes [`RenderLists`] to `other` if `self` is on the heap. + /// + /// The ref will be in an invalid state after this is called. + pub(crate) fn reclaim(&mut self, other: &mut RenderGroups) { + match self { + Self::Val(groups) => + { + if !groups.is_allocated() || other.is_allocated() { + return; + } + std::mem::swap(groups, other); + } + _ => () + } + } +} + impl<'a> Deref for RenderGroupsRef<'a> { type Target = RenderGroups; @@ -424,8 +462,11 @@ impl<'a> Deref for RenderGroupsRef<'a> { impl<'a> RenderGroupsRef<'a> { /// Gets a reference to the internal [`RenderGroups`]. + /// + /// Panices if in state [`Self::None`]. pub fn get(&self) -> &RenderGroups { match self { + Self::None => { panic!("RenderGroupsRef cannot be dereferenced when empty"); } Self::Ref(groups) => groups, Self::Val(groups) => &groups, } @@ -482,6 +523,11 @@ impl CameraView { self.layers.clear(); } + /// Returns a reference to the internal [`RenderLayers`]. + pub fn layers(&self) -> &RenderXXLayersXX { + &self.layers + } + /// Returns an iterator over [`RenderLayer`]. pub fn iter_layers(&self) -> impl Iterator + '_ { self.layers.iter() @@ -510,6 +556,11 @@ impl CameraView { groups.set_camera(camera); groups } + + /// Returns `true` if the internal [`RenderLayers`] is on the heap. + pub fn is_allocated(&self) -> bool { + self.layers.is_allocated() + } } impl From for CameraView { From a70bd8c96d46a180968495d895acc8507f59382d Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 17:25:10 -0500 Subject: [PATCH 17/54] finish removing old RenderLayers --- crates/bevy_render/src/view/mod.rs | 4 +- crates/bevy_render/src/view/visibility/mod.rs | 2 - .../src/view/visibility/render_groups.rs | 129 ++++++------ .../src/view/visibility/render_layers.rs | 186 ------------------ examples/2d/pixel_grid_snap.rs | 4 +- examples/3d/render_to_texture.rs | 4 +- 6 files changed, 72 insertions(+), 257 deletions(-) delete mode 100644 crates/bevy_render/src/view/visibility/render_layers.rs diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 73be0e50ee1d1..4f0e5945d3d64 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -47,7 +47,9 @@ impl Plugin for ViewPlugin { .register_type::() .register_type::() .register_type::() - //.register_type::() + .register_type::() + .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 720404159e30d..25371140a826b 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,11 +1,9 @@ mod propagate_render_groups; mod render_groups; -//mod render_layers; use bevy_derive::Deref; pub use propagate_render_groups::*; pub use render_groups::*; -//pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{Assets, Handle}; diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 280538cf20ff8..81651ace3c1a2 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -11,9 +11,9 @@ use std::ops::Deref; /// The default [`RenderLayer`]. pub static DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); -/// Wraps a specific render layer that can be stored in [`RenderXXLayersXX`]. +/// Wraps a specific render layer that can be stored in [`RenderLayers`]. /// -/// Stores an index into the [`RenderXXLayersXX`] internal bitmask. +/// Stores an index into the [`RenderLayers`] internal bitmask. //todo: Upper limit policy for render layer indices. #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deref, DerefMut)] pub struct RenderLayer(pub usize); @@ -41,32 +41,32 @@ impl Default for RenderLayer { /// are visible to which cameras. /// /// Individual render layers can be defined with [`RenderLayer`], which is an index -/// into the internal `RenderXXLayersXX` bitmask. +/// into the internal `RenderLayers` bitmask. /// -/// `RenderXXLayersXX::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default +/// `RenderLayers::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default /// layer. /// /// ### Performance /// -/// `RenderXXLayersXX` occupies 24 bytes on the stack. +/// `RenderLayers` occupies 24 bytes on the stack. /// -/// `RenderXXLayersXX` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte +/// `RenderLayers` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. #[derive(Debug, Clone, PartialEq, Reflect)] #[reflect(Default, PartialEq)] -pub struct RenderXXLayersXX { +pub struct RenderLayers { layers: SmallVec<[u64; 1]>, } -impl RenderXXLayersXX { - /// Makes a new `RenderXXLayersXX` with no layers. +impl RenderLayers { + /// Makes a new `RenderLayers` with no layers. pub fn empty() -> Self { Self { layers: SmallVec::default(), } } - /// Makes a new `RenderXXLayersXX` from a slice. + /// Makes a new `RenderLayers` from a slice. pub fn from_layers + Copy>(layers: &[T]) -> Self { layers.iter().map(|l| (*l).into()).collect() } @@ -100,7 +100,7 @@ impl RenderXXLayersXX { /// Copies `other` into `Self`. /// - /// This is more efficient than cloning `other` if you want to reuse a `RenderXXLayersXX` + /// This is more efficient than cloning `other` if you want to reuse a `RenderLayers` /// that is potentially allocated. pub fn set_from(&mut self, other: &Self) { self.layers.clear(); @@ -133,7 +133,7 @@ impl RenderXXLayersXX { self.layers.iter().copied().map(Self::iter_layers).flatten() } - /// Returns `true` if the specified render layer is included in this `RenderXXLayersXX`. + /// Returns `true` if the specified render layer is included in this `RenderLayers`. pub fn contains(&self, layer: impl Into) -> bool { let (buffer_index, bit) = Self::layer_info(*(layer.into())); if buffer_index >= self.layers.len() { @@ -192,7 +192,7 @@ impl RenderXXLayersXX { } } -impl> From for RenderXXLayersXX { +impl> From for RenderLayers { fn from(layer: T) -> Self { let mut layers = Self { layers: SmallVec::default(), @@ -202,7 +202,7 @@ impl> From for RenderXXLayersXX { } } -impl> FromIterator for RenderXXLayersXX { +impl> FromIterator for RenderLayers { fn from_iter>(i: T) -> Self { i.into_iter().fold(Self::empty(), |mut mask, g| { mask.add(g); @@ -211,7 +211,7 @@ impl> FromIterator for RenderXXLayersXX { } } -impl Default for RenderXXLayersXX { +impl Default for RenderLayers { fn default() -> Self { Self::from(DEFAULT_RENDER_LAYER) } @@ -220,11 +220,11 @@ impl Default for RenderXXLayersXX { /// Component on an entity that controls which cameras can see it. /// /// There are two kinds of render groups: -/// - [`RenderXXLayersXX`]: These are grouping categories that many cameras can view (see [`CameraView`]). +/// - [`RenderLayers`]: These are grouping categories that many cameras can view (see [`CameraView`]). /// - *Camera entity*: This is a specific camera that the entity is affiliated with. This is especially /// useful for UI in combination with [`PropagateRenderGroups`]. /// -/// An entity can be a member of multiple [`RenderXXLayersXX`] in addition to having a camera affiliation. +/// An entity can be a member of multiple [`RenderLayers`] in addition to having a camera affiliation. /// /// ### Default behavior /// @@ -260,7 +260,7 @@ entity.insert(groups); #[derive(Component, Debug, Clone, Reflect, PartialEq)] #[reflect(Component, Default, PartialEq)] pub struct RenderGroups { - layers: RenderXXLayersXX, + layers: RenderLayers, camera: Option, } @@ -268,7 +268,7 @@ impl RenderGroups { /// Makes a new `RenderGroups` with no groups. pub fn empty() -> Self { Self { - layers: RenderXXLayersXX::empty(), + layers: RenderLayers::empty(), camera: None, } } @@ -276,7 +276,7 @@ impl RenderGroups { /// Makes a new `RenderGroups` with just a camera. pub fn new_with_camera(camera: Entity) -> Self { Self { - layers: RenderXXLayersXX::empty(), + layers: RenderLayers::empty(), camera: Some(camera), } } @@ -284,14 +284,14 @@ impl RenderGroups { /// Makes a new `RenderGroups` with a camera and the [`DEFAULT_RENDER_LAYER`]. pub fn default_with_camera(camera: Entity) -> Self { Self { - layers: RenderXXLayersXX::default(), + layers: RenderLayers::default(), camera: Some(camera), } } /// Adds a [`RenderLayer`]. /// - /// See [`RenderXXLayersXX::add`]. + /// See [`RenderLayers::add`]. pub fn add(&mut self, layer: impl Into) -> &mut Self { self.layers.add(layer); self @@ -299,7 +299,7 @@ impl RenderGroups { /// Removes a [`RenderLayer`]. /// - /// See [`RenderXXLayersXX::remove`]. + /// See [`RenderLayers::remove`]. pub fn remove(&mut self, layer: impl Into) -> &mut Self { self.layers.remove(layer); self @@ -313,11 +313,11 @@ impl RenderGroups { /// Merges `other` into `Self`. /// - /// After merging, `Self` will include all [`RenderXXLayersXX`] from `other` and `Self`. + /// After merging, `Self` will include all [`RenderLayers`] from `other` and `Self`. /// If both `Self` and `other` have a camera affiliation, then the `Self` camera /// will be in the merged result. Otherwise the `other` camera will be in the result. /// - /// Will allocate if necessary to include all [`RenderXXLayersXX`] of `other`. + /// Will allocate if necessary to include all [`RenderLayers`] of `other`. pub fn merge(&mut self, other: &Self) { self.layers.merge(&other.layers); self.camera = self.camera.or(other.camera); @@ -334,9 +334,9 @@ impl RenderGroups { /// Overwrites self with internal parts. /// - /// This is more efficient than cloning `other` if you want to reuse a `RenderGroups` + /// This is more efficient than cloning `layers` if you want to reuse a `RenderLayers` /// that is potentially allocated. - pub fn set_from_parts(&mut self, camera: Option, layers: &RenderXXLayersXX) { + pub fn set_from_parts(&mut self, camera: Option, layers: &RenderLayers) { self.layers.set_from(layers); self.camera = camera; } @@ -368,7 +368,7 @@ impl RenderGroups { /// Returns `true` if `Self` intersects with `other`. /// - /// Checks both camera affiliation and [`RenderXXLayersXX`] intersection. + /// Checks both camera affiliation and [`RenderLayers`] intersection. pub fn intersects(&self, other: &Self) -> bool { if let (Some(a), Some(b)) = (self.camera, other.camera) { if a == b { @@ -402,15 +402,15 @@ impl From for RenderGroups { /// Makes a new `RenderGroups` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { Self { - layers: RenderXXLayersXX::from(layer), + layers: RenderLayers::from(layer), camera: None, } } } -impl From for RenderGroups { - /// Makes a new `RenderGroups` from a [`RenderXXLayersXX`]. - fn from(layers: RenderXXLayersXX) -> Self { +impl From for RenderGroups { + /// Makes a new `RenderGroups` from a [`RenderLayers`]. + fn from(layers: RenderLayers) -> Self { Self { layers, camera: None, @@ -473,7 +473,7 @@ impl<'a> RenderGroupsRef<'a> { } } -/// Component on camera entities that controls which [`RenderXXLayersXX`] are visible to +/// Component on camera entities that controls which [`RenderLayers`] are visible to /// the camera. /// /// A camera will see any entity that satisfies either of these conditions: @@ -489,22 +489,23 @@ impl<'a> RenderGroupsRef<'a> { /// layer, in addition to relevant [`RenderGroups`] camera affiliations. /// /// A default `CameraView` will include the [`DEFAULT_RENDER_LAYER`]. -#[derive(Component, Debug, Clone)] +#[derive(Component, Debug, Clone, PartialEq, Reflect)] +#[reflect(Component, Default, PartialEq)] pub struct CameraView { - layers: RenderXXLayersXX, + layers: RenderLayers, } impl CameraView { /// Makes a new `CameraView` with no visible [`RenderLayer`]. pub fn empty() -> Self { Self { - layers: RenderXXLayersXX::empty(), + layers: RenderLayers::empty(), } } /// Adds a [`RenderLayer`]. /// - /// See [`RenderXXLayersXX::add`]. + /// See [`RenderLayers::add`]. pub fn add(&mut self, layer: impl Into) -> &mut Self { self.layers.add(layer); self @@ -512,7 +513,7 @@ impl CameraView { /// Removes a [`RenderLayer`]. /// - /// See [`RenderXXLayersXX::remove`]. + /// See [`RenderLayers::remove`]. pub fn remove(&mut self, layer: impl Into) -> &mut Self { self.layers.remove(layer); self @@ -524,7 +525,7 @@ impl CameraView { } /// Returns a reference to the internal [`RenderLayers`]. - pub fn layers(&self) -> &RenderXXLayersXX { + pub fn layers(&self) -> &RenderLayers { &self.layers } @@ -541,7 +542,7 @@ impl CameraView { /// Returns `true` if the entity with the specified [`RenderGroups`] is visible /// to the `camera` that has this `CameraView`. /// - /// Checks both camera affiliation and [`RenderXXLayersXX`] intersection. + /// Checks both camera affiliation and [`RenderLayers`] intersection. pub fn entity_is_visible(&self, camera: Entity, groups: &RenderGroups) -> bool { if Some(camera) == groups.camera { return true; @@ -549,7 +550,7 @@ impl CameraView { self.layers.intersects(&groups.layers) } - /// Converts the internal [`RenderXXLayersXX`] into a [`RenderGroups`] affiliated + /// Converts the internal [`RenderLayers`] into a [`RenderGroups`] affiliated /// with the camera that has this `CameraView`. pub fn get_groups(&self, camera: Entity) -> RenderGroups { let mut groups = RenderGroups::from(self.layers.clone()); @@ -567,7 +568,7 @@ impl From for CameraView { /// Makes a new `CameraView` from a specific [`RenderLayer`]. fn from(layer: RenderLayer) -> Self { Self { - layers: RenderXXLayersXX::from(layer), + layers: RenderLayers::from(layer), } } } @@ -581,41 +582,41 @@ impl Default for CameraView { #[cfg(test)] mod rendering_mask_tests { - use super::{RenderLayer, RenderXXLayersXX, DEFAULT_RENDER_LAYER}; + use super::{RenderLayer, RenderLayers, DEFAULT_RENDER_LAYER}; use smallvec::SmallVec; #[test] fn rendering_mask_sanity() { assert_eq!( - RenderXXLayersXX::default().num_layers(), + RenderLayers::default().num_layers(), 1, "default layer contains only one layer" ); assert!( - RenderXXLayersXX::default().contains(DEFAULT_RENDER_LAYER), + RenderLayers::default().contains(DEFAULT_RENDER_LAYER), "default layer contains default" ); assert_eq!( - RenderXXLayersXX::from(RenderLayer(1)).num_layers(), + RenderLayers::from(RenderLayer(1)).num_layers(), 1, "from contains 1 layer" ); assert!( - RenderXXLayersXX::from(RenderLayer(1)).contains(RenderLayer(1)), + RenderLayers::from(RenderLayer(1)).contains(RenderLayer(1)), "contains is accurate" ); assert!( - !RenderXXLayersXX::from(RenderLayer(1)).contains(RenderLayer(2)), + !RenderLayers::from(RenderLayer(1)).contains(RenderLayer(2)), "contains fails when expected" ); assert_eq!( - RenderXXLayersXX::from(RenderLayer(0)).add(1).layers[0], + RenderLayers::from(RenderLayer(0)).add(1).layers[0], 3, "layer 0 + 1 is mask 3" ); assert_eq!( - RenderXXLayersXX::from(RenderLayer(0)) + RenderLayers::from(RenderLayer(0)) .add(1) .remove(0) .layers[0], @@ -623,40 +624,40 @@ mod rendering_mask_tests { "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderXXLayersXX::from(RenderLayer(1)) - .intersects(&RenderXXLayersXX::from(RenderLayer(1))), + RenderLayers::from(RenderLayer(1)) + .intersects(&RenderLayers::from(RenderLayer(1))), "layers match like layers" ); assert!( - RenderXXLayersXX::from(RenderLayer(0)).intersects(&RenderXXLayersXX { + RenderLayers::from(RenderLayer(0)).intersects(&RenderLayers { layers: SmallVec::from_slice(&[1]) }), "a layer of 0 means the mask is just 1 bit" ); assert!( - RenderXXLayersXX::from(RenderLayer(0)) + RenderLayers::from(RenderLayer(0)) .add(3) - .intersects(&RenderXXLayersXX::from(RenderLayer(3))), + .intersects(&RenderLayers::from(RenderLayer(3))), "a mask will match another mask containing any similar layers" ); assert!( - RenderXXLayersXX::default().intersects(&RenderXXLayersXX::default()), + RenderLayers::default().intersects(&RenderLayers::default()), "default masks match each other" ); assert!( - !RenderXXLayersXX::from(RenderLayer(0)) - .intersects(&RenderXXLayersXX::from(RenderLayer(1))), + !RenderLayers::from(RenderLayer(0)) + .intersects(&RenderLayers::from(RenderLayer(1))), "masks with differing layers do not match" ); assert!( - !RenderXXLayersXX::empty().intersects(&RenderXXLayersXX::empty()), + !RenderLayers::empty().intersects(&RenderLayers::empty()), "empty masks don't match" ); assert_eq!( - RenderXXLayersXX::from_layers(&[0, 2, 16, 30]) + RenderLayers::from_layers(&[0, 2, 16, 30]) .iter() .collect::>(), vec![ @@ -668,13 +669,13 @@ mod rendering_mask_tests { "from and get_layers should roundtrip" ); assert_eq!( - format!("{:?}", RenderXXLayersXX::from_layers(&[0, 1, 2, 3])).as_str(), - "RenderXXLayersXX([0, 1, 2, 3])", + format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(), + "RenderLayers([0, 1, 2, 3])", "Debug instance shows layers" ); assert_eq!( - RenderXXLayersXX::from_layers(&[0, 1, 2]), - >::from_iter(vec![0, 1, 2]), + RenderLayers::from_layers(&[0, 1, 2]), + >::from_iter(vec![0, 1, 2]), "from_layers and from_iter are equivalent" ); } diff --git a/crates/bevy_render/src/view/visibility/render_layers.rs b/crates/bevy_render/src/view/visibility/render_layers.rs deleted file mode 100644 index 2a0e5da7d89fc..0000000000000 --- a/crates/bevy_render/src/view/visibility/render_layers.rs +++ /dev/null @@ -1,186 +0,0 @@ -use bevy_ecs::prelude::{Component, ReflectComponent}; -use bevy_reflect::std_traits::ReflectDefault; -use bevy_reflect::Reflect; - -type LayerMask = u32; - -/// An identifier for a rendering layer. -pub type Layer = u8; - -/// Describes which rendering layers an entity belongs to. -/// -/// Cameras with this component will only render entities with intersecting -/// layers. -/// -/// There are 32 layers numbered `0` - [`TOTAL_LAYERS`](RenderLayers::TOTAL_LAYERS). Entities may -/// belong to one or more layers, or no layer at all. -/// -/// The [`Default`] instance of `RenderLayers` contains layer `0`, the first layer. -/// -/// An entity with this component without any layers is invisible. -/// -/// Entities without this component belong to layer `0`. -#[derive(Component, Copy, Clone, Reflect, PartialEq, Eq, PartialOrd, Ord)] -#[reflect(Component, Default, PartialEq)] -pub struct RenderLayers(LayerMask); - -impl std::fmt::Debug for RenderLayers { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("RenderLayers") - .field(&self.iter().collect::>()) - .finish() - } -} - -impl FromIterator for RenderLayers { - fn from_iter>(i: T) -> Self { - i.into_iter().fold(Self::none(), |mask, g| mask.with(g)) - } -} - -impl Default for RenderLayers { - /// By default, this structure includes layer `0`, which represents the first layer. - fn default() -> Self { - RenderLayers::layer(0) - } -} - -impl RenderLayers { - /// The total number of layers supported. - pub const TOTAL_LAYERS: usize = std::mem::size_of::() * 8; - - /// Create a new `RenderLayers` belonging to the given layer. - pub const fn layer(n: Layer) -> Self { - RenderLayers(0).with(n) - } - - /// Create a new `RenderLayers` that belongs to all layers. - pub const fn all() -> Self { - RenderLayers(u32::MAX) - } - - /// Create a new `RenderLayers` that belongs to no layers. - pub const fn none() -> Self { - RenderLayers(0) - } - - /// Create a `RenderLayers` from a list of layers. - pub fn from_layers(layers: &[Layer]) -> Self { - layers.iter().copied().collect() - } - - /// Add the given layer. - /// - /// This may be called multiple times to allow an entity to belong - /// to multiple rendering layers. The maximum layer is `TOTAL_LAYERS - 1`. - /// - /// # Panics - /// Panics when called with a layer greater than `TOTAL_LAYERS - 1`. - #[must_use] - pub const fn with(mut self, layer: Layer) -> Self { - assert!((layer as usize) < Self::TOTAL_LAYERS); - self.0 |= 1 << layer; - self - } - - /// Removes the given rendering layer. - /// - /// # Panics - /// Panics when called with a layer greater than `TOTAL_LAYERS - 1`. - #[must_use] - pub const fn without(mut self, layer: Layer) -> Self { - assert!((layer as usize) < Self::TOTAL_LAYERS); - self.0 &= !(1 << layer); - self - } - - /// Get an iterator of the layers. - pub fn iter(&self) -> impl Iterator { - let total: Layer = std::convert::TryInto::try_into(Self::TOTAL_LAYERS).unwrap(); - let mask = *self; - (0..total).filter(move |g| RenderLayers::layer(*g).intersects(&mask)) - } - - /// Determine if a `RenderLayers` intersects another. - /// - /// `RenderLayers`s intersect if they share any common layers. - /// - /// A `RenderLayers` with no layers will not match any other - /// `RenderLayers`, even another with no layers. - pub fn intersects(&self, other: &RenderLayers) -> bool { - (self.0 & other.0) > 0 - } - - /// get the bitmask representation of the contained layers - pub fn bits(&self) -> u32 { - self.0 - } -} - -#[cfg(test)] -mod rendering_mask_tests { - use super::{Layer, RenderLayers}; - - #[test] - fn rendering_mask_sanity() { - assert_eq!( - RenderLayers::TOTAL_LAYERS, - 32, - "total layers is what we think it is" - ); - assert_eq!(RenderLayers::layer(0).0, 1, "layer 0 is mask 1"); - assert_eq!(RenderLayers::layer(1).0, 2, "layer 1 is mask 2"); - assert_eq!(RenderLayers::layer(0).with(1).0, 3, "layer 0 + 1 is mask 3"); - assert_eq!( - RenderLayers::layer(0).with(1).without(0).0, - 2, - "layer 0 + 1 - 0 is mask 2" - ); - assert!( - RenderLayers::layer(1).intersects(&RenderLayers::layer(1)), - "layers match like layers" - ); - assert!( - RenderLayers::layer(0).intersects(&RenderLayers(1)), - "a layer of 0 means the mask is just 1 bit" - ); - - assert!( - RenderLayers::layer(0) - .with(3) - .intersects(&RenderLayers::layer(3)), - "a mask will match another mask containing any similar layers" - ); - - assert!( - RenderLayers::default().intersects(&RenderLayers::default()), - "default masks match each other" - ); - - assert!( - !RenderLayers::layer(0).intersects(&RenderLayers::layer(1)), - "masks with differing layers do not match" - ); - assert!( - !RenderLayers(0).intersects(&RenderLayers(0)), - "empty masks don't match" - ); - assert_eq!( - RenderLayers::from_layers(&[0, 2, 16, 30]) - .iter() - .collect::>(), - vec![0, 2, 16, 30], - "from_layers and get_layers should roundtrip" - ); - assert_eq!( - format!("{:?}", RenderLayers::from_layers(&[0, 1, 2, 3])).as_str(), - "RenderLayers([0, 1, 2, 3])", - "Debug instance shows layers" - ); - assert_eq!( - RenderLayers::from_layers(&[0, 1, 2]), - >::from_iter(vec![0, 1, 2]), - "from_layers and from_iter are equivalent" - ); - } -} diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 73eb81a37532f..75e4827d4081f 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -19,11 +19,11 @@ const RES_WIDTH: u32 = 160; /// In-game resolution height. const RES_HEIGHT: u32 = 90; -/// Default render groups for pixel-perfect rendering. +/// Default render layer for pixel-perfect rendering. /// You can skip adding this component, as this is the default. const PIXEL_PERFECT_LAYER: RenderLayer = RenderLayer(0); -/// Render groups for high-resolution rendering. +/// Render layer for high-resolution rendering. const HIGH_RES_LAYER: RenderLayer = RenderLayer(1); fn main() { diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index f9bd9a4592b2f..07080628b0603 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -8,7 +8,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::{RenderGroups, RenderLayer, RenderXXLayersXX}, + view::{RenderGroups, RenderLayer, RenderLayers}, }, }; @@ -94,7 +94,7 @@ fn setup( transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }, - RenderGroups::from(RenderXXLayersXX::from_layers(&[0, 1])), + RenderGroups::from(RenderLayers::from_layers(&[0, 1])), )); commands.spawn(( From 13994a3cad46b628f011d5543bcd68ad1972c63c Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 17:36:17 -0500 Subject: [PATCH 18/54] cleanup --- .../visibility/propagate_render_groups.rs | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index d16c5733ee926..7ad59cee40a85 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -139,6 +139,35 @@ use bevy_hierarchy::{Children, Parent}; use bevy_utils::error_once; use bevy_utils::tracing::warn; +/// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on +/// entities. +#[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] +pub struct PropagateRenderGroupsSet; + +pub(crate) struct PropagateRenderGroupsPlugin; + +impl Plugin for PropagateRenderGroupsPlugin { + fn build(&self, app: &mut App) { + app.init_resource::() + .add_systems( + PostUpdate, + ( + clean_propagators, + propagate_updated_propagators, + propagate_to_new_children, + handle_orphaned_nonpropagators, + handle_lost_propagator, + handle_new_children_nonpropagator, + handle_new_parent_nonpropagator, + apply_deferred, + handle_modified_rendergroups, //does not have deferred commands + ) + .chain() + .in_set(PropagateRenderGroupsSet), + ); + } +} + /// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. /// /// Entities with this component will ignore [`RenderGroups`] propagated by parents. @@ -302,40 +331,13 @@ pub fn derive_render_groups<'a>( } } -/// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on -/// entities. -#[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] -pub struct PropagateRenderGroupsSet; - -pub(crate) struct PropagateRenderGroupsPlugin; - -impl Plugin for PropagateRenderGroupsPlugin { - fn build(&self, app: &mut App) { - app.init_resource::() - .add_systems( - PostUpdate, - ( - clean_propagators, - propagate_updated_propagators, - propagate_to_new_children, - handle_orphaned_nonpropagators, - handle_lost_propagator, - handle_new_children_nonpropagator, - handle_new_parent_nonpropagator, - apply_deferred, - handle_modified_rendergroups, //does not have deferred commands - ) - .chain() - .in_set(PropagateRenderGroupsSet), - ); - } -} - #[derive(Resource, Default)] struct PropagateRenderGroupsEntityCache { - /// Buffered to absorb spurious allocations during traversals. + /// Buffered to absorb spurious allocations of propagated values during traversals. saved: RenderGroups, /// Updated entities. + /// + /// This is cleared at the start of [`PropagateRenderGroupsSet`]. entities: EntityHashSet, } From bc289e14a01361c2442641746d9e596fca7816a6 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 17:45:12 -0500 Subject: [PATCH 19/54] docs --- .../visibility/propagate_render_groups.rs | 33 +++++++++++-------- .../src/view/visibility/render_groups.rs | 4 +-- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 7ad59cee40a85..f73fe6d93989f 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -19,19 +19,19 @@ described below. Dimensions: - InheritedRenderGroups and PropagateRenderGroups should not exist on the same entity -- Add/remove/change RenderGroups -- Add/remove/change CameraView -- Add/remove/change PropagateRenderGroups -- Add/remove Camera -- Gain parent with PropagateRenderGroups -- Gain parent without PropagateRenderGroups - - Entity with InheritedRenderGroups - - Entity without InheritedRenderGroups -- Become un-parented +- RenderGroups can be added/changed/removed +- CameraView can be added/changed/removed +- PropagateRenderGroups can be added/changed/removed +- Camera can be added/removed (changes don't matter) +- Entities can gain a parent with PropagateRenderGroups +- Entities can gain a parent without PropagateRenderGroups + - The entity may or may not have InheritedRenderGroups already + - The parent may or may not have InheritedRenderGroups already +- Entities can become un-parented Actions: -- Add/remove/change InheritedRenderGroups - - When updating an entity, always compute a fresh InheritedRenderGroups::computed value. +- This algorithm can: add/remove/change InheritedRenderGroups + - When updating an entity, we always compute a fresh InheritedRenderGroups::computed value. ## Propagation algorithm @@ -120,12 +120,15 @@ its InheritedRenderGroups::computed field. Skip already-updated entities. ## Performance -The base-line performance cost of this algorithm comes from iterating in order to detect changes. +The base-line performance cost of this algorithm comes from detecting changes, which requires iterating queries. - All entities with `Children` and `PropagateRenderGroups` are iterated twice (can potentially be reduced to once). +- All entities with `Children` and `InheritedRenderGroups` are iterated once. +- All entities with `Parent` and `InheritedRenderGroups` are iterated once. +- All entities with `RenderGroups` and `InheritedRenderGroups` are iterated once. - `RemovedComponents` is iterated twice. - `RemovedComponents` is iterated once. - `RemovedComponents` is iterated once. -- `RemovedComponents` is iterated once. +- `RemovedComponents` is iterated once. */ use crate::view::*; @@ -141,6 +144,8 @@ use bevy_utils::tracing::warn; /// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on /// entities. +/// +/// Runs in [`PostUpdate`] in the [`VisibilityPropagate`] set. #[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] pub struct PropagateRenderGroupsSet; @@ -245,7 +250,7 @@ impl PropagateRenderGroups { /// /// See [`PropagateRenderGroups`]. /// -/// This is automatically updated in [`PostUpdate`] in the [`VisibilityPropagate`] set. +/// This is updated in [`PostUpdate`] in the [`PropagateRenderGroupsSet`]. /// The component will be automatically added or removed depending on if it is needed. /// /// ### Merge details diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 81651ace3c1a2..c1a637f22c86d 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -222,7 +222,7 @@ impl Default for RenderLayers { /// There are two kinds of render groups: /// - [`RenderLayers`]: These are grouping categories that many cameras can view (see [`CameraView`]). /// - *Camera entity*: This is a specific camera that the entity is affiliated with. This is especially -/// useful for UI in combination with [`PropagateRenderGroups`]. +/// useful for UI in combination with [`PropagateRenderGroups`]. /// /// An entity can be a member of multiple [`RenderLayers`] in addition to having a camera affiliation. /// @@ -486,7 +486,7 @@ impl<'a> RenderGroupsRef<'a> { /// view the [`DEFAULT_RENDER_LAYER`] layer. /// /// A camera without the `CameraView` component will see the [`DEFAULT_RENDER_LAYER`] -/// layer, in addition to relevant [`RenderGroups`] camera affiliations. +/// layer, in addition to any affiliated entities. /// /// A default `CameraView` will include the [`DEFAULT_RENDER_LAYER`]. #[derive(Component, Debug, Clone, PartialEq, Reflect)] From 7e8f07378639d726b30c28804ee21f45083c081f Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 18 Mar 2024 18:27:12 -0500 Subject: [PATCH 20/54] fmt --- .../src/view/visibility/render_groups.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index c1a637f22c86d..802b40d7ebbff 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -440,14 +440,13 @@ impl<'a> RenderGroupsRef<'a> { /// The ref will be in an invalid state after this is called. pub(crate) fn reclaim(&mut self, other: &mut RenderGroups) { match self { - Self::Val(groups) => - { + Self::Val(groups) => { if !groups.is_allocated() || other.is_allocated() { return; } std::mem::swap(groups, other); } - _ => () + _ => (), } } } @@ -466,7 +465,9 @@ impl<'a> RenderGroupsRef<'a> { /// Panices if in state [`Self::None`]. pub fn get(&self) -> &RenderGroups { match self { - Self::None => { panic!("RenderGroupsRef cannot be dereferenced when empty"); } + Self::None => { + panic!("RenderGroupsRef cannot be dereferenced when empty"); + } Self::Ref(groups) => groups, Self::Val(groups) => &groups, } @@ -616,16 +617,12 @@ mod rendering_mask_tests { "layer 0 + 1 is mask 3" ); assert_eq!( - RenderLayers::from(RenderLayer(0)) - .add(1) - .remove(0) - .layers[0], + RenderLayers::from(RenderLayer(0)).add(1).remove(0).layers[0], 2, "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderLayers::from(RenderLayer(1)) - .intersects(&RenderLayers::from(RenderLayer(1))), + RenderLayers::from(RenderLayer(1)).intersects(&RenderLayers::from(RenderLayer(1))), "layers match like layers" ); assert!( @@ -648,8 +645,7 @@ mod rendering_mask_tests { ); assert!( - !RenderLayers::from(RenderLayer(0)) - .intersects(&RenderLayers::from(RenderLayer(1))), + !RenderLayers::from(RenderLayer(0)).intersects(&RenderLayers::from(RenderLayer(1))), "masks with differing layers do not match" ); assert!( From ab44847c78ef035c1b35bc27f71e03749c3f56ad Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 15:07:20 -0500 Subject: [PATCH 21/54] cleanup --- crates/bevy_render/src/view/visibility/mod.rs | 9 ++------- .../src/view/visibility/render_groups.rs | 16 ++++++++++------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 25371140a826b..17b8f52bda9de 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -413,7 +413,7 @@ pub fn check_visibility( inherited_visibility, mut view_visibility, maybe_groups, - maybe_inherited_groups, + maybe_inherited, maybe_model_aabb, transform, no_frustum_culling, @@ -428,12 +428,7 @@ pub fn check_visibility( // Check render groups. // - If there is no RenderGroups in the entity, use the *default* value because the // entity is in the DEFAULT_RENDER_LAYER. - let default_render_groups = RenderGroups::default(); - let entity_groups = maybe_inherited_groups - .map(|i| &i.computed) - .or(maybe_groups) - .unwrap_or(&default_render_groups); - if !camera_view.entity_is_visible(camera_entity, entity_groups) { + if !camera_view.entity_is_visible(camera_entity, &derive_render_groups(maybe_inherited, maybe_groups)) { return; } diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 802b40d7ebbff..b56552d06473c 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -435,18 +435,22 @@ pub enum RenderGroupsRef<'a> { } impl<'a> RenderGroupsRef<'a> { - /// Passes [`RenderLists`] to `other` if `self` is on the heap. + /// Moves `self` into `other` if `self` is on the heap and `other` is not. /// - /// The ref will be in an invalid state after this is called. - pub(crate) fn reclaim(&mut self, other: &mut RenderGroups) { + /// Sets self to [`Self::None`]. + /// + /// Returns `true` if reclamation occurred. + pub(crate) fn reclaim(&mut self, other: &mut RenderGroups) -> bool { match self { Self::Val(groups) => { if !groups.is_allocated() || other.is_allocated() { - return; + return false; } - std::mem::swap(groups, other); + *other = std::mem::take(groups); + *self = Self::None; + true } - _ => (), + _ => false, } } } From 1a7a4a229f37eb2db03d0d6ba59e7f327b7ed651 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 16:10:08 -0500 Subject: [PATCH 22/54] clean up rendering pt.1 --- crates/bevy_pbr/src/light/mod.rs | 40 ++++++++++--------- crates/bevy_pbr/src/render/light.rs | 2 - crates/bevy_render/src/view/mod.rs | 4 -- .../visibility/propagate_render_groups.rs | 16 ++++++++ .../src/view/visibility/render_groups.rs | 22 ++++++++++ 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 5e405c0cf8e32..6c12b31569982 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -14,8 +14,8 @@ use bevy_render::{ render_resource::BufferBindingType, renderer::RenderDevice, view::{ - derive_render_groups, extract_render_groups, InheritedRenderGroups, InheritedVisibility, - RenderGroups, ViewVisibility, VisibleEntities, + CameraView, derive_render_groups, derive_render_groups_ptr, extract_camera_view, InheritedRenderGroups, + InheritedVisibility, RenderGroups, RenderGroupsPtr, ViewVisibility, VisibleEntities, }, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -1006,7 +1006,6 @@ pub(crate) fn directional_light_order( .then_with(|| entity_1.cmp(entity_2)) // stable } -#[derive(Clone, Copy)] // data required for assigning lights to clusters pub(crate) struct PointLightAssignmentData { entity: Entity, @@ -1014,7 +1013,7 @@ pub(crate) struct PointLightAssignmentData { range: f32, shadows_enabled: bool, spot_light_angle: Option, - render_groups: u32, + render_groups: RenderGroupsPtr, } impl PointLightAssignmentData { @@ -1026,6 +1025,11 @@ impl PointLightAssignmentData { } } +/// SAFETY: PointLightAssignmentData is only used in `assign_lights_to_clusters`, where it is not reused +/// between system calls. +unsafe impl Send for PointLightAssignmentData { } +unsafe impl Sync for PointLightAssignmentData { } + #[derive(Resource, Default)] pub struct GlobalVisiblePointLights { entities: HashSet, @@ -1045,7 +1049,7 @@ impl GlobalVisiblePointLights { // NOTE: Run this before update_point_light_frusta! #[allow(clippy::too_many_arguments)] -pub(crate) fn assign_lights_to_clusters( +pub(crate) fn assign_lights_to_clusters<'w, 's>( mut commands: Commands, mut global_lights: ResMut, mut views: Query<( @@ -1055,8 +1059,7 @@ pub(crate) fn assign_lights_to_clusters( &Frustum, &ClusterConfig, &mut Clusters, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&CameraView>, Option<&mut VisiblePointLights>, )>, point_lights_query: Query<( @@ -1092,14 +1095,14 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, point_light, _maybe_groups, _maybe_inherited, _visibility)| { + |(entity, transform, point_light, maybe_groups, maybe_inherited, _visibility)| { PointLightAssignmentData { entity, transform: GlobalTransform::from_translation(transform.translation()), shadows_enabled: point_light.shadows_enabled, range: point_light.range, spot_light_angle: None, - render_groups: 0u32, //extract_render_groups(maybe_inherited, maybe_groups), + render_groups: derive_render_groups_ptr(maybe_inherited, maybe_groups), } }, ), @@ -1109,14 +1112,14 @@ pub(crate) fn assign_lights_to_clusters( .iter() .filter(|(.., visibility)| visibility.get()) .map( - |(entity, transform, spot_light, _maybe_groups, _maybe_inherited, _visibility)| { + |(entity, transform, spot_light, maybe_groups, maybe_inherited, _visibility)| { PointLightAssignmentData { entity, transform: *transform, shadows_enabled: spot_light.shadows_enabled, range: spot_light.range, spot_light_angle: Some(spot_light.outer_angle), - render_groups: 0u32, //extract_render_groups(maybe_inherited, maybe_groups), + render_groups: derive_render_groups_ptr(maybe_inherited, maybe_groups), } }, ), @@ -1147,7 +1150,7 @@ pub(crate) fn assign_lights_to_clusters( // check each light against each view's frustum, keep only those that affect at least one of our views let frusta: Vec<_> = views .iter() - .map(|(_, _, _, frustum, _, _, _, _, _)| *frustum) + .map(|(_, _, _, frustum, _, _, _, _)| *frustum) .collect(); let mut lights_in_view_count = 0; lights.retain(|light| { @@ -1186,12 +1189,11 @@ pub(crate) fn assign_lights_to_clusters( frustum, config, clusters, - maybe_groups, - maybe_inherited, + maybe_view, mut visible_lights, ) in &mut views { - let view_groups = extract_render_groups(maybe_inherited, maybe_groups); + let view_groups = extract_camera_view(view_entity, maybe_view); let clusters = clusters.into_inner(); @@ -1414,9 +1416,11 @@ pub(crate) fn assign_lights_to_clusters( let mut update_from_light_intersections = |visible_lights: &mut Vec| { for light in &lights { - // check if the light layers overlap the view layers - if !view_groups.intersects(&RenderGroups::default()) { - //&light.render_groups) { + // check if the light groups overlap the view groups + // SAFETY: `lights` is cleared at the start of this system call, and is populated from + // immutable queries. + let light_rendergroups = unsafe { light.render_groups.get() }; + if !view_groups.intersects(&light_rendergroups) { continue; } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 7ec7c7ffe66ea..49f2923a86aaf 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -176,7 +176,6 @@ pub struct GpuDirectionalLight { num_cascades: u32, cascades_overlap_proportion: f32, depth_texture_base_index: u32, - render_groups: u32, } // NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl! @@ -922,7 +921,6 @@ pub fn prepare_lights( num_cascades: num_cascades as u32, cascades_overlap_proportion: light.cascade_shadow_config.overlap_proportion, depth_texture_base_index: num_directional_cascades_enabled as u32, - render_groups: 0u32, //light.render_groups.bits(), }; if index < directional_shadow_enabled_count { num_directional_cascades_enabled += num_cascades; diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 4f0e5945d3d64..835adc0fe5f46 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -179,7 +179,6 @@ pub struct ViewUniform { frustum: [Vec4; 6], color_grading: ColorGrading, mip_bias: f32, - render_groups: u32, } #[derive(Resource, Default)] @@ -377,7 +376,6 @@ pub fn prepare_view_uniforms( Option<&Frustum>, Option<&TemporalJitter>, Option<&MipBias>, - Option<&ExtractedRenderGroups>, )>, ) { let view_iter = views.iter(); @@ -396,7 +394,6 @@ pub fn prepare_view_uniforms( frustum, temporal_jitter, mip_bias, - _maybe_groups, ) in &views { let viewport = extracted_view.viewport.as_vec4(); @@ -441,7 +438,6 @@ pub fn prepare_view_uniforms( frustum, color_grading: extracted_view.color_grading, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, - render_groups: 0u32, //maybe_groups.cloned().unwrap_or(RenderGroups::default()), }), }; diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index f73fe6d93989f..e14a670e4d031 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -336,6 +336,22 @@ pub fn derive_render_groups<'a>( } } +/// Derives a [`RenderGroupsPtr`] from an optional [`InheritedRenderGroups`] and [`RenderGroups`]. +/// +/// See [`derive_render_groups`]. +pub fn derive_render_groups_ptr( + inherited: Option<&InheritedRenderGroups>, + render_groups: Option<&RenderGroups>, +) -> RenderGroupsPtr { + if let Some(inherited) = inherited { + RenderGroupsPtr::Ptr(&inherited.computed) + } else if let Some(render_groups) = render_groups { + RenderGroupsPtr::Ptr(render_groups) + } else { + RenderGroupsPtr::Val(RenderGroups::default()) + } +} + #[derive(Resource, Default)] struct PropagateRenderGroupsEntityCache { /// Buffered to absorb spurious allocations of propagated values during traversals. diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index b56552d06473c..d385a280c9b32 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -478,6 +478,28 @@ impl<'a> RenderGroupsRef<'a> { } } +/// Stores a [`RenderGroups`] pointer or value. +/// +/// Useful as an alternative to [`RenderGroupsRef`] when you can't store a reference, for example within a [`Local`] +/// that buffers a cache that is rewritten every system call. +pub enum RenderGroupsPtr { + Ptr(*const RenderGroups), + Val(RenderGroups), +} + +impl RenderGroupsPtr { + /// Gets a reference to the internal [`RenderGroups`]. + /// + /// Safety must be established by the user. + pub unsafe fn get(&self) -> &RenderGroups { + match self { + // SAFETY: Safety is established by the caller. + Self::Ptr(groups) => unsafe { groups.as_ref().unwrap() }, + Self::Val(groups) => &groups, + } + } +} + /// Component on camera entities that controls which [`RenderLayers`] are visible to /// the camera. /// From cc7ac691169a2c85517f9e7b4345a963c083e513 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 16:21:48 -0500 Subject: [PATCH 23/54] add comment --- crates/bevy_pbr/src/light/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 6c12b31569982..7d7e127a1faed 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1942,6 +1942,9 @@ pub fn check_light_mesh_visibility( continue; } + // Note: A visible entity may have a camera on it, but in this case we ignore the camera and + // treat it as a normal entity. The CameraView component controls what the camera can see, while + // RenderGroups on the camera entity controls who can see the camera entity. if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { continue; } From dc8c7ba2764bf80c814777ffed2c8941dd6a2a22 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 18:18:54 -0500 Subject: [PATCH 24/54] refactor PBR lighting so light filtering occurs on the CPU --- crates/bevy_pbr/src/render/light.rs | 29 ++++++++++++++----- .../bevy_pbr/src/render/mesh_view_types.wgsl | 2 +- crates/bevy_pbr/src/render/pbr_functions.wgsl | 6 ++-- crates/bevy_render/src/view/view.wgsl | 1 - 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 49f2923a86aaf..4c345a20d797c 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -14,10 +14,7 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{ - extract_render_groups, ExtractedRenderGroups, ExtractedView, InheritedRenderGroups, - RenderGroups, ViewVisibility, VisibleEntities, - }, + view::{extract_render_groups, ExtractedRenderGroups, ExtractedView, InheritedRenderGroups, RenderGroups, ViewVisibility, VisibleEntities,}, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -167,6 +164,7 @@ pub struct GpuDirectionalCascade { #[derive(Copy, Clone, ShaderType, Default, Debug)] pub struct GpuDirectionalLight { + skip: u32, cascades: [GpuDirectionalCascade; MAX_CASCADES_PER_LIGHT], color: Vec4, dir_to_light: Vec3, @@ -688,7 +686,7 @@ pub fn prepare_lights( mut global_light_meta: ResMut, mut light_meta: ResMut, views: Query< - (Entity, &ExtractedView, &ExtractedClusterConfig), + (Entity, &ExtractedView, &ExtractedClusterConfig, &ExtractedRenderGroups), With>, >, ambient_light: Res, @@ -908,6 +906,8 @@ pub fn prepare_lights( .len() .min(MAX_CASCADES_PER_LIGHT); gpu_directional_lights[index] = GpuDirectionalLight { + // Set to true later when necessary. + skip: 0u32, // Filled in later. cascades: [GpuDirectionalCascade::default(); MAX_CASCADES_PER_LIGHT], // premultiply color by illuminance @@ -933,7 +933,7 @@ pub fn prepare_lights( .write_buffer(&render_device, &render_queue); // set up light data for each view - for (entity, extracted_view, clusters) in &views { + for (entity, extracted_view, clusters, extracted_render_groups) in &views { let point_light_depth_texture = texture_cache.get( &render_device, TextureDescriptor { @@ -1133,8 +1133,21 @@ pub fn prepare_lights( for (light_index, &(light_entity, light)) in directional_lights .iter() .enumerate() - .take(directional_shadow_enabled_count) + .take(MAX_DIRECTIONAL_LIGHTS) { + let gpu_light = &mut gpu_lights.directional_lights[light_index]; + + // Check if the light intersects with the view. + if extracted_render_groups.intersects(&light.render_groups) { + gpu_light.skip = 1u32; + continue; + } + + // Only deal with cascades when shadows are enabled. + if (gpu_light.flags & DirectionalLightFlags::SHADOWS_ENABLED.bits()) == 0u32 { + continue; + } + let cascades = light .cascades .get(&entity) @@ -1152,7 +1165,7 @@ pub fn prepare_lights( .zip(&light.cascade_shadow_config.bounds) .enumerate() { - gpu_lights.directional_lights[light_index].cascades[cascade_index] = + gpu_light.cascades[cascade_index] = GpuDirectionalCascade { view_projection: cascade.view_projection, texel_size: cascade.texel_size, diff --git a/crates/bevy_pbr/src/render/mesh_view_types.wgsl b/crates/bevy_pbr/src/render/mesh_view_types.wgsl index 1d9a5b59b44e6..b46e5edf2da08 100644 --- a/crates/bevy_pbr/src/render/mesh_view_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_view_types.wgsl @@ -23,6 +23,7 @@ struct DirectionalCascade { } struct DirectionalLight { + skip: u32, cascades: array, color: vec4, direction_to_light: vec3, @@ -33,7 +34,6 @@ struct DirectionalLight { num_cascades: u32, cascades_overlap_proportion: f32, depth_texture_base_index: u32, - render_groups: u32, }; const DIRECTIONAL_LIGHT_FLAGS_SHADOWS_ENABLED_BIT: u32 = 1u; diff --git a/crates/bevy_pbr/src/render/pbr_functions.wgsl b/crates/bevy_pbr/src/render/pbr_functions.wgsl index a2f0f7ac87f71..f28422c8355aa 100644 --- a/crates/bevy_pbr/src/render/pbr_functions.wgsl +++ b/crates/bevy_pbr/src/render/pbr_functions.wgsl @@ -277,10 +277,10 @@ fn apply_pbr_lighting( // directional lights (direct) let n_directional_lights = view_bindings::lights.n_directional_lights; for (var i: u32 = 0u; i < n_directional_lights; i = i + 1u) { - // check the directional light render layers intersect the view render layers - // note this is not necessary for point and spot lights, as the relevant lights are filtered in `assign_lights_to_clusters` + // check if this light should be skipped, which occurs if this light does not intersect with the view + // note point and spot lights aren't skippable, as the relevant lights are filtered in `assign_lights_to_clusters` let light = &view_bindings::lights.directional_lights[i]; - if ((*light).render_groups & view_bindings::view.render_groups) == 0u { + if (*light).skip != 0u { continue; } diff --git a/crates/bevy_render/src/view/view.wgsl b/crates/bevy_render/src/view/view.wgsl index 59218996f4db0..0fe74860dbe60 100644 --- a/crates/bevy_render/src/view/view.wgsl +++ b/crates/bevy_render/src/view/view.wgsl @@ -22,5 +22,4 @@ struct View { frustum: array, 6>, color_grading: ColorGrading, mip_bias: f32, - render_group: u32, }; From 187b8c08ee572e449bbe8a614f138cec427ae4ff Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 18:28:51 -0500 Subject: [PATCH 25/54] fmt --- crates/bevy_pbr/src/light/mod.rs | 9 ++++---- crates/bevy_pbr/src/render/light.rs | 23 ++++++++++++------- crates/bevy_render/src/view/mod.rs | 10 +------- crates/bevy_render/src/view/visibility/mod.rs | 5 +++- .../src/view/visibility/render_groups.rs | 2 +- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 7d7e127a1faed..d56f729ab1345 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -14,8 +14,9 @@ use bevy_render::{ render_resource::BufferBindingType, renderer::RenderDevice, view::{ - CameraView, derive_render_groups, derive_render_groups_ptr, extract_camera_view, InheritedRenderGroups, - InheritedVisibility, RenderGroups, RenderGroupsPtr, ViewVisibility, VisibleEntities, + derive_render_groups, derive_render_groups_ptr, extract_camera_view, CameraView, + InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, ViewVisibility, + VisibleEntities, }, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -1027,8 +1028,8 @@ impl PointLightAssignmentData { /// SAFETY: PointLightAssignmentData is only used in `assign_lights_to_clusters`, where it is not reused /// between system calls. -unsafe impl Send for PointLightAssignmentData { } -unsafe impl Sync for PointLightAssignmentData { } +unsafe impl Send for PointLightAssignmentData {} +unsafe impl Sync for PointLightAssignmentData {} #[derive(Resource, Default)] pub struct GlobalVisiblePointLights { diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 4c345a20d797c..b57badff64a25 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -14,7 +14,10 @@ use bevy_render::{ render_resource::*, renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, - view::{extract_render_groups, ExtractedRenderGroups, ExtractedView, InheritedRenderGroups, RenderGroups, ViewVisibility, VisibleEntities,}, + view::{ + extract_render_groups, ExtractedRenderGroups, ExtractedView, InheritedRenderGroups, + RenderGroups, ViewVisibility, VisibleEntities, + }, Extract, }; use bevy_transform::{components::GlobalTransform, prelude::Transform}; @@ -686,7 +689,12 @@ pub fn prepare_lights( mut global_light_meta: ResMut, mut light_meta: ResMut, views: Query< - (Entity, &ExtractedView, &ExtractedClusterConfig, &ExtractedRenderGroups), + ( + Entity, + &ExtractedView, + &ExtractedClusterConfig, + &ExtractedRenderGroups, + ), With>, >, ambient_light: Res, @@ -1165,12 +1173,11 @@ pub fn prepare_lights( .zip(&light.cascade_shadow_config.bounds) .enumerate() { - gpu_light.cascades[cascade_index] = - GpuDirectionalCascade { - view_projection: cascade.view_projection, - texel_size: cascade.texel_size, - far_bound: *bound, - }; + gpu_light.cascades[cascade_index] = GpuDirectionalCascade { + view_projection: cascade.view_projection, + texel_size: cascade.texel_size, + far_bound: *bound, + }; let depth_texture_view = directional_light_depth_texture diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 835adc0fe5f46..616c9001c6f9f 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -387,15 +387,7 @@ pub fn prepare_view_uniforms( else { return; }; - for ( - entity, - extracted_camera, - extracted_view, - frustum, - temporal_jitter, - mip_bias, - ) in &views - { + for (entity, extracted_camera, extracted_view, frustum, temporal_jitter, mip_bias) in &views { let viewport = extracted_view.viewport.as_vec4(); let unjittered_projection = extracted_view.projection; let mut projection = unjittered_projection; diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 17b8f52bda9de..19157581d60c0 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -428,7 +428,10 @@ pub fn check_visibility( // Check render groups. // - If there is no RenderGroups in the entity, use the *default* value because the // entity is in the DEFAULT_RENDER_LAYER. - if !camera_view.entity_is_visible(camera_entity, &derive_render_groups(maybe_inherited, maybe_groups)) { + if !camera_view.entity_is_visible( + camera_entity, + &derive_render_groups(maybe_inherited, maybe_groups), + ) { return; } diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index d385a280c9b32..9330ff0a20457 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -438,7 +438,7 @@ impl<'a> RenderGroupsRef<'a> { /// Moves `self` into `other` if `self` is on the heap and `other` is not. /// /// Sets self to [`Self::None`]. - /// + /// /// Returns `true` if reclamation occurred. pub(crate) fn reclaim(&mut self, other: &mut RenderGroups) -> bool { match self { From 3a5ce6f15773bb95617b30948909f6d08dc9f171 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 18:33:33 -0500 Subject: [PATCH 26/54] review comments --- crates/bevy_render/src/view/visibility/render_groups.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 9330ff0a20457..6a0fe971efda7 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -251,6 +251,10 @@ entity.insert(groups); let mut groups = RenderGroups::from(0); groups.add(RenderLayer(1); entity.insert(groups); + +// Option 3: manual +let groups = RenderGroups::from(RenderLayers::from_layers(&[0, 1])); +entity.insert(groups); ``` /// /// Similarly, if an entity without [`RenderGroups`] inherits from an entity with [`PropagateRenderGroups`] that @@ -273,7 +277,7 @@ impl RenderGroups { } } - /// Makes a new `RenderGroups` with just a camera. + /// Makes a new `RenderGroups` with just a camera and no [`RenderLayers`]. pub fn new_with_camera(camera: Entity) -> Self { Self { layers: RenderLayers::empty(), From 1b6e31b82daa2534568150029436cfe4e29cfebc Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 18:46:45 -0500 Subject: [PATCH 27/54] lints --- .../visibility/propagate_render_groups.rs | 47 ++++++++++--------- .../src/view/visibility/render_groups.rs | 9 ++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index e14a670e4d031..2b807b215dea7 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -131,6 +131,8 @@ The base-line performance cost of this algorithm comes from detecting changes, w - `RemovedComponents` is iterated once. */ +#![allow(clippy::manual_map, clippy::collapsible_match)] + use crate::view::*; use crate::prelude::Camera; @@ -217,14 +219,14 @@ impl PropagateRenderGroups { Self::Camera => { if !is_camera { warn!("failed propagating PropagateRenderGroups::Camera, {entity} doesn't have a camera"); - RenderGroupsRef::Val(RenderGroups::empty()); + return RenderGroupsRef::Val(RenderGroups::empty()); }; RenderGroupsRef::Val(RenderGroups::new_with_camera(entity)) } Self::CameraWithView => { if !is_camera { warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); - RenderGroupsRef::Val(RenderGroups::empty()); + return RenderGroupsRef::Val(RenderGroups::empty()); }; let empty_camera_view = CameraView::empty(); let view = view.unwrap_or(&empty_camera_view); @@ -233,7 +235,7 @@ impl PropagateRenderGroups { // Reuse saved allocation. let mut temp = RenderGroups::default(); std::mem::swap(&mut temp, saved); - temp.set_from_parts(Some(entity), &view.layers()); + temp.set_from_parts(Some(entity), view.layers()); RenderGroupsRef::Val(temp) } else { // Allocate a new RenderGroups @@ -255,7 +257,7 @@ impl PropagateRenderGroups { /// /// ### Merge details /// -/// The merge direction is 'entity_rendergroups.merge(propagated_rendergroups)` +/// The merge direction is `entity_rendergroups.merge(propagated_rendergroups)` /// (see [`RenderGroups::merge`]). /// This means the entity's affiliated camera will be prioritized over the propagated affiliated camera. #[derive(Component, Debug, Clone)] @@ -380,20 +382,21 @@ impl PropagateRenderGroupsEntityCache { } } -/// Removes InheritedRenderGroups from entities with PropagateRenderGroups. +/// Removes `InheritedRenderGroups` from entities with `PropagateRenderGroups`. fn clean_propagators( mut commands: Commands, dirty_propagators: Query, With)>, ) { for dirty in dirty_propagators.iter() { - commands.get_entity(dirty).map(|mut e| { - e.remove::(); - }); + if let Some(mut entity) = commands.get_entity(dirty) { + entity.remove::(); + } } } /// Propagates propagation values that have changed. //todo: Detect if the propagated value has actually changed? Hard to expect this would matter in practice. +#[allow(clippy::too_many_arguments)] fn propagate_updated_propagators( mut commands: Commands, mut updated_entities: ResMut, @@ -677,7 +680,7 @@ fn apply_new_children_propagation( } } -/// Removes InheritedRenderGroups from orphaned branches of the hierarchy. +/// Removes `InheritedRenderGroups` from orphaned branches of the hierarchy. fn handle_orphaned_nonpropagators( mut commands: Commands, mut updated_entities: ResMut, @@ -714,9 +717,9 @@ fn apply_orphan_cleanup( maybe_children: Option<&Children>, ) { // Remove InheritedRenderGroups. - commands.get_entity(entity).map(|mut e| { - e.remove::(); - }); + if let Some(mut entity) = commands.get_entity(entity) { + entity.remove::(); + } // Mark as updated. updated_entities.insert(entity); @@ -741,7 +744,7 @@ fn apply_orphan_cleanup( } } -/// Handles entities that lost the PropagateRenderGroups component. +/// Handles entities that lost the `PropagateRenderGroups` component. fn handle_lost_propagator( mut commands: Commands, mut updated_entities: ResMut, @@ -951,7 +954,7 @@ fn apply_full_propagation_force_update( } } -/// Applies InheritedRenderGroups removal to entities for `handle_lost_propagator`, +/// Applies `InheritedRenderGroups` removal to entities for `handle_lost_propagator`, /// `handle_new_children_nonpropagator`, and `handle_new_parent_nonpropagator`. fn apply_full_propagation_force_remove( commands: &mut Commands, @@ -994,7 +997,7 @@ fn apply_full_propagation_force_remove( } } -/// Handles non-propagator entities with InheritedRenderGroups whose children changed. +/// Handles non-propagator entities with `InheritedRenderGroups` whose children changed. fn handle_new_children_nonpropagator( mut commands: Commands, mut updated_entities: ResMut, @@ -1089,12 +1092,12 @@ fn handle_new_children_nonpropagator( } } -/// Handles non-propagator entities with InheritedRenderGroups whose parents changed. -/// - Since handle_new_children_nonpropagator handles all cases where the parent has InheritedRenderGroups, this -/// system just needs to remove InheritedRenderGroups from non-updated entities and their non-updated descendents -/// that have InheritedRenderGroups (stopping at propagators and non-updated descendents without -/// InheritedRenderGroups). -/// - We skip non-updated entities whose parents are updated, because that implies the current InheritedRenderGroups +/// Handles non-propagator entities with `InheritedRenderGroups` whose parents changed. +/// - Since `handle_new_children_nonpropagator` handles all cases where the parent has `InheritedRenderGroups`, this +/// system just needs to remove `InheritedRenderGroups` from non-updated entities and their non-updated descendents +/// that have `InheritedRenderGroups` (stopping at propagators and non-updated descendents without +/// `InheritedRenderGroups`). +/// - We skip non-updated entities whose parents are updated, because that implies the current `InheritedRenderGroups` /// propagator is accurate. fn handle_new_parent_nonpropagator( mut commands: Commands, @@ -1151,7 +1154,7 @@ fn handle_new_parent_nonpropagator( } } -/// Handles added/removed/changed RenderGroups for entities with existing InheritedRenderGroups. +/// Handles added/removed/changed `RenderGroups` for entities with existing `InheritedRenderGroups`. fn handle_modified_rendergroups( mut updated_entities: ResMut, // Entities with InheritedRenderGroups that changed RenderGroups diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 6a0fe971efda7..79d9a5390ea42 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -130,7 +130,7 @@ impl RenderLayers { /// Iterates the internal render layers. pub fn iter(&self) -> impl Iterator + '_ { - self.layers.iter().copied().map(Self::iter_layers).flatten() + self.layers.iter().copied().flat_map(Self::iter_layers) } /// Returns `true` if the specified render layer is included in this `RenderLayers`. @@ -327,7 +327,7 @@ impl RenderGroups { self.camera = self.camera.or(other.camera); } - /// Copies `other` into `Self. + /// Copies `other` into `Self`. /// /// This is more efficient than cloning `other` if you want to reuse a `RenderGroups` /// that is potentially allocated. @@ -477,7 +477,7 @@ impl<'a> RenderGroupsRef<'a> { panic!("RenderGroupsRef cannot be dereferenced when empty"); } Self::Ref(groups) => groups, - Self::Val(groups) => &groups, + Self::Val(groups) => groups, } } } @@ -494,12 +494,13 @@ pub enum RenderGroupsPtr { impl RenderGroupsPtr { /// Gets a reference to the internal [`RenderGroups`]. /// + /// # Safety /// Safety must be established by the user. pub unsafe fn get(&self) -> &RenderGroups { match self { // SAFETY: Safety is established by the caller. Self::Ptr(groups) => unsafe { groups.as_ref().unwrap() }, - Self::Val(groups) => &groups, + Self::Val(groups) => groups, } } } From dab2d88a54a702543166c60eb725aa2a365f2224 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 18:52:16 -0500 Subject: [PATCH 28/54] lints --- crates/bevy_render/src/view/visibility/mod.rs | 82 ++++++++++--------- .../visibility/propagate_render_groups.rs | 4 +- 2 files changed, 44 insertions(+), 42 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index 19157581d60c0..f6feb30cdc847 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -215,46 +215,48 @@ impl Plugin for VisibilityPlugin { fn build(&self, app: &mut bevy_app::App) { use VisibilitySystems::*; - app.configure_sets( - PostUpdate, - PropagateRenderGroupsSet.in_set(VisibilityPropagate), - ) - .add_systems( - PostUpdate, - ( - calculate_bounds.in_set(CalculateBounds), - update_frusta:: - .in_set(UpdateOrthographicFrusta) - .after(camera_system::) - .after(TransformSystem::TransformPropagate) - // We assume that no camera will have more than one projection component, - // so these systems will run independently of one another. - // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. - .ambiguous_with(update_frusta::) - .ambiguous_with(update_frusta::), - update_frusta:: - .in_set(UpdatePerspectiveFrusta) - .after(camera_system::) - .after(TransformSystem::TransformPropagate) - // We assume that no camera will have more than one projection component, - // so these systems will run independently of one another. - // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. - .ambiguous_with(update_frusta::), - update_frusta:: - .in_set(UpdateProjectionFrusta) - .after(camera_system::) - .after(TransformSystem::TransformPropagate), - (visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate), - check_visibility - .in_set(CheckVisibility) - .after(CalculateBounds) - .after(UpdateOrthographicFrusta) - .after(UpdatePerspectiveFrusta) - .after(UpdateProjectionFrusta) - .after(VisibilityPropagate) - .after(TransformSystem::TransformPropagate), - ), - ); + app.add_plugins(PropagateRenderGroupsPlugin) + .configure_sets( + PostUpdate, + PropagateRenderGroupsSet.in_set(VisibilityPropagate), + ) + .add_systems( + PostUpdate, + ( + calculate_bounds.in_set(CalculateBounds), + update_frusta:: + .in_set(UpdateOrthographicFrusta) + .after(camera_system::) + .after(TransformSystem::TransformPropagate) + // We assume that no camera will have more than one projection component, + // so these systems will run independently of one another. + // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. + .ambiguous_with(update_frusta::) + .ambiguous_with(update_frusta::), + update_frusta:: + .in_set(UpdatePerspectiveFrusta) + .after(camera_system::) + .after(TransformSystem::TransformPropagate) + // We assume that no camera will have more than one projection component, + // so these systems will run independently of one another. + // FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481. + .ambiguous_with(update_frusta::), + update_frusta:: + .in_set(UpdateProjectionFrusta) + .after(camera_system::) + .after(TransformSystem::TransformPropagate), + (visibility_propagate_system, reset_view_visibility) + .in_set(VisibilityPropagate), + check_visibility + .in_set(CheckVisibility) + .after(CalculateBounds) + .after(UpdateOrthographicFrusta) + .after(UpdatePerspectiveFrusta) + .after(UpdateProjectionFrusta) + .after(VisibilityPropagate) + .after(TransformSystem::TransformPropagate), + ), + ); } } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 2b807b215dea7..f177538802036 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -136,7 +136,7 @@ The base-line performance cost of this algorithm comes from detecting changes, w use crate::view::*; use crate::prelude::Camera; -use bevy_app::{App, Plugin, PostUpdate}; +use bevy_app::PostUpdate; use bevy_derive::Deref; use bevy_ecs::entity::EntityHashSet; use bevy_ecs::prelude::*; @@ -275,7 +275,7 @@ pub struct InheritedRenderGroups { } impl InheritedRenderGroups { - fn empty() -> Self { + pub fn empty() -> Self { Self { propagator: Entity::PLACEHOLDER, computed: RenderGroups::empty(), From a07d7dc32822f8b76172c2e9862a0c4cc4f04870 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 19:07:13 -0500 Subject: [PATCH 29/54] cleanup --- crates/bevy_pbr/src/light/mod.rs | 2 +- crates/bevy_render/src/view/visibility/mod.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index d56f729ab1345..8a2522277ac4b 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1050,7 +1050,7 @@ impl GlobalVisiblePointLights { // NOTE: Run this before update_point_light_frusta! #[allow(clippy::too_many_arguments)] -pub(crate) fn assign_lights_to_clusters<'w, 's>( +pub(crate) fn assign_lights_to_clusters( mut commands: Commands, mut global_lights: ResMut, mut views: Query<( diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index f6feb30cdc847..ff7c59a477231 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -427,9 +427,7 @@ pub fn check_visibility( return; } - // Check render groups. - // - If there is no RenderGroups in the entity, use the *default* value because the - // entity is in the DEFAULT_RENDER_LAYER. + // Check if this camera can see the entity. if !camera_view.entity_is_visible( camera_entity, &derive_render_groups(maybe_inherited, maybe_groups), From 98d6883103abfd3cf7555bdcff8d359a50aa4a34 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 19:16:45 -0500 Subject: [PATCH 30/54] fix query access error --- .../view/visibility/propagate_render_groups.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index f177538802036..58c41952cc238 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -1003,8 +1003,12 @@ fn handle_new_children_nonpropagator( mut updated_entities: ResMut, // Entities with InheritedRenderGroups that changed children inherited_with_children: Query< - (Entity, &Children, &InheritedRenderGroups), - (Changed, Without), + (Entity, &Children), + ( + Changed, + With, + Without, + ), >, // Query for accessing propagators all_propagators: Query<( @@ -1022,14 +1026,17 @@ fn handle_new_children_nonpropagator( Without, >, ) { - for (entity, children, inherited) in inherited_with_children.iter() { + for (entity, children) in inherited_with_children.iter() { // Skip entity if already updated, which implies children are already in an accurate state. if updated_entities.contains(entity) { continue; } + // Get the inherited component. We need to do a lookup due to query conflict (Error B0001). + let inherited_propagator = maybe_inherited.get(entity).unwrap().1.unwrap().propagator; + let Ok((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = - all_propagators.get(inherited.propagator) + all_propagators.get(inherited_propagator) else { // Remove InheritedRenderGroups from descendents if the propagator is missing // - This is either an error caused by manually modifying InheritedRenderGroups, or is caused by a From 57a7702726e2fcd23226c578ed2e7e495c1ded7b Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 19:33:53 -0500 Subject: [PATCH 31/54] add upper bounds on RenderLayer --- .../src/view/visibility/render_groups.rs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 79d9a5390ea42..68997cfff5689 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -4,10 +4,27 @@ use bevy_derive::{Deref, DerefMut}; use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; use bevy_reflect::prelude::ReflectDefault; use bevy_reflect::Reflect; +use bevy_utils::warn_once; use smallvec::SmallVec; use std::ops::Deref; +/// Records the highest [`RenderLayer`] that can be added to a [`RenderLayers`] before +/// a warning is emitted. +/// +/// We issue a warning because [`RenderLayers`] allocates in order to have enough room for a given +/// [`RenderLayer`], which is an index into a growable bitset. Large [`RenderLayer`] values can consume +/// a lot of memory since [`RenderGroups`] and [`InheritedRenderGroups`] are potentially on many entities. +pub const RENDER_LAYERS_WARNING_LIMIT: usize = 1024; + +/// Records the highest [`RenderLayer`] that can be added to a [`RenderLayers`] before +/// a panic occurs. +/// +/// We panic because [`RenderLayers`] allocates in order to have enough room for a given +/// [`RenderLayer`], which is an index into a growable bitset. Large [`RenderLayer`] values can consume +/// a lot of memory since [`RenderGroups`] and [`InheritedRenderGroups`] are potentially on many entities. +pub const RENDER_LAYERS_PANIC_LIMIT: usize = 1_000_000; + /// The default [`RenderLayer`]. pub static DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); @@ -165,6 +182,16 @@ impl RenderLayers { } fn layer_info(layer: usize) -> (usize, u64) { + if layer > RENDER_LAYERS_WARNING_LIMIT { + warn_once!("RenderLayers encountered a layer {layer} that exceeded the warning limit \ + RENDER_LAYERS_WARNING_LIMIT = {RENDER_LAYERS_WARNING_LIMIT}, you can ignore this message if \ + that is not a bug"); + } + if layer > RENDER_LAYERS_PANIC_LIMIT { + panic!("RenderLayers encountered a layer {layer} that exceeded the maximum upper bound on number of \ + layers RENDER_LAYERS_PANIC_LIMIT = {RENDER_LAYERS_PANIC_LIMIT}"); + } + let buffer_index = layer / 64; let bit_index = layer % 64; let bit = 1u64 << bit_index; From 6f90f28798af3120ff91bfcb438020001c8958c8 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 19:34:56 -0500 Subject: [PATCH 32/54] add docs --- crates/bevy_render/src/view/visibility/render_groups.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 68997cfff5689..f147ea4d81bfc 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -69,6 +69,8 @@ impl Default for RenderLayer { /// /// `RenderLayers` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. +/// +/// See [`RENDER_LAYERS_WARNING_LIMIT`] and [`RENDER_LAYERS_PANIC_LIMIT`] for `RenderLayers` restrictions. #[derive(Debug, Clone, PartialEq, Reflect)] #[reflect(Default, PartialEq)] pub struct RenderLayers { From 4614a5d2bef07f16ab694692b205eadc664b5c34 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 19:41:06 -0500 Subject: [PATCH 33/54] rebase and fix debug_overlay --- crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs | 10 +++++----- .../bevy_render/src/view/visibility/render_groups.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs index 3029a05c9b6cc..f056cf06b2f27 100644 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs +++ b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs @@ -12,7 +12,7 @@ use bevy_math::{Vec2, Vec3Swizzles}; use bevy_render::{ camera::RenderTarget, prelude::*, - view::{RenderLayers, VisibilitySystems}, + view::{RenderGroups, RenderLayer, VisibilitySystems}, }; use bevy_transform::{prelude::GlobalTransform, TransformSystem}; use bevy_ui::{DefaultUiCamera, Display, Node, Style, TargetCamera, UiScale}; @@ -27,8 +27,8 @@ mod inset; /// The [`Camera::order`] index used by the layout debug camera. pub const LAYOUT_DEBUG_CAMERA_ORDER: isize = 255; -/// The [`RenderLayers`] used by the debug gizmos and the debug camera. -pub const LAYOUT_DEBUG_LAYERS: RenderLayers = RenderLayers::none().with(16); +/// The [`RenderLayer`] used by the debug gizmos and the debug camera. +pub const LAYOUT_DEBUG_LAYER: RenderLayer = RenderLayer(16); #[derive(Clone, Copy)] struct LayoutRect { @@ -101,7 +101,7 @@ fn update_debug_camera( }, ..default() }, - LAYOUT_DEBUG_LAYERS, + RenderGroups::from(LAYOUT_DEBUG_LAYER), DebugOverlayCamera, Name::new("Layout Debug Camera"), )) @@ -109,7 +109,7 @@ fn update_debug_camera( }; if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { config.enabled = true; - config.render_layers = LAYOUT_DEBUG_LAYERS; + config.render_groups = RenderGroups::from(LAYOUT_DEBUG_LAYER); } let cam = *options.layout_gizmos_camera.get_or_insert_with(spawn_cam); let Ok(mut cam) = debug_cams.get_mut(cam) else { diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index f147ea4d81bfc..f6b06dba97a64 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -69,7 +69,7 @@ impl Default for RenderLayer { /// /// `RenderLayers` can store up to `RenderLayer(63)` without allocating. Allocations occur in 8-byte /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. -/// +/// /// See [`RENDER_LAYERS_WARNING_LIMIT`] and [`RENDER_LAYERS_PANIC_LIMIT`] for `RenderLayers` restrictions. #[derive(Debug, Clone, PartialEq, Reflect)] #[reflect(Default, PartialEq)] From 7ac9b040735b4a83d4753d829b721fdadca25d3e Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 20:11:10 -0500 Subject: [PATCH 34/54] CI fixes --- crates/bevy_pbr/src/light/mod.rs | 8 +++++--- .../src/view/visibility/propagate_render_groups.rs | 4 ++-- .../src/view/visibility/render_groups.rs | 14 +++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 8a2522277ac4b..4051f257b9264 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1026,9 +1026,11 @@ impl PointLightAssignmentData { } } -/// SAFETY: PointLightAssignmentData is only used in `assign_lights_to_clusters`, where it is not reused -/// between system calls. +// SAFETY: `PointLightAssignmentData` is only used in `assign_lights_to_clusters`, where it is not reused +// between system calls. unsafe impl Send for PointLightAssignmentData {} +// SAFETY: `PointLightAssignmentData` is only used in `assign_lights_to_clusters`, where it is not reused +// between system calls. unsafe impl Sync for PointLightAssignmentData {} #[derive(Resource, Default)] @@ -1421,7 +1423,7 @@ pub(crate) fn assign_lights_to_clusters( // SAFETY: `lights` is cleared at the start of this system call, and is populated from // immutable queries. let light_rendergroups = unsafe { light.render_groups.get() }; - if !view_groups.intersects(&light_rendergroups) { + if !view_groups.intersects(light_rendergroups) { continue; } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index 58c41952cc238..ca9191da4d704 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -147,7 +147,7 @@ use bevy_utils::tracing::warn; /// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on /// entities. /// -/// Runs in [`PostUpdate`] in the [`VisibilityPropagate`] set. +/// Runs in [`PostUpdate`] in the [`VisibilityPropagate`](VisibilitySystems::VisibilityPropagate) set. #[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] pub struct PropagateRenderGroupsSet; @@ -185,7 +185,7 @@ pub enum PropagateRenderGroups { /// If the entity has a [`RenderGroups`] component, that value is propagated, otherwise a default /// [`RenderGroups`] is propagated. /// - /// Note that it is allowed to add a [`RenderGroup`] component to a camera. + /// Note that it is allowed to add a [`RenderGroup`](crate::view::RenderGroups) component to a camera. Auto, /// If the entity has a [`Camera`] component, propagates `RenderGroups::new_with_camera(entity)`. /// diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index f6b06dba97a64..bc1724a880dde 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -1,4 +1,4 @@ -use crate::view::ExtractedRenderGroups; +use crate::view::*; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; @@ -71,7 +71,7 @@ impl Default for RenderLayer { /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. /// /// See [`RENDER_LAYERS_WARNING_LIMIT`] and [`RENDER_LAYERS_PANIC_LIMIT`] for `RenderLayers` restrictions. -#[derive(Debug, Clone, PartialEq, Reflect)] +#[derive(Clone, PartialEq, Reflect)] #[reflect(Default, PartialEq)] pub struct RenderLayers { layers: SmallVec<[u64; 1]>, @@ -246,6 +246,14 @@ impl Default for RenderLayers { } } +impl std::fmt::Debug for RenderLayers { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("RenderLayers") + .field(&self.iter().map(|l| *l).collect::>()) + .finish() + } +} + /// Component on an entity that controls which cameras can see it. /// /// There are two kinds of render groups: @@ -270,7 +278,7 @@ impl Default for RenderLayers { /// For example, if you do `entity.insert(RenderGroups::from(RenderLayer(1)))`, then `entity` /// will only be in layer 1. You can instead do: /** -```no-run +```no_run // Option 1: default let mut groups = RenderGroups::default(); groups.add(RenderLayer(1); From d8a63c1739fb0457dd36969cc8bf3f9b8bf44e34 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 20:16:07 -0500 Subject: [PATCH 35/54] CI complaints --- crates/bevy_render/src/view/visibility/render_groups.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index bc1724a880dde..0a8c6b863cbce 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -1,8 +1,7 @@ use crate::view::*; use bevy_derive::{Deref, DerefMut}; -use bevy_ecs::prelude::{Component, Entity, ReflectComponent}; -use bevy_reflect::prelude::ReflectDefault; +use bevy_ecs::prelude::Component; use bevy_reflect::Reflect; use bevy_utils::warn_once; From c82492b6cc4217a15821455144a76b4780166b9a Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 20:38:51 -0500 Subject: [PATCH 36/54] more CI --- crates/bevy_render/src/view/visibility/render_groups.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 0a8c6b863cbce..629c0c41be1b2 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -280,12 +280,12 @@ impl std::fmt::Debug for RenderLayers { ```no_run // Option 1: default let mut groups = RenderGroups::default(); -groups.add(RenderLayer(1); +groups.add(RenderLayer(1)); entity.insert(groups); // Option 2: explicit let mut groups = RenderGroups::from(0); -groups.add(RenderLayer(1); +groups.add(RenderLayer(1)); entity.insert(groups); // Option 3: manual From 35b8dcab29c0765605415e5043fce2ea0bb5f951 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 20:48:52 -0500 Subject: [PATCH 37/54] CI again --- crates/bevy_render/src/view/visibility/render_groups.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 629c0c41be1b2..971c767c8447c 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -277,7 +277,7 @@ impl std::fmt::Debug for RenderLayers { /// For example, if you do `entity.insert(RenderGroups::from(RenderLayer(1)))`, then `entity` /// will only be in layer 1. You can instead do: /** -```no_run +```ignore // Option 1: default let mut groups = RenderGroups::default(); groups.add(RenderLayer(1)); From 75b3be2b983c52d6cb319575cdbe72d0385666c8 Mon Sep 17 00:00:00 2001 From: koe Date: Tue, 19 Mar 2024 22:16:53 -0500 Subject: [PATCH 38/54] don't use default RenderGroups when propagating --- .../visibility/propagate_render_groups.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs index ca9191da4d704..91b8aa3760890 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_groups.rs @@ -233,7 +233,7 @@ impl PropagateRenderGroups { if view.is_allocated() && saved.is_allocated() { // Reuse saved allocation. - let mut temp = RenderGroups::default(); + let mut temp = RenderGroups::empty(); std::mem::swap(&mut temp, saved); temp.set_from_parts(Some(entity), view.layers()); RenderGroupsRef::Val(temp) @@ -275,6 +275,7 @@ pub struct InheritedRenderGroups { } impl InheritedRenderGroups { + /// Makes an empty `InheritedRenderGroups`. pub fn empty() -> Self { Self { propagator: Entity::PLACEHOLDER, @@ -514,8 +515,8 @@ fn apply_full_propagation( }; // Update inherited value or insert a new one. - let default_render_groups = RenderGroups::default(); - let initial_groups = maybe_render_groups.unwrap_or(&default_render_groups); + let empty_render_groups = RenderGroups::empty(); + let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); let apply_changes = |groups: &mut InheritedRenderGroups| { groups.propagator = propagator; groups.computed.set_from(initial_groups); @@ -644,8 +645,8 @@ fn apply_new_children_propagation( } // Update inherited value or insert a new one. - let default_render_groups = RenderGroups::default(); - let initial_groups = maybe_render_groups.unwrap_or(&default_render_groups); + let empty_render_groups = RenderGroups::empty(); + let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); let apply_changes = |groups: &mut InheritedRenderGroups| { groups.propagator = propagator; groups.computed.set_from(initial_groups); @@ -918,8 +919,8 @@ fn apply_full_propagation_force_update( } // Force-update - let default_render_groups = RenderGroups::default(); - let initial_groups = maybe_render_groups.unwrap_or(&default_render_groups); + let empty_render_groups = RenderGroups::empty(); + let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); let mut new = InheritedRenderGroups::empty(); if let Some(mut inherited) = maybe_inherited_groups { @@ -1184,7 +1185,7 @@ fn handle_modified_rendergroups( &PropagateRenderGroups, )>, // Query for updating InheritedRenderGroups on non-propagator entities. - mut maybe_inherited: Query< + mut inherited: Query< (Option<&RenderGroups>, &mut InheritedRenderGroups), Without, >, @@ -1196,7 +1197,7 @@ fn handle_modified_rendergroups( } // Skip entity if it's a propagator or doesn't exist. - let Ok((entity_render_groups, mut inherited)) = maybe_inherited.get_mut(entity) else { + let Ok((entity_render_groups, mut inherited)) = inherited.get_mut(entity) else { continue; }; @@ -1221,7 +1222,7 @@ fn handle_modified_rendergroups( // Update entity value. inherited .computed - .set_from(entity_render_groups.unwrap_or(&RenderGroups::default())); + .set_from(entity_render_groups.unwrap_or(&RenderGroups::empty())); inherited.computed.merge(&propagated); // Mark updated (in case of duplicates due to removals). From 5c40a7f4e10a14effa4d708385ff99a78f157f44 Mon Sep 17 00:00:00 2001 From: koe Date: Wed, 20 Mar 2024 18:10:30 -0500 Subject: [PATCH 39/54] API cleanup --- .../src/ui_debug_overlay/mod.rs | 4 +- .../src/view/visibility/render_groups.rs | 99 +++++++++++-------- examples/2d/pixel_grid_snap.rs | 12 +-- examples/3d/render_to_texture.rs | 6 +- 4 files changed, 69 insertions(+), 52 deletions(-) diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs index f056cf06b2f27..497783dfbbc3d 100644 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs +++ b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs @@ -101,7 +101,7 @@ fn update_debug_camera( }, ..default() }, - RenderGroups::from(LAYOUT_DEBUG_LAYER), + RenderGroups::from_layer(LAYOUT_DEBUG_LAYER), DebugOverlayCamera, Name::new("Layout Debug Camera"), )) @@ -109,7 +109,7 @@ fn update_debug_camera( }; if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { config.enabled = true; - config.render_groups = RenderGroups::from(LAYOUT_DEBUG_LAYER); + config.render_groups = RenderGroups::from_layer(LAYOUT_DEBUG_LAYER); } let cam = *options.layout_gizmos_camera.get_or_insert_with(spawn_cam); let Ok(mut cam) = debug_cams.get_mut(cam) else { diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index 971c767c8447c..a9868120d0a69 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -84,6 +84,15 @@ impl RenderLayers { } } + /// Makes a new `RenderLayers` from a single [`RenderLayer`]. + pub fn from_layer(layer: impl Into) -> Self { + let mut layers = Self { + layers: SmallVec::default(), + }; + layers.add(layer); + layers + } + /// Makes a new `RenderLayers` from a slice. pub fn from_layers + Copy>(layers: &[T]) -> Self { layers.iter().map(|l| (*l).into()).collect() @@ -220,16 +229,6 @@ impl RenderLayers { } } -impl> From for RenderLayers { - fn from(layer: T) -> Self { - let mut layers = Self { - layers: SmallVec::default(), - }; - layers.add(layer); - layers - } -} - impl> FromIterator for RenderLayers { fn from_iter>(i: T) -> Self { i.into_iter().fold(Self::empty(), |mut mask, g| { @@ -241,7 +240,7 @@ impl> FromIterator for RenderLayers { impl Default for RenderLayers { fn default() -> Self { - Self::from(DEFAULT_RENDER_LAYER) + Self::from_layer(DEFAULT_RENDER_LAYER) } } @@ -329,6 +328,22 @@ impl RenderGroups { } } + /// Makes a new `RenderGroups` with a single [`RenderLayer`] and no camera. + pub fn from_layer(layer: impl Into) -> Self { + Self { + layers: RenderLayers::from_layer(layer), + camera: None, + } + } + + /// Makes a new `RenderGroups` with an array of [`RenderLayer`] and no camera. + pub fn from_layers + Copy>(layers: &[T]) -> Self { + Self { + layers: RenderLayers::from_layers(layers), + camera: None, + } + } + /// Adds a [`RenderLayer`]. /// /// See [`RenderLayers::add`]. @@ -438,16 +453,6 @@ impl RenderGroups { } } -impl From for RenderGroups { - /// Makes a new `RenderGroups` from a specific [`RenderLayer`]. - fn from(layer: RenderLayer) -> Self { - Self { - layers: RenderLayers::from(layer), - camera: None, - } - } -} - impl From for RenderGroups { /// Makes a new `RenderGroups` from a [`RenderLayers`]. fn from(layers: RenderLayers) -> Self { @@ -459,9 +464,9 @@ impl From for RenderGroups { } impl Default for RenderGroups { - /// Equivalent to `Self::from(DEFAULT_RENDER_LAYER)`. + /// Equivalent to `Self::from_layer(DEFAULT_RENDER_LAYER)`. fn default() -> Self { - Self::from(DEFAULT_RENDER_LAYER) + Self::from_layer(DEFAULT_RENDER_LAYER) } } @@ -571,6 +576,20 @@ impl CameraView { } } + /// Makes a new `CameraView` with a single [`RenderLayer`] and no camera. + pub fn from_layer(layer: impl Into) -> Self { + Self { + layers: RenderLayers::from_layer(layer), + } + } + + /// Makes a new `CameraView` with an array of [`RenderLayer`] and no camera. + pub fn from_layers + Copy>(layers: &[T]) -> Self { + Self { + layers: RenderLayers::from_layers(layers), + } + } + /// Adds a [`RenderLayer`]. /// /// See [`RenderLayers::add`]. @@ -632,19 +651,17 @@ impl CameraView { } } -impl From for CameraView { - /// Makes a new `CameraView` from a specific [`RenderLayer`]. - fn from(layer: RenderLayer) -> Self { - Self { - layers: RenderLayers::from(layer), - } +impl From for CameraView { + /// Makes a new `CameraView` from a [`RenderLayers`]. + fn from(layers: RenderLayers) -> Self { + Self { layers } } } impl Default for CameraView { - /// Equivalent to `Self::from(DEFAULT_RENDER_LAYER)`. + /// Equivalent to `Self::from_layer(DEFAULT_RENDER_LAYER)`. fn default() -> Self { - Self::from(DEFAULT_RENDER_LAYER) + Self::from_layer(DEFAULT_RENDER_LAYER) } } @@ -665,44 +682,44 @@ mod rendering_mask_tests { "default layer contains default" ); assert_eq!( - RenderLayers::from(RenderLayer(1)).num_layers(), + RenderLayers::from_layer(1).num_layers(), 1, "from contains 1 layer" ); assert!( - RenderLayers::from(RenderLayer(1)).contains(RenderLayer(1)), + RenderLayers::from_layer(1).contains(RenderLayer(1)), "contains is accurate" ); assert!( - !RenderLayers::from(RenderLayer(1)).contains(RenderLayer(2)), + !RenderLayers::from_layer(1).contains(RenderLayer(2)), "contains fails when expected" ); assert_eq!( - RenderLayers::from(RenderLayer(0)).add(1).layers[0], + RenderLayers::from_layer(0).add(1).layers[0], 3, "layer 0 + 1 is mask 3" ); assert_eq!( - RenderLayers::from(RenderLayer(0)).add(1).remove(0).layers[0], + RenderLayers::from_layer(0).add(1).remove(0).layers[0], 2, "layer 0 + 1 - 0 is mask 2" ); assert!( - RenderLayers::from(RenderLayer(1)).intersects(&RenderLayers::from(RenderLayer(1))), + RenderLayers::from_layer(1).intersects(&RenderLayers::from_layer(1)), "layers match like layers" ); assert!( - RenderLayers::from(RenderLayer(0)).intersects(&RenderLayers { + RenderLayers::from_layer(0).intersects(&RenderLayers { layers: SmallVec::from_slice(&[1]) }), "a layer of 0 means the mask is just 1 bit" ); assert!( - RenderLayers::from(RenderLayer(0)) + RenderLayers::from_layer(0) .add(3) - .intersects(&RenderLayers::from(RenderLayer(3))), + .intersects(&RenderLayers::from_layer(3)), "a mask will match another mask containing any similar layers" ); @@ -712,7 +729,7 @@ mod rendering_mask_tests { ); assert!( - !RenderLayers::from(RenderLayer(0)).intersects(&RenderLayers::from(RenderLayer(1))), + !RenderLayers::from_layer(0).intersects(&RenderLayers::from_layer(1)), "masks with differing layers do not match" ); assert!( diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 75e4827d4081f..30a52f0bc8551 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -60,7 +60,7 @@ fn setup_sprite(mut commands: Commands, asset_server: Res) { ..default() }, Rotate, - RenderGroups::from(PIXEL_PERFECT_LAYER), + RenderGroups::from_layer(PIXEL_PERFECT_LAYER), )); // the sample sprite that will be rendered to the high-res "outer world" @@ -71,7 +71,7 @@ fn setup_sprite(mut commands: Commands, asset_server: Res) { ..default() }, Rotate, - RenderGroups::from(HIGH_RES_LAYER), + RenderGroups::from_layer(HIGH_RES_LAYER), )); } @@ -89,7 +89,7 @@ fn setup_mesh( ..default() }, Rotate, - RenderGroups::from(PIXEL_PERFECT_LAYER), + RenderGroups::from_layer(PIXEL_PERFECT_LAYER), )); } @@ -134,7 +134,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { ..default() }, InGameCamera, - CameraView::from(PIXEL_PERFECT_LAYER), + CameraView::from_layer(PIXEL_PERFECT_LAYER), )); // spawn the canvas @@ -144,7 +144,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { ..default() }, Canvas, - RenderGroups::from(HIGH_RES_LAYER), + RenderGroups::from_layer(HIGH_RES_LAYER), )); // the "outer" camera renders whatever is on `HIGH_RES_LAYER` to the screen. @@ -152,7 +152,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { commands.spawn(( Camera2dBundle::default(), OuterCamera, - CameraView::from(HIGH_RES_LAYER), + CameraView::from_layer(HIGH_RES_LAYER), )); } diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 07080628b0603..459fd57c81c28 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -8,7 +8,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::{RenderGroups, RenderLayer, RenderLayers}, + view::RenderGroups, }, }; @@ -71,7 +71,7 @@ fn setup( }); // This specifies the group used for the first pass, which will be attached to the first pass camera and cube. - let first_pass_group = RenderGroups::from(RenderLayer(1)); + let first_pass_group = RenderGroups::from_layer(1); // The cube that will be rendered to the texture. commands.spawn(( @@ -94,7 +94,7 @@ fn setup( transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }, - RenderGroups::from(RenderLayers::from_layers(&[0, 1])), + RenderGroups::from_layers(&[0, 1]), )); commands.spawn(( From 64ce22e88e1c80abd16ffee446b2f48b12042064 Mon Sep 17 00:00:00 2001 From: koe Date: Wed, 20 Mar 2024 22:52:52 -0500 Subject: [PATCH 40/54] fix directional lights not being applied properly --- crates/bevy_pbr/src/render/light.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index b57badff64a25..580b05c478b26 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1146,7 +1146,7 @@ pub fn prepare_lights( let gpu_light = &mut gpu_lights.directional_lights[light_index]; // Check if the light intersects with the view. - if extracted_render_groups.intersects(&light.render_groups) { + if !extracted_render_groups.intersects(&light.render_groups) { gpu_light.skip = 1u32; continue; } From a646c1457a609f4c106225ae6ec27d1b7c3de548 Mon Sep 17 00:00:00 2001 From: Talin Date: Thu, 21 Mar 2024 11:00:35 -0700 Subject: [PATCH 41/54] Render groups example (#2) * RenderGroups example. --- Cargo.toml | 11 +++ examples/3d/render_groups.rs | 131 +++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 examples/3d/render_groups.rs diff --git a/Cargo.toml b/Cargo.toml index 019ce0c6e9059..76cd378c16981 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1993,6 +1993,17 @@ description = "Demonstrates loading from and saving scenes to files" category = "Scene" wasm = false +[[example]] +name = "render_groups" +path = "examples/3d/render_groups.rs" +doc-scrape-examples = true + +[package.metadata.example.render_groups] +name = "Render Groups" +description = "Spawning multiple copies of a scene from a GLTF file, each with a different render group" +category = "3D Rendering" +wasm = true + # Shaders [[package.metadata.example_category]] name = "Shaders" diff --git a/examples/3d/render_groups.rs b/examples/3d/render_groups.rs new file mode 100644 index 0000000000000..846fe0265bf15 --- /dev/null +++ b/examples/3d/render_groups.rs @@ -0,0 +1,131 @@ +//! Load a scene from a glTF file and render it with different render groups. + +use bevy::{ + color::palettes, + pbr::DirectionalLightShadowMap, + prelude::*, + render::view::{CameraView, PropagateRenderGroups, RenderGroups}, +}; + +fn main() { + App::new() + .insert_resource(DirectionalLightShadowMap { size: 4096 }) + .add_plugins(DefaultPlugins) + .add_systems(Startup, setup) + .add_systems(Update, toggle_layers) + .run(); +} + +#[derive(Component)] +struct MovedScene; + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn(( + DirectionalLightBundle { + transform: Transform::from_xyz(4.0, 25.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y), + directional_light: DirectionalLight { + shadows_enabled: true, + illuminance: 100000.0, + color: palettes::basic::RED.into(), + ..default() + }, + ..default() + }, + RenderGroups::from_layers(&[1, 2, 3]), + )); + + commands.spawn(( + Camera3dBundle { + transform: Transform::from_xyz(0., 1.4, 2.0) + .looking_at(Vec3::new(0., 0.3, 0.0), Vec3::Y), + ..default() + }, + EnvironmentMapLight { + diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + intensity: 1500.0, + }, + CameraView::from_layers(&[0, 1, 2, 3]), + )); + + commands.spawn(( + TextBundle::from_section( + "Press '1..3' to toggle camera render layers\n\ + Press '4..6' to toggle directional light render layers", + TextStyle { + font_size: 20., + ..default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(12.0), + left: Val::Px(12.0), + ..default() + }), + RenderGroups::from_layer(0), + )); + + // Spawn three copies of the scene, each with a different render group. + for i in 0..3 { + commands.spawn(( + SceneBundle { + transform: Transform::from_xyz(i as f32 - 1.0, 0.0, 0.0), + scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"), + ..default() + }, + RenderGroups::from_layer(i + 1), + PropagateRenderGroups::Auto, + )); + } +} + +fn toggle_layers( + mut query_camera: Query<&mut CameraView>, + mut query_light: Query<&mut RenderGroups, With>, + keyboard: Res>, +) { + let Ok(mut camera_view) = query_camera.get_single_mut() else { + return; + }; + + let Ok(mut light_groups) = query_light.get_single_mut() else { + return; + }; + + if keyboard.just_pressed(KeyCode::Digit1) { + toggle_camera_layer(&mut camera_view, 1); + } + if keyboard.just_pressed(KeyCode::Digit2) { + toggle_camera_layer(&mut camera_view, 2); + } + if keyboard.just_pressed(KeyCode::Digit3) { + toggle_camera_layer(&mut camera_view, 3); + } + + if keyboard.just_pressed(KeyCode::Digit4) { + toggle_render_layer(&mut light_groups, 1); + } + if keyboard.just_pressed(KeyCode::Digit5) { + toggle_render_layer(&mut light_groups, 2); + } + if keyboard.just_pressed(KeyCode::Digit6) { + toggle_render_layer(&mut light_groups, 3); + } +} + +fn toggle_camera_layer(camera_view: &mut CameraView, layer: usize) { + if camera_view.contains_layer(layer) { + camera_view.remove(layer); + } else { + camera_view.add(layer); + } +} + +fn toggle_render_layer(groups: &mut RenderGroups, layer: usize) { + if groups.contains_layer(layer) { + groups.remove(layer); + } else { + groups.add(layer); + } +} From 52870ee3bb179d0d05bcab73ce187ac815947487 Mon Sep 17 00:00:00 2001 From: koe Date: Thu, 21 Mar 2024 17:31:09 -0500 Subject: [PATCH 42/54] update directional light intersections with entities so if a camera sees a directional light, it will see shadows applied by that light to entities it can see --- crates/bevy_pbr/src/light/mod.rs | 39 +++++++++-- .../src/view/visibility/render_groups.rs | 16 ++++- examples/3d/render_groups.rs | 65 ++++++++----------- 3 files changed, 76 insertions(+), 44 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 4051f257b9264..858332f6bf50a 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use bevy_ecs::entity::EntityHashMap; +use bevy_ecs::entity::{EntityHashMap, EntityHashSet}; use bevy_ecs::prelude::*; use bevy_math::{ AspectRatio, Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles, @@ -15,8 +15,8 @@ use bevy_render::{ renderer::RenderDevice, view::{ derive_render_groups, derive_render_groups_ptr, extract_camera_view, CameraView, - InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, ViewVisibility, - VisibleEntities, + InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, RenderLayers, + ViewVisibility, VisibleEntities, }, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -1834,7 +1834,10 @@ pub fn update_spot_light_frusta( } pub fn check_light_mesh_visibility( + mut aggregate_view_entities: Local, + mut aggregate_view_layers: Local, visible_point_lights: Query<&VisiblePointLights>, + views: Query<(Entity, Option<&CameraView>), With>, mut point_lights: Query<( &PointLight, &GlobalTransform, @@ -1929,8 +1932,27 @@ pub fn check_light_mesh_visibility( continue; } + // Get the aggregate render groups for all cameras that can see this directional light. + // - We use the camera-aggregate to determine cascade visibility because if any camera can + // see both a directional light and an entity, then it will use the light to illuminate the entity. + // We want illuminated entities to cast shadows as expected, even if an entity and a light technically + // don't 'see' each other - only what the camera sees matters. + aggregate_view_entities.clear(); + aggregate_view_layers.clear(); let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); + for (camera_entity, maybe_view) in views.iter() { + let default_layers = RenderLayers::default(); + let view_layers = maybe_view.map(|v| v.layers()).unwrap_or(&default_layers); + + if !view_mask.intersects_parts(Some(camera_entity), view_layers) { + continue; + } + + aggregate_view_entities.insert(camera_entity); + aggregate_view_layers.merge(view_layers); + } + for ( entity, inherited_visibility, @@ -1948,8 +1970,15 @@ pub fn check_light_mesh_visibility( // Note: A visible entity may have a camera on it, but in this case we ignore the camera and // treat it as a normal entity. The CameraView component controls what the camera can see, while // RenderGroups on the camera entity controls who can see the camera entity. - if !view_mask.intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) { - continue; + let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); + if !aggregate_view_layers.intersects(derived_mask.get().layers()) { + // Only do a hashmap lookup if the entity has a camera affiliation. + if !derived_mask + .camera() + .map_or(false, |c| aggregate_view_entities.contains(&c)) + { + continue; + } } // If we have an aabb and transform, do frustum culling diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_groups.rs index a9868120d0a69..10d9d10f17513 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_groups.rs @@ -425,12 +425,19 @@ impl RenderGroups { /// /// Checks both camera affiliation and [`RenderLayers`] intersection. pub fn intersects(&self, other: &Self) -> bool { - if let (Some(a), Some(b)) = (self.camera, other.camera) { + self.intersects_parts(other.camera, &other.layers) + } + + /// Returns `true` if `Self` intersects with the input parts. + /// + /// Checks both camera affiliation and [`RenderLayers`] intersection. + pub fn intersects_parts(&self, camera: Option, layers: &RenderLayers) -> bool { + if let (Some(a), Some(b)) = (self.camera, camera) { if a == b { return true; } } - self.layers.intersects(&other.layers) + self.layers.intersects(layers) } /// Returns `true` if `Self` intersects with an [`ExtractedRenderGroups`]. @@ -447,6 +454,11 @@ impl RenderGroups { self.camera } + /// Gets the internal [`RenderLayers`]. + pub fn layers(&self) -> &RenderLayers { + &self.layers + } + /// Returns `true` if the internal [`RenderLayers`] is on the heap. pub fn is_allocated(&self) -> bool { self.layers.is_allocated() diff --git a/examples/3d/render_groups.rs b/examples/3d/render_groups.rs index 846fe0265bf15..688fd4aedf0db 100644 --- a/examples/3d/render_groups.rs +++ b/examples/3d/render_groups.rs @@ -20,20 +20,6 @@ fn main() { struct MovedScene; fn setup(mut commands: Commands, asset_server: Res) { - commands.spawn(( - DirectionalLightBundle { - transform: Transform::from_xyz(4.0, 25.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y), - directional_light: DirectionalLight { - shadows_enabled: true, - illuminance: 100000.0, - color: palettes::basic::RED.into(), - ..default() - }, - ..default() - }, - RenderGroups::from_layers(&[1, 2, 3]), - )); - commands.spawn(( Camera3dBundle { transform: Transform::from_xyz(0., 1.4, 2.0) @@ -45,12 +31,12 @@ fn setup(mut commands: Commands, asset_server: Res) { specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 1500.0, }, - CameraView::from_layers(&[0, 1, 2, 3]), + CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), )); commands.spawn(( TextBundle::from_section( - "Press '1..3' to toggle camera render layers\n\ + "Press '1..3' to toggle mesh render layers\n\ Press '4..6' to toggle directional light render layers", TextStyle { font_size: 20., @@ -78,21 +64,35 @@ fn setup(mut commands: Commands, asset_server: Res) { PropagateRenderGroups::Auto, )); } + + // Spawn three directional lights, each with a different render group. + let colors = [ + palettes::basic::RED, + palettes::basic::GREEN, + palettes::basic::AQUA, + ]; + for (i, color) in (0..3).zip(colors.iter()) { + commands.spawn(( + DirectionalLightBundle { + transform: Transform::from_xyz(4.0, 25.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y), + directional_light: DirectionalLight { + shadows_enabled: true, + illuminance: 100000.0, + color: (*color).into(), + ..default() + }, + ..default() + }, + RenderGroups::from_layer(i + 4), + )); + } } -fn toggle_layers( - mut query_camera: Query<&mut CameraView>, - mut query_light: Query<&mut RenderGroups, With>, - keyboard: Res>, -) { +fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res>) { let Ok(mut camera_view) = query_camera.get_single_mut() else { return; }; - let Ok(mut light_groups) = query_light.get_single_mut() else { - return; - }; - if keyboard.just_pressed(KeyCode::Digit1) { toggle_camera_layer(&mut camera_view, 1); } @@ -102,15 +102,14 @@ fn toggle_layers( if keyboard.just_pressed(KeyCode::Digit3) { toggle_camera_layer(&mut camera_view, 3); } - if keyboard.just_pressed(KeyCode::Digit4) { - toggle_render_layer(&mut light_groups, 1); + toggle_camera_layer(&mut camera_view, 4); } if keyboard.just_pressed(KeyCode::Digit5) { - toggle_render_layer(&mut light_groups, 2); + toggle_camera_layer(&mut camera_view, 5); } if keyboard.just_pressed(KeyCode::Digit6) { - toggle_render_layer(&mut light_groups, 3); + toggle_camera_layer(&mut camera_view, 6); } } @@ -121,11 +120,3 @@ fn toggle_camera_layer(camera_view: &mut CameraView, layer: usize) { camera_view.add(layer); } } - -fn toggle_render_layer(groups: &mut RenderGroups, layer: usize) { - if groups.contains_layer(layer) { - groups.remove(layer); - } else { - groups.add(layer); - } -} From 6d9a32df2caf0ebf5bcce7f8c950343327242326 Mon Sep 17 00:00:00 2001 From: koe Date: Thu, 21 Mar 2024 17:38:36 -0500 Subject: [PATCH 43/54] update examples template --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 3f749784e7780..96e784cbb57e3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -143,6 +143,7 @@ Example | Description [Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations [Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties [Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes +[Render Groups](../examples/3d/render_groups.rs) | Spawning multiple copies of a scene from a GLTF file, each with a different render group [Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images [Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion [Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene From b82a0849477d4ad51a0d6da63e2b40780fe85294 Mon Sep 17 00:00:00 2001 From: koe Date: Thu, 21 Mar 2024 18:08:36 -0500 Subject: [PATCH 44/54] clippy --- crates/bevy_pbr/src/light/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 858332f6bf50a..9378e3db48a47 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1833,6 +1833,7 @@ pub fn update_spot_light_frusta( } } +#[allow(clippy::too_many_arguments)] pub fn check_light_mesh_visibility( mut aggregate_view_entities: Local, mut aggregate_view_layers: Local, From 858b88f6b1a93ea67402f4a886e1e88d5f4734f2 Mon Sep 17 00:00:00 2001 From: koe Date: Thu, 21 Mar 2024 18:21:42 -0500 Subject: [PATCH 45/54] add floor to example: --- examples/3d/render_groups.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/3d/render_groups.rs b/examples/3d/render_groups.rs index 688fd4aedf0db..ff2623e45f574 100644 --- a/examples/3d/render_groups.rs +++ b/examples/3d/render_groups.rs @@ -19,7 +19,13 @@ fn main() { #[derive(Component)] struct MovedScene; -fn setup(mut commands: Commands, asset_server: Res) { +fn setup( + mut commands: Commands, + asset_server: Res, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + // Camera commands.spawn(( Camera3dBundle { transform: Transform::from_xyz(0., 1.4, 2.0) @@ -34,6 +40,14 @@ fn setup(mut commands: Commands, asset_server: Res) { CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), )); + // Plane + commands.spawn(PbrBundle { + mesh: meshes.add(Plane3d::default().mesh().size(5000.0, 5000.0)), + material: materials.add(Color::srgb(0.3, 0.5, 0.3)), + ..default() + }); + + // Text commands.spawn(( TextBundle::from_section( "Press '1..3' to toggle mesh render layers\n\ @@ -69,7 +83,7 @@ fn setup(mut commands: Commands, asset_server: Res) { let colors = [ palettes::basic::RED, palettes::basic::GREEN, - palettes::basic::AQUA, + palettes::basic::NAVY, ]; for (i, color) in (0..3).zip(colors.iter()) { commands.spawn(( From 962d722a9935cdb934e46ce9a47b231466c20f86 Mon Sep 17 00:00:00 2001 From: koe Date: Fri, 22 Mar 2024 08:43:21 -0500 Subject: [PATCH 46/54] fix shadows appearing erroneously for point and spot lights when lights can see a mesh but the camera can't see the lights --- crates/bevy_pbr/src/light/mod.rs | 88 ++++++++++++++++++++------------ examples/3d/render_groups.rs | 26 ++++++++-- 2 files changed, 77 insertions(+), 37 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 9378e3db48a47..c3ff261aee0fa 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -15,7 +15,7 @@ use bevy_render::{ renderer::RenderDevice, view::{ derive_render_groups, derive_render_groups_ptr, extract_camera_view, CameraView, - InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, RenderLayers, + InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, RenderGroupsRef, RenderLayers, ViewVisibility, VisibleEntities, }, }; @@ -1835,8 +1835,8 @@ pub fn update_spot_light_frusta( #[allow(clippy::too_many_arguments)] pub fn check_light_mesh_visibility( - mut aggregate_view_entities: Local, mut aggregate_view_layers: Local, + mut aggregate_view_entities: Local, visible_point_lights: Query<&VisiblePointLights>, views: Query<(Entity, Option<&CameraView>), With>, mut point_lights: Query<( @@ -1879,6 +1879,47 @@ pub fn check_light_mesh_visibility( (Without, Without), >, ) { + // We use camera-aggregates to determine cascade visibility because if any camera can + // see both a light and an entity, then it will use the light to illuminate the entity. + // We want illuminated entities to cast shadows as expected, even if an entity and a light technically + // don't 'see' each other. Only what the camera sees matters. + let set_aggregate_view = | + aggregate_view_layers: &mut RenderLayers, + aggregate_view_entities: &mut EntityHashSet, + light_groups: RenderGroupsRef<'_>, + | { + aggregate_view_layers.clear(); + aggregate_view_entities.clear(); + for (camera_entity, maybe_view) in views.iter() { + let default_layers = RenderLayers::default(); + let view_layers = maybe_view.map(|v| v.layers()).unwrap_or(&default_layers); + + if !light_groups.intersects_parts(Some(camera_entity), view_layers) { + continue; + } + + aggregate_view_layers.merge(view_layers); + aggregate_view_entities.insert(camera_entity); + } + }; + + fn aggregate_view_intersects( + aggregate_view_layers: &RenderLayers, + aggregate_view_entities: &EntityHashSet, + entity_groups: RenderGroupsRef<'_>, + ) -> bool { + if aggregate_view_layers.intersects(entity_groups.get().layers()) { + return true; + } + + let Some(camera_affiliation) = entity_groups.camera() else { + return false; + }; + + // Only do a hashmap lookup if the entity has a camera affiliation. + aggregate_view_entities.contains(&camera_affiliation) + } + fn shrink_entities(visible_entities: &mut VisibleEntities) { // Check that visible entities capacity() is no more than two times greater than len() let capacity = visible_entities.entities.capacity(); @@ -1934,25 +1975,8 @@ pub fn check_light_mesh_visibility( } // Get the aggregate render groups for all cameras that can see this directional light. - // - We use the camera-aggregate to determine cascade visibility because if any camera can - // see both a directional light and an entity, then it will use the light to illuminate the entity. - // We want illuminated entities to cast shadows as expected, even if an entity and a light technically - // don't 'see' each other - only what the camera sees matters. - aggregate_view_entities.clear(); - aggregate_view_layers.clear(); let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - - for (camera_entity, maybe_view) in views.iter() { - let default_layers = RenderLayers::default(); - let view_layers = maybe_view.map(|v| v.layers()).unwrap_or(&default_layers); - - if !view_mask.intersects_parts(Some(camera_entity), view_layers) { - continue; - } - - aggregate_view_entities.insert(camera_entity); - aggregate_view_layers.merge(view_layers); - } + set_aggregate_view(&mut aggregate_view_layers, &mut aggregate_view_entities, view_mask); for ( entity, @@ -1972,14 +1996,8 @@ pub fn check_light_mesh_visibility( // treat it as a normal entity. The CameraView component controls what the camera can see, while // RenderGroups on the camera entity controls who can see the camera entity. let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_layers.intersects(derived_mask.get().layers()) { - // Only do a hashmap lookup if the entity has a camera affiliation. - if !derived_mask - .camera() - .map_or(false, |c| aggregate_view_entities.contains(&c)) - { - continue; - } + if !aggregate_view_intersects(&aggregate_view_layers, &aggregate_view_entities, derived_mask) { + continue; } // If we have an aabb and transform, do frustum culling @@ -2044,6 +2062,8 @@ pub fn check_light_mesh_visibility( } let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); + set_aggregate_view(&mut aggregate_view_layers, &mut aggregate_view_entities, view_mask); + let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2063,9 +2083,8 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask - .intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) - { + let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); + if !aggregate_view_intersects(&aggregate_view_layers, &aggregate_view_entities, derived_mask) { continue; } @@ -2117,6 +2136,8 @@ pub fn check_light_mesh_visibility( } let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); + set_aggregate_view(&mut aggregate_view_layers, &mut aggregate_view_entities, view_mask); + let light_sphere = Sphere { center: Vec3A::from(transform.translation()), radius: point_light.range, @@ -2136,9 +2157,8 @@ pub fn check_light_mesh_visibility( continue; } - if !view_mask - .intersects(&derive_render_groups(maybe_em_inherited, maybe_entity_mask)) - { + let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); + if !aggregate_view_intersects(&aggregate_view_layers, &aggregate_view_entities, derived_mask) { continue; } diff --git a/examples/3d/render_groups.rs b/examples/3d/render_groups.rs index ff2623e45f574..1c77173ea5f7e 100644 --- a/examples/3d/render_groups.rs +++ b/examples/3d/render_groups.rs @@ -37,7 +37,7 @@ fn setup( specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 1500.0, }, - CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), + CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), )); // Plane @@ -51,7 +51,8 @@ fn setup( commands.spawn(( TextBundle::from_section( "Press '1..3' to toggle mesh render layers\n\ - Press '4..6' to toggle directional light render layers", + Press '4..6' to toggle directional light render layers\n\ + Press '1 and 7' to toggle the spot light render layers", TextStyle { font_size: 20., ..default() @@ -91,7 +92,7 @@ fn setup( transform: Transform::from_xyz(4.0, 25.0, 8.0).looking_at(Vec3::ZERO, Vec3::Y), directional_light: DirectionalLight { shadows_enabled: true, - illuminance: 100000.0, + illuminance: 100_000.0, color: (*color).into(), ..default() }, @@ -100,6 +101,22 @@ fn setup( RenderGroups::from_layer(i + 4), )); } + + // Spawn a spot light that is in the same layer as mesh 1. + // - Notice that the mesh does not cast a shadow when the camera can see the light but not the mesh. + commands.spawn(( + SpotLightBundle { + transform: Transform::from_xyz(- 3.0, 2.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y), + spot_light: SpotLight { + shadows_enabled: true, + intensity: 10_000_000.0, + color: palettes::basic::LIME.into(), + ..default() + }, + ..default() + }, + RenderGroups::from_layers(&[1, 7]), + )); } fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res>) { @@ -125,6 +142,9 @@ fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res Date: Fri, 22 Mar 2024 08:46:36 -0500 Subject: [PATCH 47/54] cleanup example --- examples/3d/render_groups.rs | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/examples/3d/render_groups.rs b/examples/3d/render_groups.rs index 1c77173ea5f7e..2af525b5655c4 100644 --- a/examples/3d/render_groups.rs +++ b/examples/3d/render_groups.rs @@ -37,7 +37,7 @@ fn setup( specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 1500.0, }, - CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), + CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), )); // Plane @@ -51,8 +51,7 @@ fn setup( commands.spawn(( TextBundle::from_section( "Press '1..3' to toggle mesh render layers\n\ - Press '4..6' to toggle directional light render layers\n\ - Press '1 and 7' to toggle the spot light render layers", + Press '4..6' to toggle directional light render layers", TextStyle { font_size: 20., ..default() @@ -101,22 +100,6 @@ fn setup( RenderGroups::from_layer(i + 4), )); } - - // Spawn a spot light that is in the same layer as mesh 1. - // - Notice that the mesh does not cast a shadow when the camera can see the light but not the mesh. - commands.spawn(( - SpotLightBundle { - transform: Transform::from_xyz(- 3.0, 2.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y), - spot_light: SpotLight { - shadows_enabled: true, - intensity: 10_000_000.0, - color: palettes::basic::LIME.into(), - ..default() - }, - ..default() - }, - RenderGroups::from_layers(&[1, 7]), - )); } fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res>) { @@ -142,9 +125,6 @@ fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res Date: Fri, 22 Mar 2024 08:46:48 -0500 Subject: [PATCH 48/54] fmt --- crates/bevy_pbr/src/light/mod.rs | 48 +++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index c3ff261aee0fa..1c6cc8fa9597c 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -15,8 +15,8 @@ use bevy_render::{ renderer::RenderDevice, view::{ derive_render_groups, derive_render_groups_ptr, extract_camera_view, CameraView, - InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, RenderGroupsRef, RenderLayers, - ViewVisibility, VisibleEntities, + InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, RenderGroupsRef, + RenderLayers, ViewVisibility, VisibleEntities, }, }; use bevy_transform::components::{GlobalTransform, Transform}; @@ -1883,11 +1883,9 @@ pub fn check_light_mesh_visibility( // see both a light and an entity, then it will use the light to illuminate the entity. // We want illuminated entities to cast shadows as expected, even if an entity and a light technically // don't 'see' each other. Only what the camera sees matters. - let set_aggregate_view = | - aggregate_view_layers: &mut RenderLayers, - aggregate_view_entities: &mut EntityHashSet, - light_groups: RenderGroupsRef<'_>, - | { + let set_aggregate_view = |aggregate_view_layers: &mut RenderLayers, + aggregate_view_entities: &mut EntityHashSet, + light_groups: RenderGroupsRef<'_>| { aggregate_view_layers.clear(); aggregate_view_entities.clear(); for (camera_entity, maybe_view) in views.iter() { @@ -1976,7 +1974,11 @@ pub fn check_light_mesh_visibility( // Get the aggregate render groups for all cameras that can see this directional light. let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - set_aggregate_view(&mut aggregate_view_layers, &mut aggregate_view_entities, view_mask); + set_aggregate_view( + &mut aggregate_view_layers, + &mut aggregate_view_entities, + view_mask, + ); for ( entity, @@ -1996,7 +1998,11 @@ pub fn check_light_mesh_visibility( // treat it as a normal entity. The CameraView component controls what the camera can see, while // RenderGroups on the camera entity controls who can see the camera entity. let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_intersects(&aggregate_view_layers, &aggregate_view_entities, derived_mask) { + if !aggregate_view_intersects( + &aggregate_view_layers, + &aggregate_view_entities, + derived_mask, + ) { continue; } @@ -2062,7 +2068,11 @@ pub fn check_light_mesh_visibility( } let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - set_aggregate_view(&mut aggregate_view_layers, &mut aggregate_view_entities, view_mask); + set_aggregate_view( + &mut aggregate_view_layers, + &mut aggregate_view_entities, + view_mask, + ); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), @@ -2084,7 +2094,11 @@ pub fn check_light_mesh_visibility( } let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_intersects(&aggregate_view_layers, &aggregate_view_entities, derived_mask) { + if !aggregate_view_intersects( + &aggregate_view_layers, + &aggregate_view_entities, + derived_mask, + ) { continue; } @@ -2136,7 +2150,11 @@ pub fn check_light_mesh_visibility( } let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - set_aggregate_view(&mut aggregate_view_layers, &mut aggregate_view_entities, view_mask); + set_aggregate_view( + &mut aggregate_view_layers, + &mut aggregate_view_entities, + view_mask, + ); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), @@ -2158,7 +2176,11 @@ pub fn check_light_mesh_visibility( } let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_intersects(&aggregate_view_layers, &aggregate_view_entities, derived_mask) { + if !aggregate_view_intersects( + &aggregate_view_layers, + &aggregate_view_entities, + derived_mask, + ) { continue; } From 6b4a2d3e558e765e234defaf0eb5be2eadd68356 Mon Sep 17 00:00:00 2001 From: koe Date: Sat, 23 Mar 2024 15:05:18 -0500 Subject: [PATCH 49/54] add second camera to render groups example --- examples/3d/render_groups.rs | 158 ++++++++++++++++++++++++++++++----- 1 file changed, 135 insertions(+), 23 deletions(-) diff --git a/examples/3d/render_groups.rs b/examples/3d/render_groups.rs index 2af525b5655c4..3a6160b2546e7 100644 --- a/examples/3d/render_groups.rs +++ b/examples/3d/render_groups.rs @@ -4,7 +4,9 @@ use bevy::{ color::palettes, pbr::DirectionalLightShadowMap, prelude::*, + render::camera::Viewport, render::view::{CameraView, PropagateRenderGroups, RenderGroups}, + window::PrimaryWindow, }; fn main() { @@ -12,45 +14,104 @@ fn main() { .insert_resource(DirectionalLightShadowMap { size: 4096 }) .add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, toggle_layers) + .add_systems(Update, (toggle_layers_camera1, toggle_layers_camera2)) .run(); } #[derive(Component)] -struct MovedScene; +struct Camera1; + +#[derive(Component)] +struct Camera2; fn setup( mut commands: Commands, asset_server: Res, mut meshes: ResMut>, mut materials: ResMut>, + window: Query<&Window, With>, ) { - // Camera + // Camera 1 + let window = window.single(); + let camera1 = commands + .spawn(( + Camera3dBundle { + camera: Camera { + viewport: Some(Viewport { + physical_position: UVec2 { + x: window.physical_width() / 4, + y: 0, + }, + physical_size: UVec2 { + x: window.physical_width() / 2, + y: window.physical_height() / 2, + }, + ..Default::default() + }), + ..Default::default() + }, + transform: Transform::from_xyz(0., 1.4, 2.0) + .looking_at(Vec3::new(0., 0.3, 0.0), Vec3::Y), + ..default() + }, + EnvironmentMapLight { + diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + intensity: 1500.0, + }, + CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), + Camera1, + )) + .id(); + + // Camera 2 + let camera2 = commands + .spawn(( + Camera3dBundle { + camera: Camera { + order: 1, + viewport: Some(Viewport { + physical_position: UVec2 { + x: window.physical_width() / 4, + y: window.physical_height() / 2, + }, + physical_size: UVec2 { + x: window.physical_width() / 2, + y: window.physical_height() / 2, + }, + ..Default::default() + }), + ..Default::default() + }, + transform: Transform::from_xyz(0., 1.4, 2.0) + .looking_at(Vec3::new(0., 0.3, 0.0), Vec3::Y), + ..default() + }, + EnvironmentMapLight { + diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), + specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), + intensity: 1500.0, + }, + CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), + Camera2, + )) + .id(); + + // Plane commands.spawn(( - Camera3dBundle { - transform: Transform::from_xyz(0., 1.4, 2.0) - .looking_at(Vec3::new(0., 0.3, 0.0), Vec3::Y), + PbrBundle { + mesh: meshes.add(Plane3d::default().mesh().size(5000.0, 5000.0)), + material: materials.add(Color::srgb(0.3, 0.5, 0.3)), ..default() }, - EnvironmentMapLight { - diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"), - specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), - intensity: 1500.0, - }, - CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), + RenderGroups::from_layer(0), )); - // Plane - commands.spawn(PbrBundle { - mesh: meshes.add(Plane3d::default().mesh().size(5000.0, 5000.0)), - material: materials.add(Color::srgb(0.3, 0.5, 0.3)), - ..default() - }); - - // Text + // Text (camera 1) commands.spawn(( TextBundle::from_section( - "Press '1..3' to toggle mesh render layers\n\ + "Camera 1:\n\ + Press '1..3' to toggle mesh render layers\n\ Press '4..6' to toggle directional light render layers", TextStyle { font_size: 20., @@ -63,7 +124,27 @@ fn setup( left: Val::Px(12.0), ..default() }), - RenderGroups::from_layer(0), + TargetCamera(camera1), + )); + + // Text (camera 2) + commands.spawn(( + TextBundle::from_section( + "Camera 2:\n\ + Press 'Q/W/E' to toggle mesh render layers\n\ + Press 'R/T/Y' to toggle directional light render layers", + TextStyle { + font_size: 20., + ..default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(12.0), + left: Val::Px(12.0), + ..default() + }), + TargetCamera(camera2), )); // Spawn three copies of the scene, each with a different render group. @@ -102,7 +183,10 @@ fn setup( } } -fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res>) { +fn toggle_layers_camera1( + mut query_camera: Query<&mut CameraView, With>, + keyboard: Res>, +) { let Ok(mut camera_view) = query_camera.get_single_mut() else { return; }; @@ -127,6 +211,34 @@ fn toggle_layers(mut query_camera: Query<&mut CameraView>, keyboard: Res>, + keyboard: Res>, +) { + let Ok(mut camera_view) = query_camera.get_single_mut() else { + return; + }; + + if keyboard.just_pressed(KeyCode::KeyQ) { + toggle_camera_layer(&mut camera_view, 1); + } + if keyboard.just_pressed(KeyCode::KeyW) { + toggle_camera_layer(&mut camera_view, 2); + } + if keyboard.just_pressed(KeyCode::KeyE) { + toggle_camera_layer(&mut camera_view, 3); + } + if keyboard.just_pressed(KeyCode::KeyR) { + toggle_camera_layer(&mut camera_view, 4); + } + if keyboard.just_pressed(KeyCode::KeyT) { + toggle_camera_layer(&mut camera_view, 5); + } + if keyboard.just_pressed(KeyCode::KeyY) { + toggle_camera_layer(&mut camera_view, 6); + } +} + fn toggle_camera_layer(camera_view: &mut CameraView, layer: usize) { if camera_view.contains_layer(layer) { camera_view.remove(layer); From 19bacda2a6c94a0c79d28741f5ae24c4c5778092 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 25 Mar 2024 20:28:53 -0500 Subject: [PATCH 50/54] simplify: RenderGroups -> RenderLayers, CameraView -> CameraLayer, lights now intersect entities again but warn if they have more than one RenderLayer --- Cargo.toml | 6 +- assets/shaders/custom_material.vert | 2 +- .../src/ui_debug_overlay/mod.rs | 6 +- crates/bevy_gizmos/src/config.rs | 10 +- crates/bevy_gizmos/src/pipeline_2d.rs | 14 +- crates/bevy_gizmos/src/pipeline_3d.rs | 14 +- crates/bevy_pbr/src/light/mod.rs | 180 ++--- crates/bevy_pbr/src/render/light.rs | 18 +- crates/bevy_render/src/camera/camera.rs | 8 +- crates/bevy_render/src/view/mod.rs | 3 +- crates/bevy_render/src/view/visibility/mod.rs | 34 +- ...r_groups.rs => propagate_render_layers.rs} | 695 +++++++++--------- .../{render_groups.rs => render_layers.rs} | 475 ++++-------- crates/bevy_ui/src/render/render_pass.rs | 4 +- examples/2d/pixel_grid_snap.rs | 14 +- .../3d/{render_groups.rs => render_layers.rs} | 32 +- examples/3d/render_to_texture.rs | 18 +- examples/README.md | 2 +- 18 files changed, 629 insertions(+), 906 deletions(-) rename crates/bevy_render/src/view/visibility/{propagate_render_groups.rs => propagate_render_layers.rs} (60%) rename crates/bevy_render/src/view/visibility/{render_groups.rs => render_layers.rs} (52%) rename examples/3d/{render_groups.rs => render_layers.rs} (91%) diff --git a/Cargo.toml b/Cargo.toml index 76cd378c16981..8023f0ddaddb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1998,9 +1998,9 @@ name = "render_groups" path = "examples/3d/render_groups.rs" doc-scrape-examples = true -[package.metadata.example.render_groups] -name = "Render Groups" -description = "Spawning multiple copies of a scene from a GLTF file, each with a different render group" +[package.metadata.example.render_layers] +name = "Render Layers" +description = "Load a scene from a glTF file and render it with different render layers." category = "3D Rendering" wasm = true diff --git a/assets/shaders/custom_material.vert b/assets/shaders/custom_material.vert index 91d660ec3194c..88bde8c046cd9 100644 --- a/assets/shaders/custom_material.vert +++ b/assets/shaders/custom_material.vert @@ -6,7 +6,7 @@ layout(location = 2) in vec2 Vertex_Uv; layout(location = 0) out vec2 v_Uv; -layout(set = 0, binding = 0) uniform CameraViewProj { +layout(set = 0, binding = 0) uniform CameraLayerProj { mat4 ViewProj; mat4 View; mat4 InverseView; diff --git a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs index 497783dfbbc3d..a7be3eca61dad 100644 --- a/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs +++ b/crates/bevy_dev_tools/src/ui_debug_overlay/mod.rs @@ -12,7 +12,7 @@ use bevy_math::{Vec2, Vec3Swizzles}; use bevy_render::{ camera::RenderTarget, prelude::*, - view::{RenderGroups, RenderLayer, VisibilitySystems}, + view::{RenderLayer, RenderLayers, VisibilitySystems}, }; use bevy_transform::{prelude::GlobalTransform, TransformSystem}; use bevy_ui::{DefaultUiCamera, Display, Node, Style, TargetCamera, UiScale}; @@ -101,7 +101,7 @@ fn update_debug_camera( }, ..default() }, - RenderGroups::from_layer(LAYOUT_DEBUG_LAYER), + RenderLayers::from_layer(LAYOUT_DEBUG_LAYER), DebugOverlayCamera, Name::new("Layout Debug Camera"), )) @@ -109,7 +109,7 @@ fn update_debug_camera( }; if let Some((config, _)) = gizmo_config.get_config_mut_dyn(&TypeId::of::()) { config.enabled = true; - config.render_groups = RenderGroups::from_layer(LAYOUT_DEBUG_LAYER); + config.render_layers = RenderLayers::from_layer(LAYOUT_DEBUG_LAYER); } let cam = *options.layout_gizmos_camera.get_or_insert_with(spawn_cam); let Ok(mut cam) = debug_cams.get_mut(cam) else { diff --git a/crates/bevy_gizmos/src/config.rs b/crates/bevy_gizmos/src/config.rs index 285bbc1cb41b0..c3030d9a3488f 100644 --- a/crates/bevy_gizmos/src/config.rs +++ b/crates/bevy_gizmos/src/config.rs @@ -5,7 +5,7 @@ pub use bevy_gizmos_macros::GizmoConfigGroup; use bevy_ecs::{component::Component, reflect::ReflectResource, system::Resource}; use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypePath}; -use bevy_render::view::RenderGroups; +use bevy_render::view::RenderLayers; use bevy_utils::TypeIdMap; use core::panic; use std::{ @@ -164,7 +164,7 @@ pub struct GizmoConfig { /// Describes which rendering layers gizmos will be rendered to. /// /// Gizmos will only be rendered to cameras with intersecting layers. - pub render_groups: RenderGroups, + pub render_layers: RenderLayers, /// Describe how lines should join pub line_joints: GizmoLineJoint, @@ -178,7 +178,7 @@ impl Default for GizmoConfig { line_perspective: false, line_style: GizmoLineStyle::Solid, depth_bias: 0., - render_groups: Default::default(), + render_layers: Default::default(), line_joints: GizmoLineJoint::None, } @@ -189,7 +189,7 @@ impl Default for GizmoConfig { pub(crate) struct GizmoMeshConfig { pub line_perspective: bool, pub line_style: GizmoLineStyle, - pub render_groups: RenderGroups, + pub render_layers: RenderLayers, } impl From<&GizmoConfig> for GizmoMeshConfig { @@ -197,7 +197,7 @@ impl From<&GizmoConfig> for GizmoMeshConfig { GizmoMeshConfig { line_perspective: item.line_perspective, line_style: item.line_style, - render_groups: item.render_groups.clone(), + render_layers: item.render_layers.clone(), } } } diff --git a/crates/bevy_gizmos/src/pipeline_2d.rs b/crates/bevy_gizmos/src/pipeline_2d.rs index 055bbb9e1ed5e..a29141ff3904f 100644 --- a/crates/bevy_gizmos/src/pipeline_2d.rs +++ b/crates/bevy_gizmos/src/pipeline_2d.rs @@ -20,7 +20,7 @@ use bevy_render::{ render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase}, render_resource::*, texture::BevyDefault, - view::{ExtractedRenderGroups, ExtractedView, Msaa, ViewTarget}, + view::{ExtractedRenderLayers, ExtractedView, Msaa, ViewTarget}, Render, RenderApp, RenderSet, }; use bevy_sprite::{Mesh2dPipeline, Mesh2dPipelineKey, SetMesh2dViewBindGroup}; @@ -257,17 +257,17 @@ fn queue_line_gizmos_2d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&ExtractedRenderGroups>, + Option<&ExtractedRenderLayers>, )>, ) { let draw_function = draw_functions.read().get_id::().unwrap(); - for (view, mut transparent_phase, render_groups) in &mut views { + for (view, mut transparent_phase, render_layers) in &mut views { let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) | Mesh2dPipelineKey::from_hdr(view.hdr); for (entity, handle, config) in &line_gizmos { - if !config.render_groups.intersects_extracted(render_groups) { + if !config.render_layers.intersects_extracted(render_layers) { continue; } @@ -309,7 +309,7 @@ fn queue_line_joint_gizmos_2d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&ExtractedRenderGroups>, + Option<&ExtractedRenderLayers>, )>, ) { let draw_function = draw_functions @@ -317,12 +317,12 @@ fn queue_line_joint_gizmos_2d( .get_id::() .unwrap(); - for (view, mut transparent_phase, render_groups) in &mut views { + for (view, mut transparent_phase, render_layers) in &mut views { let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples()) | Mesh2dPipelineKey::from_hdr(view.hdr); for (entity, handle, config) in &line_gizmos { - if !config.render_groups.intersects_extracted(render_groups) { + if !config.render_layers.intersects_extracted(render_layers) { continue; } diff --git a/crates/bevy_gizmos/src/pipeline_3d.rs b/crates/bevy_gizmos/src/pipeline_3d.rs index 1a06c8d6ed012..80c6e04835cc1 100644 --- a/crates/bevy_gizmos/src/pipeline_3d.rs +++ b/crates/bevy_gizmos/src/pipeline_3d.rs @@ -24,7 +24,7 @@ use bevy_render::{ render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase}, render_resource::*, texture::BevyDefault, - view::{ExtractedRenderGroups, ExtractedView, Msaa, ViewTarget}, + view::{ExtractedRenderLayers, ExtractedView, Msaa, ViewTarget}, Render, RenderApp, RenderSet, }; use bevy_utils::tracing::error; @@ -282,7 +282,7 @@ fn queue_line_gizmos_3d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&ExtractedRenderGroups>, + Option<&ExtractedRenderLayers>, ( Has, Has, @@ -296,7 +296,7 @@ fn queue_line_gizmos_3d( for ( view, mut transparent_phase, - render_groups, + render_layers, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut views { @@ -320,7 +320,7 @@ fn queue_line_gizmos_3d( } for (entity, handle, config) in &line_gizmos { - if !config.render_groups.intersects_extracted(render_groups) { + if !config.render_layers.intersects_extracted(render_layers) { continue; } @@ -363,7 +363,7 @@ fn queue_line_joint_gizmos_3d( mut views: Query<( &ExtractedView, &mut SortedRenderPhase, - Option<&ExtractedRenderGroups>, + Option<&ExtractedRenderLayers>, ( Has, Has, @@ -380,7 +380,7 @@ fn queue_line_joint_gizmos_3d( for ( view, mut transparent_phase, - render_groups, + render_layers, (normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass), ) in &mut views { @@ -404,7 +404,7 @@ fn queue_line_joint_gizmos_3d( } for (entity, handle, config) in &line_gizmos { - if !config.render_groups.intersects_extracted(render_groups) { + if !config.render_layers.intersects_extracted(render_layers) { continue; } diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index 1c6cc8fa9597c..fdfdb724c5dce 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use bevy_ecs::entity::{EntityHashMap, EntityHashSet}; +use bevy_ecs::entity::EntityHashMap; use bevy_ecs::prelude::*; use bevy_math::{ AspectRatio, Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles, @@ -14,13 +14,13 @@ use bevy_render::{ render_resource::BufferBindingType, renderer::RenderDevice, view::{ - derive_render_groups, derive_render_groups_ptr, extract_camera_view, CameraView, - InheritedRenderGroups, InheritedVisibility, RenderGroups, RenderGroupsPtr, RenderGroupsRef, - RenderLayers, ViewVisibility, VisibleEntities, + derive_render_layers, derive_render_layers_ptr, extract_camera_layer, CameraLayer, + InheritedRenderLayers, InheritedVisibility, RenderLayers, RenderLayersPtr, RenderLayersRef, + ViewVisibility, VisibleEntities, }, }; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::tracing::warn; +use bevy_utils::{tracing::warn, warn_once}; use crate::*; @@ -1014,7 +1014,7 @@ pub(crate) struct PointLightAssignmentData { range: f32, shadows_enabled: bool, spot_light_angle: Option, - render_groups: RenderGroupsPtr, + render_layers: RenderLayersPtr, } impl PointLightAssignmentData { @@ -1062,23 +1062,23 @@ pub(crate) fn assign_lights_to_clusters( &Frustum, &ClusterConfig, &mut Clusters, - Option<&CameraView>, + Option<&CameraLayer>, Option<&mut VisiblePointLights>, )>, point_lights_query: Query<( Entity, &GlobalTransform, &PointLight, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, &ViewVisibility, )>, spot_lights_query: Query<( Entity, &GlobalTransform, &SpotLight, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, &ViewVisibility, )>, mut lights: Local>, @@ -1105,7 +1105,10 @@ pub(crate) fn assign_lights_to_clusters( shadows_enabled: point_light.shadows_enabled, range: point_light.range, spot_light_angle: None, - render_groups: derive_render_groups_ptr(maybe_inherited, maybe_groups), + render_layers: derive_render_layers_ptr_for_light( + maybe_inherited, + maybe_groups, + ), } }, ), @@ -1122,7 +1125,10 @@ pub(crate) fn assign_lights_to_clusters( shadows_enabled: spot_light.shadows_enabled, range: spot_light.range, spot_light_angle: Some(spot_light.outer_angle), - render_groups: derive_render_groups_ptr(maybe_inherited, maybe_groups), + render_layers: derive_render_layers_ptr_for_light( + maybe_inherited, + maybe_groups, + ), } }, ), @@ -1196,7 +1202,7 @@ pub(crate) fn assign_lights_to_clusters( mut visible_lights, ) in &mut views { - let view_groups = extract_camera_view(view_entity, maybe_view); + let view_layer = extract_camera_layer(maybe_view); let clusters = clusters.into_inner(); @@ -1422,8 +1428,8 @@ pub(crate) fn assign_lights_to_clusters( // check if the light groups overlap the view groups // SAFETY: `lights` is cleared at the start of this system call, and is populated from // immutable queries. - let light_rendergroups = unsafe { light.render_groups.get() }; - if !view_groups.intersects(light_rendergroups) { + let light_renderlayers = unsafe { light.render_layers.get() }; + if !view_layer.intersects(light_renderlayers) { continue; } @@ -1835,33 +1841,30 @@ pub fn update_spot_light_frusta( #[allow(clippy::too_many_arguments)] pub fn check_light_mesh_visibility( - mut aggregate_view_layers: Local, - mut aggregate_view_entities: Local, visible_point_lights: Query<&VisiblePointLights>, - views: Query<(Entity, Option<&CameraView>), With>, mut point_lights: Query<( &PointLight, &GlobalTransform, &CubemapFrusta, &mut CubemapVisibleEntities, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, )>, mut spot_lights: Query<( &SpotLight, &GlobalTransform, &Frustum, &mut VisibleEntities, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, )>, mut directional_lights: Query< ( &DirectionalLight, &CascadesFrusta, &mut CascadesVisibleEntities, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, &mut ViewVisibility, ), Without, @@ -1871,53 +1874,14 @@ pub fn check_light_mesh_visibility( Entity, &InheritedVisibility, &mut ViewVisibility, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, Option<&Aabb>, Option<&GlobalTransform>, ), (Without, Without), >, ) { - // We use camera-aggregates to determine cascade visibility because if any camera can - // see both a light and an entity, then it will use the light to illuminate the entity. - // We want illuminated entities to cast shadows as expected, even if an entity and a light technically - // don't 'see' each other. Only what the camera sees matters. - let set_aggregate_view = |aggregate_view_layers: &mut RenderLayers, - aggregate_view_entities: &mut EntityHashSet, - light_groups: RenderGroupsRef<'_>| { - aggregate_view_layers.clear(); - aggregate_view_entities.clear(); - for (camera_entity, maybe_view) in views.iter() { - let default_layers = RenderLayers::default(); - let view_layers = maybe_view.map(|v| v.layers()).unwrap_or(&default_layers); - - if !light_groups.intersects_parts(Some(camera_entity), view_layers) { - continue; - } - - aggregate_view_layers.merge(view_layers); - aggregate_view_entities.insert(camera_entity); - } - }; - - fn aggregate_view_intersects( - aggregate_view_layers: &RenderLayers, - aggregate_view_entities: &EntityHashSet, - entity_groups: RenderGroupsRef<'_>, - ) -> bool { - if aggregate_view_layers.intersects(entity_groups.get().layers()) { - return true; - } - - let Some(camera_affiliation) = entity_groups.camera() else { - return false; - }; - - // Only do a hashmap lookup if the entity has a camera affiliation. - aggregate_view_entities.contains(&camera_affiliation) - } - fn shrink_entities(visible_entities: &mut VisibleEntities) { // Check that visible entities capacity() is no more than two times greater than len() let capacity = visible_entities.entities.capacity(); @@ -1972,13 +1936,8 @@ pub fn check_light_mesh_visibility( continue; } - // Get the aggregate render groups for all cameras that can see this directional light. - let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - set_aggregate_view( - &mut aggregate_view_layers, - &mut aggregate_view_entities, - view_mask, - ); + // Get render layers for this light. + let view_mask = derive_render_layers_for_light(maybe_vm_inherited, maybe_view_mask); for ( entity, @@ -1995,14 +1954,9 @@ pub fn check_light_mesh_visibility( } // Note: A visible entity may have a camera on it, but in this case we ignore the camera and - // treat it as a normal entity. The CameraView component controls what the camera can see, while - // RenderGroups on the camera entity controls who can see the camera entity. - let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_intersects( - &aggregate_view_layers, - &aggregate_view_entities, - derived_mask, - ) { + // treat it as a normal entity. The CameraLayer component controls what the camera can see, while + // RenderLayers on the camera entity controls who can see the camera entity. + if !view_mask.intersects(&derive_render_layers(maybe_em_inherited, maybe_entity_mask)) { continue; } @@ -2067,12 +2021,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - set_aggregate_view( - &mut aggregate_view_layers, - &mut aggregate_view_entities, - view_mask, - ); + let view_mask = derive_render_layers_for_light(maybe_vm_inherited, maybe_view_mask); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), @@ -2093,12 +2042,9 @@ pub fn check_light_mesh_visibility( continue; } - let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_intersects( - &aggregate_view_layers, - &aggregate_view_entities, - derived_mask, - ) { + if !view_mask + .intersects(&derive_render_layers(maybe_em_inherited, maybe_entity_mask)) + { continue; } @@ -2149,12 +2095,7 @@ pub fn check_light_mesh_visibility( continue; } - let view_mask = derive_render_groups(maybe_vm_inherited, maybe_view_mask); - set_aggregate_view( - &mut aggregate_view_layers, - &mut aggregate_view_entities, - view_mask, - ); + let view_mask = derive_render_layers_for_light(maybe_vm_inherited, maybe_view_mask); let light_sphere = Sphere { center: Vec3A::from(transform.translation()), @@ -2175,12 +2116,9 @@ pub fn check_light_mesh_visibility( continue; } - let derived_mask = derive_render_groups(maybe_em_inherited, maybe_entity_mask); - if !aggregate_view_intersects( - &aggregate_view_layers, - &aggregate_view_entities, - derived_mask, - ) { + if !view_mask + .intersects(&derive_render_layers(maybe_em_inherited, maybe_entity_mask)) + { continue; } @@ -2208,6 +2146,38 @@ pub fn check_light_mesh_visibility( } } +fn derive_render_layers_ptr_for_light( + maybe_inherited: Option<&InheritedRenderLayers>, + maybe_layers: Option<&RenderLayers>, +) -> RenderLayersPtr { + let render_layers = derive_render_layers_ptr(maybe_inherited, maybe_layers); + // SAFETY: The pointer points to references within the lifetime of this function. + let len = unsafe { render_layers.get().len() }; + if len > 1 { + warn_once!( + "light detected with more than one RenderLayer, this may cause unexpected \ + behavior (missing shadows or shadows where they should not exist)" + ); + } + + render_layers +} + +fn derive_render_layers_for_light<'a>( + maybe_inherited: Option<&'a InheritedRenderLayers>, + maybe_layers: Option<&'a RenderLayers>, +) -> RenderLayersRef<'a> { + let render_layers = derive_render_layers(maybe_inherited, maybe_layers); + if render_layers.get().len() > 1 { + warn_once!( + "light detected with more than one RenderLayer, this may cause unexpected \ + behavior (missing shadows or shadows where they should not exist)" + ); + } + + render_layers +} + #[cfg(test)] mod test { use super::*; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 580b05c478b26..78a9fea3c0c7f 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -15,8 +15,8 @@ use bevy_render::{ renderer::{RenderContext, RenderDevice, RenderQueue}, texture::*, view::{ - extract_render_groups, ExtractedRenderGroups, ExtractedView, InheritedRenderGroups, - RenderGroups, ViewVisibility, VisibleEntities, + extract_render_layers, ExtractedRenderLayers, ExtractedView, InheritedRenderLayers, + RenderLayers, ViewVisibility, VisibleEntities, }, Extract, }; @@ -54,7 +54,7 @@ pub struct ExtractedDirectionalLight { pub cascade_shadow_config: CascadeShadowConfig, pub cascades: EntityHashMap>, pub frusta: EntityHashMap>, - pub render_groups: ExtractedRenderGroups, + pub render_layers: ExtractedRenderLayers, } #[derive(Copy, Clone, ShaderType, Default, Debug)] @@ -348,8 +348,8 @@ pub fn extract_lights( &CascadesFrusta, &GlobalTransform, &ViewVisibility, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, ), Without, >, @@ -493,7 +493,7 @@ pub fn extract_lights( cascade_shadow_config: cascade_config.clone(), cascades: cascades.cascades.clone(), frusta: frusta.frusta.clone(), - render_groups: extract_render_groups(maybe_inherited, maybe_groups), + render_layers: extract_render_layers(maybe_inherited, maybe_groups), }, render_visible_entities, )); @@ -693,7 +693,7 @@ pub fn prepare_lights( Entity, &ExtractedView, &ExtractedClusterConfig, - &ExtractedRenderGroups, + &ExtractedRenderLayers, ), With>, >, @@ -941,7 +941,7 @@ pub fn prepare_lights( .write_buffer(&render_device, &render_queue); // set up light data for each view - for (entity, extracted_view, clusters, extracted_render_groups) in &views { + for (entity, extracted_view, clusters, extracted_render_layers) in &views { let point_light_depth_texture = texture_cache.get( &render_device, TextureDescriptor { @@ -1146,7 +1146,7 @@ pub fn prepare_lights( let gpu_light = &mut gpu_lights.directional_lights[light_index]; // Check if the light intersects with the view. - if !extracted_render_groups.intersects(&light.render_groups) { + if !extracted_render_layers.intersects(&light.render_layers) { gpu_light.skip = 1u32; continue; } diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 13394715ee454..fea5d8700a2a7 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -7,7 +7,7 @@ use crate::{ render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_resource::TextureView, view::{ - extract_camera_view, CameraView, ColorGrading, ExtractedView, ExtractedWindows, + extract_camera_layer, CameraLayer, ColorGrading, ExtractedView, ExtractedWindows, VisibleEntities, }, Extract, @@ -814,7 +814,7 @@ pub fn extract_cameras( Option<&ColorGrading>, Option<&Exposure>, Option<&TemporalJitter>, - Option<&CameraView>, + Option<&CameraLayer>, Option<&Projection>, )>, >, @@ -831,7 +831,7 @@ pub fn extract_cameras( color_grading, exposure, temporal_jitter, - camera_view, + camera_layer, projection, ) in query.iter() { @@ -891,7 +891,7 @@ pub fn extract_cameras( }, visible_entities.clone(), *frustum, - extract_camera_view(entity, camera_view), + extract_camera_layer(camera_layer), )); if let Some(temporal_jitter) = temporal_jitter { diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 616c9001c6f9f..7a847c0f78b01 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -48,8 +48,7 @@ impl Plugin for ViewPlugin { .register_type::() .register_type::() .register_type::() - .register_type::() - .register_type::() + .register_type::() .register_type::() .register_type::() .register_type::() diff --git a/crates/bevy_render/src/view/visibility/mod.rs b/crates/bevy_render/src/view/visibility/mod.rs index ff7c59a477231..e0399c4d249ca 100644 --- a/crates/bevy_render/src/view/visibility/mod.rs +++ b/crates/bevy_render/src/view/visibility/mod.rs @@ -1,9 +1,9 @@ -mod propagate_render_groups; -mod render_groups; +mod propagate_render_layers; +mod render_layers; use bevy_derive::Deref; -pub use propagate_render_groups::*; -pub use render_groups::*; +pub use propagate_render_layers::*; +pub use render_layers::*; use bevy_app::{Plugin, PostUpdate}; use bevy_asset::{Assets, Handle}; @@ -201,7 +201,7 @@ pub enum VisibilitySystems { UpdatePerspectiveFrusta, /// Label for the [`update_frusta`] system. UpdateProjectionFrusta, - /// Label for the system propagating [`InheritedVisibility`] and applying [`PropagateRenderGroups`] + /// Label for the system propagating [`InheritedVisibility`] and applying [`PropagateRenderLayers`] /// in a [`hierarchy`](bevy_hierarchy). VisibilityPropagate, /// Label for the [`check_visibility`] system updating [`ViewVisibility`] @@ -215,10 +215,10 @@ impl Plugin for VisibilityPlugin { fn build(&self, app: &mut bevy_app::App) { use VisibilitySystems::*; - app.add_plugins(PropagateRenderGroupsPlugin) + app.add_plugins(PropagateRenderLayersPlugin) .configure_sets( PostUpdate, - PropagateRenderGroupsSet.in_set(VisibilityPropagate), + PropagateRenderLayersSet.in_set(VisibilityPropagate), ) .add_systems( PostUpdate, @@ -381,31 +381,29 @@ fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { pub fn check_visibility( mut thread_queues: Local>>, mut view_query: Query<( - Entity, &mut VisibleEntities, &Frustum, - Option<&CameraView>, + Option<&CameraLayer>, &Camera, )>, mut visible_aabb_query: Query<( Entity, &InheritedVisibility, &mut ViewVisibility, - Option<&RenderGroups>, - Option<&InheritedRenderGroups>, + Option<&RenderLayers>, + Option<&InheritedRenderLayers>, Option<&Aabb>, &GlobalTransform, Has, )>, deterministic_rendering_config: Res, ) { - for (camera_entity, mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query - { + for (mut visible_entities, frustum, maybe_camera_view, camera) in &mut view_query { if !camera.is_active { continue; } - let default_camera_view = CameraView::default(); + let default_camera_view = CameraLayer::default(); let camera_view = maybe_camera_view.unwrap_or(&default_camera_view); visible_entities.entities.clear(); @@ -414,7 +412,7 @@ pub fn check_visibility( entity, inherited_visibility, mut view_visibility, - maybe_groups, + maybe_layers, maybe_inherited, maybe_model_aabb, transform, @@ -428,10 +426,8 @@ pub fn check_visibility( } // Check if this camera can see the entity. - if !camera_view.entity_is_visible( - camera_entity, - &derive_render_groups(maybe_inherited, maybe_groups), - ) { + if !camera_view.entity_is_visible(&derive_render_layers(maybe_inherited, maybe_layers)) + { return; } diff --git a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs b/crates/bevy_render/src/view/visibility/propagate_render_layers.rs similarity index 60% rename from crates/bevy_render/src/view/visibility/propagate_render_groups.rs rename to crates/bevy_render/src/view/visibility/propagate_render_layers.rs index 91b8aa3760890..671020e0071a0 100644 --- a/crates/bevy_render/src/view/visibility/propagate_render_groups.rs +++ b/crates/bevy_render/src/view/visibility/propagate_render_layers.rs @@ -1,9 +1,9 @@ /* -# Notes on propagation algorithm for PropagateRenderGroups +# Notes on propagation algorithm for PropagateRenderLayers -The propagation algorithm is in control of updating InheritedRenderGroups on the descendents of -entities with PropagateRenderGroups. It must take into account changes in propagated values, -changes in RenderGroups on descendents, the possibility of entities gaining/losing PropagateRenderGroups, +The propagation algorithm is in control of updating InheritedRenderLayers on the descendents of +entities with PropagateRenderLayers. It must take into account changes in propagated values, +changes in RenderLayers on descendents, the possibility of entities gaining/losing PropagateRenderLayers, and all potential hierarchy mutations (which can occur simultaneously with the other factors). At the same time, the algorithm must be efficient. It should not update entities more than once (unless @@ -18,20 +18,20 @@ described below. ## Contributing factors Dimensions: -- InheritedRenderGroups and PropagateRenderGroups should not exist on the same entity -- RenderGroups can be added/changed/removed -- CameraView can be added/changed/removed -- PropagateRenderGroups can be added/changed/removed +- InheritedRenderLayers and PropagateRenderLayers should not exist on the same entity +- RenderLayers can be added/changed/removed +- CameraLayer can be added/changed/removed +- PropagateRenderLayers can be added/changed/removed - Camera can be added/removed (changes don't matter) -- Entities can gain a parent with PropagateRenderGroups -- Entities can gain a parent without PropagateRenderGroups - - The entity may or may not have InheritedRenderGroups already - - The parent may or may not have InheritedRenderGroups already +- Entities can gain a parent with PropagateRenderLayers +- Entities can gain a parent without PropagateRenderLayers + - The entity may or may not have InheritedRenderLayers already + - The parent may or may not have InheritedRenderLayers already - Entities can become un-parented Actions: -- This algorithm can: add/remove/change InheritedRenderGroups - - When updating an entity, we always compute a fresh InheritedRenderGroups::computed value. +- This algorithm can: add/remove/change InheritedRenderLayers + - When updating an entity, we always compute a fresh InheritedRenderLayers::computed value. ## Propagation algorithm @@ -43,59 +43,59 @@ We take note of a couple 'problems' that are challenging edge cases, and how the ### Propagator Repair -- If an entity has both InheritedRenderGroups and PropagateRenderGroups, then remove its InheritedRenderGroups. +- If an entity has both InheritedRenderLayers and PropagateRenderLayers, then remove its InheritedRenderLayers. - SYSTEM: clean_propagators - If a propagator has an updated or new propagate-value, then propagate it. Stop propagating only if another propagator is encountered. - SYSTEM: propagate_updated_propagators - - Possible change sources: RenderGroups (add/remove/change), CameraView (add/remove/change), Camera (add/remove), - PropagateRenderGroups (add/change). + - Possible change sources: RenderLayers (add/remove/change), CameraLayer (add/remove/change), Camera (add/remove), + PropagateRenderLayers (add/change). - If a propagator gains non-propagator children, then propagate it to the new children. Stop propagating if the propagator entity is already known to a descendent (do not mark those descendents as updated), or if another propagator is encountered. - SYSTEM: propagate_to_new_children - PROBLEM: If a new entity is inserted between a propagator and another entity, then the propagator will run in this step, and the new entity's non-propagator children will run in "If a non-propagator entity is parented to a - non-propagator". This step will insert an InheritedRenderGroups component to the new entity, but it *won't* be + non-propagator". This step will insert an InheritedRenderLayers component to the new entity, but it *won't* be added to the pre-existing entity which already records the propagator. When the pre-eisting entity gets updated - because of being parented to a non-propagator, the parent's InheritedRenderGroups component won't be available + because of being parented to a non-propagator, the parent's InheritedRenderLayers component won't be available since its insertion is deferred. - SOLUTION: See "If a non-propagator entity is parented to a non-propagator". ### Non-Propagator Hierarchy Repair -- If a non-propagator entity with InheritedRenderGroups is un-parented, then remove InheritedRenderGroups from the +- If a non-propagator entity with InheritedRenderLayers is un-parented, then remove InheritedRenderLayers from the entity and its children (stopping at propagators). - SYSTEM: handle_orphaned_nonpropagators - - Iterate all children even if one without InheritedRenderGroups is encountered. This ensures 'gaps' caused - by hierarchy changes won't cause problems. For example, an entity without InheritedRenderGroups parented to a + - Iterate all children even if one without InheritedRenderLayers is encountered. This ensures 'gaps' caused + by hierarchy changes won't cause problems. For example, an entity without InheritedRenderLayers parented to a descendent of an un-parented entity will want to pull inheritance from its parent, but removal of - InheritedRenderGroups is deferred so the component it would access would be stale. -- If an entity loses a PropagateRenderGroups component, then inherit its parent's propagator entity, and propagate + InheritedRenderLayers is deferred so the component it would access would be stale. +- If an entity loses a PropagateRenderLayers component, then inherit its parent's propagator entity, and propagate that to its own children (stopping when a propagator is encountered or if an entity is non-updated and has an -InheritedRenderGroups that matches the propagator). If the parent isn't a propagator and doesn't -have InheritedRenderGroups (or there is no parent), then remove InheritedRenderGroups from the entity and its -children (stopping at propagators and non-updated descendents without InheritedRenderGroups). Skip already-updated -entities that lost PropagateRenderGroups. +InheritedRenderLayers that matches the propagator). If the parent isn't a propagator and doesn't +have InheritedRenderLayers (or there is no parent), then remove InheritedRenderLayers from the entity and its +children (stopping at propagators and non-updated descendents without InheritedRenderLayers). Skip already-updated +entities that lost PropagateRenderLayers. - SYSTEM: handle_lost_propagator - The goal of this step is to update the span of entities starting at the entity that lost a - PropagateRenderGroups component, and ending at entities that aren't potentially-invalid. + PropagateRenderLayers component, and ending at entities that aren't potentially-invalid. - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator - stays the same (see "If a propagator gains non-propagator children") (this can only happen if InheritedRenderGroups - was manually inserted by a user, since the entity would not have had InheritedRenderGroups in the previous tick - because we force-remove it if an entity has PropagateRenderGroups). In that case, instead of using the parent's - InheritedRenderGroups, recompute this entity's InheritedRenderGroups from its existing propagator (we assume it + stays the same (see "If a propagator gains non-propagator children") (this can only happen if InheritedRenderLayers + was manually inserted by a user, since the entity would not have had InheritedRenderLayers in the previous tick + because we force-remove it if an entity has PropagateRenderLayers). In that case, instead of using the parent's + InheritedRenderLayers, recompute this entity's InheritedRenderLayers from its existing propagator (we assume it is invalid/stale since the entity used to be a propagator). - - PROBLEM: What if multiple entities in a hierarchy lose PropagateRenderGroups components? + - PROBLEM: What if multiple entities in a hierarchy lose PropagateRenderLayers components? - SOLUTION: Ignore the updated_entities cache and force-update children even if they were previously updated. This will cause some redundant work, but gives the correct result. - - If a child is marked as updated, then always insert/remove InheritedRenderGroups components to match - the desired policy (regardless of it the entity has InheritedRenderGroups), in case previous deferred + - If a child is marked as updated, then always insert/remove InheritedRenderLayers components to match + the desired policy (regardless of it the entity has InheritedRenderLayers), in case previous deferred updates of this type need to be overwritten. -- If a non-propagator entity is parented to a non-propagator, then propagate the parent's InheritedRenderGroups +- If a non-propagator entity is parented to a non-propagator, then propagate the parent's InheritedRenderLayers propagator entity (stopping at propagators and descendents that share the parent's propagator). If the parent doesn't -have InheritedRenderGroups, then remove InheritedRenderGroups from the entity and its children (stopping at propagators -and children that don't have InheritedRenderGroups and aren't updated). Skip already-updated entities that were parented +have InheritedRenderLayers, then remove InheritedRenderLayers from the entity and its children (stopping at propagators +and children that don't have InheritedRenderLayers and aren't updated). Skip already-updated entities that were parented to a non-propagator. - SYSTEMS: handle_new_children_nonpropagator, handle_new_parent_nonpropagator - The goal of this step is to update the span of entities starting at the entity that was reparented, and ending @@ -103,9 +103,9 @@ to a non-propagator. - If the parent is marked updated (but the entity is not marked updated), then this entity's propagator stays the same (see "If a propagator gains non-propagator children"). In that case, do not mark the entity updated and simply skip it. - - Force-update children for the same reason as "If an entity loses a PropagateRenderGroups component". - - The implementation does not iterate non-propagator entities without InheritedRenderGroups that were parented - to entities without InheritedRenderGroups. Issues that can arise from that case, such as other hierarchy moves + - Force-update children for the same reason as "If an entity loses a PropagateRenderLayers component". + - The implementation does not iterate non-propagator entities without InheritedRenderLayers that were parented + to entities without InheritedRenderLayers. Issues that can arise from that case, such as other hierarchy moves below or above an entity, will be handled correctly by this and previous steps. - Note that the challenging hierarchy reasoning used here is necessary to allow efficient queries. A careless solution would require iterating all entities with Parent or Children changes, and traversing the hierarchy many @@ -113,20 +113,20 @@ to a non-propagator. ### Non-Propagator Targeted Repair -- If a non-propagator entity with InheritedRenderGroups has an added/removed/changed RenderGroups, then recompute -its InheritedRenderGroups::computed field. Skip already-updated entities. - - SYSTEM: handle_modified_rendergroups +- If a non-propagator entity with InheritedRenderLayers has an added/removed/changed RenderLayers, then recompute +its InheritedRenderLayers::computed field. Skip already-updated entities. + - SYSTEM: handle_modified_renderlayers ## Performance The base-line performance cost of this algorithm comes from detecting changes, which requires iterating queries. -- All entities with `Children` and `PropagateRenderGroups` are iterated twice (can potentially be reduced to once). -- All entities with `Children` and `InheritedRenderGroups` are iterated once. -- All entities with `Parent` and `InheritedRenderGroups` are iterated once. -- All entities with `RenderGroups` and `InheritedRenderGroups` are iterated once. -- `RemovedComponents` is iterated twice. -- `RemovedComponents` is iterated once. +- All entities with `Children` and `PropagateRenderLayers` are iterated twice (can potentially be reduced to once). +- All entities with `Children` and `InheritedRenderLayers` are iterated once. +- All entities with `Parent` and `InheritedRenderLayers` are iterated once. +- All entities with `RenderLayers` and `InheritedRenderLayers` are iterated once. +- `RemovedComponents` is iterated twice. +- `RemovedComponents` is iterated once. - `RemovedComponents` is iterated once. - `RemovedComponents` is iterated once. */ @@ -144,18 +144,18 @@ use bevy_hierarchy::{Children, Parent}; use bevy_utils::error_once; use bevy_utils::tracing::warn; -/// System set that applies [`PropagateRenderGroups`] by updating [`InheritedRenderGroups`] components on +/// System set that applies [`PropagateRenderLayers`] by updating [`InheritedRenderLayers`] components on /// entities. /// /// Runs in [`PostUpdate`] in the [`VisibilityPropagate`](VisibilitySystems::VisibilityPropagate) set. #[derive(SystemSet, Debug, Clone, Hash, Eq, PartialEq)] -pub struct PropagateRenderGroupsSet; +pub struct PropagateRenderLayersSet; -pub(crate) struct PropagateRenderGroupsPlugin; +pub(crate) struct PropagateRenderLayersPlugin; -impl Plugin for PropagateRenderGroupsPlugin { +impl Plugin for PropagateRenderLayersPlugin { fn build(&self, app: &mut App) { - app.init_resource::() + app.init_resource::() .add_systems( PostUpdate, ( @@ -167,205 +167,188 @@ impl Plugin for PropagateRenderGroupsPlugin { handle_new_children_nonpropagator, handle_new_parent_nonpropagator, apply_deferred, - handle_modified_rendergroups, //does not have deferred commands + handle_modified_renderlayers, //does not have deferred commands ) .chain() - .in_set(PropagateRenderGroupsSet), + .in_set(PropagateRenderLayersSet), ); } } -/// Component on an entity that causes it to propagate a [`RenderGroups`] value to its children. +/// Component on an entity that causes it to propagate a [`RenderLayers`] value to its children. /// -/// Entities with this component will ignore [`RenderGroups`] propagated by parents. +/// Entities with this component will ignore [`RenderLayers`] propagated by parents. /// -/// See [`RenderGroups`] and [`CameraView`]. +/// See [`RenderLayers`] and [`CameraLayer`]. #[derive(Component)] -pub enum PropagateRenderGroups { - /// If the entity has a [`RenderGroups`] component, that value is propagated, otherwise a default - /// [`RenderGroups`] is propagated. +pub enum PropagateRenderLayers { + /// If the entity has a [`RenderLayers`] component, that value is propagated, otherwise a default + /// [`RenderLayers`] is propagated. /// - /// Note that it is allowed to add a [`RenderGroup`](crate::view::RenderGroups) component to a camera. + /// Note that it is allowed to add a [`RenderLayers`] component to a camera for propagation. Auto, - /// If the entity has a [`Camera`] component, propagates `RenderGroups::new_with_camera(entity)`. + /// If the entity has a [`Camera`] component and a [`CameraLayer`] component, propagates + /// [`CameraLayer::get_layers`]. Uses [`CameraLayer::default`] if there is no [`CameraLayer`]. /// - /// Otherwise a warning will be logged and an empty [`RenderGroups`] will be propagated. + /// If there is no [`Camera`] component, a warning will be logged and an empty [`RenderLayers`] will be propagated. Camera, - /// If the entity has a [`Camera`] component and a [`CameraView`] component, propagates - /// `CameraView::get_groups(entity)`. - /// - /// Otherwise a warning will be logged and an empty [`RenderGroups`] will be propagated. - CameraWithView, - /// Propagates a custom [`RenderGroups`]. - Custom(RenderGroups), + /// Propagates a custom [`RenderLayers`]. + Custom(RenderLayers), } -impl PropagateRenderGroups { - pub fn get_render_groups<'a>( +impl PropagateRenderLayers { + pub fn get_render_layers<'a>( &'a self, - saved: &mut RenderGroups, - entity: Entity, - groups: Option<&'a RenderGroups>, - view: Option<&CameraView>, + saved: &mut RenderLayers, + layers: Option<&'a RenderLayers>, + view: Option<&CameraLayer>, is_camera: bool, - ) -> RenderGroupsRef<'a> { + ) -> RenderLayersRef<'a> { match self { Self::Auto => { - let Some(groups) = groups else { - return RenderGroupsRef::Val(RenderGroups::default()); + let Some(layers) = layers else { + return RenderLayersRef::Val(RenderLayers::default()); }; - RenderGroupsRef::Ref(groups) + RenderLayersRef::Ref(layers) } Self::Camera => { if !is_camera { - warn!("failed propagating PropagateRenderGroups::Camera, {entity} doesn't have a camera"); - return RenderGroupsRef::Val(RenderGroups::empty()); - }; - RenderGroupsRef::Val(RenderGroups::new_with_camera(entity)) - } - Self::CameraWithView => { - if !is_camera { - warn!("failed propagating PropagateRenderGroups::CameraWithView, {entity} doesn't have a camera"); - return RenderGroupsRef::Val(RenderGroups::empty()); + warn!("failed propagating PropagateRenderLayers::Camera, entity doesn't have a camera"); + return RenderLayersRef::Val(RenderLayers::empty()); }; - let empty_camera_view = CameraView::empty(); - let view = view.unwrap_or(&empty_camera_view); - - if view.is_allocated() && saved.is_allocated() { - // Reuse saved allocation. - let mut temp = RenderGroups::empty(); - std::mem::swap(&mut temp, saved); - temp.set_from_parts(Some(entity), view.layers()); - RenderGroupsRef::Val(temp) - } else { - // Allocate a new RenderGroups - RenderGroupsRef::Val(view.get_groups(entity)) + let default_camera_layer = CameraLayer::default(); + let view = view.unwrap_or(&default_camera_layer); + + // Reuse saved allocation. + saved.clear(); + if let Some(layer) = view.layer() { + saved.add(layer); } + let mut temp = RenderLayers::empty(); + std::mem::swap(&mut temp, saved); + RenderLayersRef::Val(temp) } - Self::Custom(groups) => RenderGroupsRef::Ref(groups), + Self::Custom(layers) => RenderLayersRef::Ref(layers), } } } -/// Component on an entity that stores the result of merging the entity's [`RenderGroups`] -/// component with the [`RenderGroups`] of an entity propagated by the entity's parent. +/// Component on an entity that stores the result of merging the entity's [`RenderLayers`] +/// component with the [`RenderLayers`] of an entity propagated by the entity's parent. /// -/// See [`PropagateRenderGroups`]. +/// See [`PropagateRenderLayers`]. /// -/// This is updated in [`PostUpdate`] in the [`PropagateRenderGroupsSet`]. +/// This is updated in [`PostUpdate`] in the [`PropagateRenderLayersSet`]. /// The component will be automatically added or removed depending on if it is needed. /// /// ### Merge details /// -/// The merge direction is `entity_rendergroups.merge(propagated_rendergroups)` -/// (see [`RenderGroups::merge`]). +/// The merge direction is `entity_renderLayers.merge(propagated_renderLayers)` +/// (see [`RenderLayers::merge`]). /// This means the entity's affiliated camera will be prioritized over the propagated affiliated camera. #[derive(Component, Debug, Clone)] -pub struct InheritedRenderGroups { - /// The entity that propagated a [`RenderGroups`] to this entity. +pub struct InheritedRenderLayers { + /// The entity that propagated a [`RenderLayers`] to this entity. /// /// This is cached so children of this entity can update themselves without needing to traverse the /// entire hierarchy. pub propagator: Entity, - /// The [`RenderGroups`] computed by merging the [`RenderGroups`] of the `Self::propagator` entity into - /// the node's [`RenderGroups`] component. + /// The [`RenderLayers`] computed by merging the [`RenderLayers`] of the `Self::propagator` entity into + /// the node's [`RenderLayers`] component. /// /// This is cached for efficient access in the [`check_visibility`] system. - pub computed: RenderGroups, + pub computed: RenderLayers, } -impl InheritedRenderGroups { - /// Makes an empty `InheritedRenderGroups`. +impl InheritedRenderLayers { + /// Makes an empty `InheritedRenderLayers`. pub fn empty() -> Self { Self { propagator: Entity::PLACEHOLDER, - computed: RenderGroups::empty(), + computed: RenderLayers::empty(), } } } -/// Contains the final [`RenderGroups`] of an entity for extraction to the render world. +/// Contains the final [`RenderLayers`] of an entity for extraction to the render world. #[derive(Component, Debug, Deref)] -pub struct ExtractedRenderGroups(RenderGroups); +pub struct ExtractedRenderLayers(RenderLayers); -/// Evaluates an entity's possible `RenderGroups` and `InheritedRenderGroups` components to get a -/// final [`ExtractedRenderGroups`] for the render world. +/// Evaluates an entity's possible `RenderLayers` and `InheritedRenderLayers` components to get a +/// final [`ExtractedRenderLayers`] for the render world. /// -/// Potentially allocates if [`InheritedRenderGroups`] or [`RenderGroups`] is allocated. -pub fn extract_render_groups( - inherited: Option<&InheritedRenderGroups>, - render_groups: Option<&RenderGroups>, -) -> ExtractedRenderGroups { - ExtractedRenderGroups( +/// Potentially allocates if [`InheritedRenderLayers`] or [`RenderLayers`] is allocated. +pub fn extract_render_layers( + inherited: Option<&InheritedRenderLayers>, + render_layers: Option<&RenderLayers>, +) -> ExtractedRenderLayers { + ExtractedRenderLayers( inherited .map(|i| &i.computed) - .or(render_groups) + .or(render_layers) .cloned() - .unwrap_or(RenderGroups::default()), + .unwrap_or(RenderLayers::default()), ) } -/// Evaluates a camera's possible `CameraView` and `InheritedRenderGroups` components to get a -/// final [`ExtractedRenderGroups`] for the render world. +/// Evaluates a camera's possible `CameraLayer` component to get a +/// final [`ExtractedRenderLayers`] for the render world. /// -/// Potentially allocates if [`InheritedRenderGroups`] or [`RenderGroups`] is allocated. -pub fn extract_camera_view( - camera: Entity, - camera_view: Option<&CameraView>, -) -> ExtractedRenderGroups { - ExtractedRenderGroups( - camera_view - .map(|i| i.get_groups(camera)) - .unwrap_or(RenderGroups::default_with_camera(camera)), +/// Potentially allocates if [`InheritedRenderLayers`] or [`CameraLayer`] is allocated. +pub fn extract_camera_layer(camera_layer: Option<&CameraLayer>) -> ExtractedRenderLayers { + ExtractedRenderLayers( + camera_layer + .map(|i| i.get_layers()) + .unwrap_or(RenderLayers::default()), ) } -/// Derives a [`RenderGroupsRef`] from an optional [`InheritedRenderGroups`] and [`RenderGroups`]. +/// Derives a [`RenderLayersRef`] from an optional [`InheritedRenderLayers`] and [`RenderLayers`]. /// /// Returns in order of priority: -/// - [`InheritedRenderGroups::computed`] -/// - [`RenderGroups`] -/// - [`RenderGroups::default`] -pub fn derive_render_groups<'a>( - inherited: Option<&'a InheritedRenderGroups>, - render_groups: Option<&'a RenderGroups>, -) -> RenderGroupsRef<'a> { +/// - [`InheritedRenderLayers::computed`] +/// - [`RenderLayers`] +/// - [`RenderLayers::default`] +pub fn derive_render_layers<'a>( + inherited: Option<&'a InheritedRenderLayers>, + render_layers: Option<&'a RenderLayers>, +) -> RenderLayersRef<'a> { if let Some(inherited) = inherited { - RenderGroupsRef::Ref(&inherited.computed) - } else if let Some(render_groups) = render_groups { - RenderGroupsRef::Ref(render_groups) + RenderLayersRef::Ref(&inherited.computed) + } else if let Some(render_layers) = render_layers { + RenderLayersRef::Ref(render_layers) } else { - RenderGroupsRef::Val(RenderGroups::default()) + RenderLayersRef::Val(RenderLayers::default()) } } -/// Derives a [`RenderGroupsPtr`] from an optional [`InheritedRenderGroups`] and [`RenderGroups`]. +/// Derives a [`RenderLayersPtr`] from an optional [`InheritedRenderLayers`] and [`RenderLayers`]. /// -/// See [`derive_render_groups`]. -pub fn derive_render_groups_ptr( - inherited: Option<&InheritedRenderGroups>, - render_groups: Option<&RenderGroups>, -) -> RenderGroupsPtr { +/// See [`derive_render_layers`]. +pub fn derive_render_layers_ptr( + inherited: Option<&InheritedRenderLayers>, + render_layers: Option<&RenderLayers>, +) -> RenderLayersPtr { if let Some(inherited) = inherited { - RenderGroupsPtr::Ptr(&inherited.computed) - } else if let Some(render_groups) = render_groups { - RenderGroupsPtr::Ptr(render_groups) + RenderLayersPtr::Ptr(&inherited.computed) + } else if let Some(render_layers) = render_layers { + RenderLayersPtr::Ptr(render_layers) } else { - RenderGroupsPtr::Val(RenderGroups::default()) + RenderLayersPtr::Val(RenderLayers::default()) } } #[derive(Resource, Default)] -struct PropagateRenderGroupsEntityCache { +struct PropagateRenderLayersEntityCache { /// Buffered to absorb spurious allocations of propagated values during traversals. - saved: RenderGroups, + saved: RenderLayers, /// Updated entities. /// - /// This is cleared at the start of [`PropagateRenderGroupsSet`]. + /// This is cleared at the start of [`PropagateRenderLayersSet`]. entities: EntityHashSet, } -impl PropagateRenderGroupsEntityCache { +impl PropagateRenderLayersEntityCache { fn insert(&mut self, entity: Entity) { self.entities.insert(entity); } @@ -378,19 +361,19 @@ impl PropagateRenderGroupsEntityCache { self.entities.clear(); } - fn saved(&mut self) -> &mut RenderGroups { + fn saved(&mut self) -> &mut RenderLayers { &mut self.saved } } -/// Removes `InheritedRenderGroups` from entities with `PropagateRenderGroups`. +/// Removes `InheritedRenderLayers` from entities with `PropagateRenderLayers`. fn clean_propagators( mut commands: Commands, - dirty_propagators: Query, With)>, + dirty_propagators: Query, With)>, ) { for dirty in dirty_propagators.iter() { if let Some(mut entity) = commands.get_entity(dirty) { - entity.remove::(); + entity.remove::(); } } } @@ -400,49 +383,49 @@ fn clean_propagators( #[allow(clippy::too_many_arguments)] fn propagate_updated_propagators( mut commands: Commands, - mut updated_entities: ResMut, - // Detect added/changed: RenderGroups, CameraView, Camera (added only), PropagateRenderGroups. + mut updated_entities: ResMut, + // Detect added/changed: RenderLayers, CameraLayer, Camera (added only), PropagateRenderLayers. changed_propagators: Query< ( Entity, &Children, - Option<&RenderGroups>, - Option<&CameraView>, + Option<&RenderLayers>, + Option<&CameraLayer>, Has, - &PropagateRenderGroups, + &PropagateRenderLayers, ), Or<( - Changed, - Changed, + Changed, + Changed, Added, - Changed, + Changed, )>, >, - // Detect removed: RenderGroups, CameraView, Camera. - mut removed_rendergroups: RemovedComponents, - mut removed_cameraview: RemovedComponents, + // Detect removed: RenderLayers, CameraLayer, Camera. + mut removed_renderlayers: RemovedComponents, + mut removed_cameralayer: RemovedComponents, mut removed_camera: RemovedComponents, all_propagators: Query<( Entity, &Children, - Option<&RenderGroups>, - Option<&CameraView>, + Option<&RenderLayers>, + Option<&CameraLayer>, Has, - &PropagateRenderGroups, + &PropagateRenderLayers, )>, // Query for getting Children. children_query: Query<&Children>, - // Query for updating InheritedRenderGroups on non-propagator entities. + // Query for updating InheritedRenderLayers on non-propagator entities. mut maybe_inherited: Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, ) { // Track updated entities to prevent redundant updates, as `Commands` changes are deferred. // - // Using this ensures that once mutations to entities with PropagateRenderGroups have been + // Using this ensures that once mutations to entities with PropagateRenderLayers have been // propagated, all entities affected by those changes won't be mutated again. This makes it - // safe to read parent InheritedRenderGroups (in the other cases that need to be handled) in + // safe to read parent InheritedRenderLayers (in the other cases that need to be handled) in // order to 'pull in' inherited changes when needed. See algorithm description for more details. updated_entities.clear(); @@ -450,15 +433,15 @@ fn propagate_updated_propagators( let propagators = changed_propagators.iter().chain( // IMPORTANT: Removals should be ordered first if propagate_to_new_children is merged // into changed_propagators. - removed_rendergroups + removed_renderlayers .read() - .chain(removed_cameraview.read()) + .chain(removed_cameralayer.read()) .chain(removed_camera.read()) .filter_map(|e| all_propagators.get(e).ok()), ); // Propagate each propagator. - for (propagator, children, maybe_render_groups, maybe_camera_view, maybe_camera, propagate) in + for (propagator, children, maybe_render_layers, maybe_camera_layer, maybe_camera, propagate) in propagators { // There can be duplicates due to component removals. @@ -467,11 +450,10 @@ fn propagate_updated_propagators( } // Get value to propagate. - let mut propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_layers( updated_entities.saved(), - propagator, - maybe_render_groups, - maybe_camera_view, + maybe_render_layers, + maybe_camera_layer, maybe_camera, ); @@ -499,34 +481,34 @@ fn propagate_updated_propagators( // Note: This does not require checking updated_entities because all children will be fresh. fn apply_full_propagation( commands: &mut Commands, - updated_entities: &mut PropagateRenderGroupsEntityCache, + updated_entities: &mut PropagateRenderLayersEntityCache, children_query: &Query<&Children>, maybe_inherited: &mut Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, propagator: Entity, - propagated: &RenderGroups, + propagated: &RenderLayers, entity: Entity, ) { - // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((maybe_render_groups, maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + // Leave if entity doesn't exist or has PropagateRenderLayers. + let Ok((maybe_render_layers, maybe_inherited_layers)) = maybe_inherited.get_mut(entity) else { return; }; // Update inherited value or insert a new one. - let empty_render_groups = RenderGroups::empty(); - let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); - let apply_changes = |groups: &mut InheritedRenderGroups| { - groups.propagator = propagator; - groups.computed.set_from(initial_groups); - groups.computed.merge(propagated); + let empty_render_layers = RenderLayers::empty(); + let initial_layers = maybe_render_layers.unwrap_or(&empty_render_layers); + let apply_changes = |layers: &mut InheritedRenderLayers| { + layers.propagator = propagator; + layers.computed.set_from(initial_layers); + layers.computed.merge(propagated); }; - if let Some(mut inherited) = maybe_inherited_groups { + if let Some(mut inherited) = maybe_inherited_layers { apply_changes(&mut inherited); } else { - let mut new = InheritedRenderGroups::empty(); + let mut new = InheritedRenderLayers::empty(); apply_changes(&mut new); commands.entity(entity).insert(new); } @@ -557,29 +539,29 @@ fn apply_full_propagation( // are handled. fn propagate_to_new_children( mut commands: Commands, - mut updated_entities: ResMut, + mut updated_entities: ResMut, // Changed children. changed_children: Query< ( Entity, &Children, - Option<&RenderGroups>, - Option<&CameraView>, + Option<&RenderLayers>, + Option<&CameraLayer>, Has, - &PropagateRenderGroups, + &PropagateRenderLayers, ), Changed, >, // Query for getting Children. children_query: Query<&Children>, - // Query for updating InheritedRenderGroups on non-propagator entities. + // Query for updating InheritedRenderLayers on non-propagator entities. mut maybe_inherited: Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, ) { // Propagate each propagator that has new children. - for (propagator, children, maybe_render_groups, maybe_camera_view, maybe_camera, propagate) in + for (propagator, children, maybe_render_layers, maybe_camera_layer, maybe_camera, propagate) in changed_children.iter() { // The propagator could have been updated in a previous step. @@ -588,11 +570,10 @@ fn propagate_to_new_children( } // Get value to propagate. - let mut propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_layers( updated_entities.saved(), - propagator, - maybe_render_groups, - maybe_camera_view, + maybe_render_layers, + maybe_camera_layer, maybe_camera, ); @@ -620,23 +601,23 @@ fn propagate_to_new_children( // Note: This does not require checking updated_entities because all children will be fresh. fn apply_new_children_propagation( commands: &mut Commands, - updated_entities: &mut PropagateRenderGroupsEntityCache, + updated_entities: &mut PropagateRenderLayersEntityCache, children_query: &Query<&Children>, maybe_inherited: &mut Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, propagator: Entity, - propagated: &RenderGroups, + propagated: &RenderLayers, entity: Entity, ) { - // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((maybe_render_groups, maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + // Leave if entity doesn't exist or has PropagateRenderLayers. + let Ok((maybe_render_layers, maybe_inherited_layers)) = maybe_inherited.get_mut(entity) else { return; }; // Leave if the propagator is already known (implying this is a pre-existing child). - if maybe_inherited_groups + if maybe_inherited_layers .as_ref() .map(|i| i.propagator == propagator) .unwrap_or(false) @@ -645,18 +626,18 @@ fn apply_new_children_propagation( } // Update inherited value or insert a new one. - let empty_render_groups = RenderGroups::empty(); - let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); - let apply_changes = |groups: &mut InheritedRenderGroups| { - groups.propagator = propagator; - groups.computed.set_from(initial_groups); - groups.computed.merge(propagated); + let empty_render_layers = RenderLayers::empty(); + let initial_layers = maybe_render_layers.unwrap_or(&empty_render_layers); + let apply_changes = |layers: &mut InheritedRenderLayers| { + layers.propagator = propagator; + layers.computed.set_from(initial_layers); + layers.computed.merge(propagated); }; - if let Some(mut inherited) = maybe_inherited_groups { + if let Some(mut inherited) = maybe_inherited_layers { apply_changes(&mut inherited); } else { - let mut new = InheritedRenderGroups::empty(); + let mut new = InheritedRenderLayers::empty(); apply_changes(&mut new); commands.entity(entity).insert(new); } @@ -681,22 +662,22 @@ fn apply_new_children_propagation( } } -/// Removes `InheritedRenderGroups` from orphaned branches of the hierarchy. +/// Removes `InheritedRenderLayers` from orphaned branches of the hierarchy. fn handle_orphaned_nonpropagators( mut commands: Commands, - mut updated_entities: ResMut, - // Orphaned non-propagator entities that previously had InheritedRenderGroups. + mut updated_entities: ResMut, + // Orphaned non-propagator entities that previously had InheritedRenderLayers. mut removed_parents: RemovedComponents, orphaned: Query< (Entity, Option<&Children>), ( Without, - With, - Without, + With, + Without, ), >, // Query for getting non-propagator children. - nonpropagators: Query, Without>, + nonpropagators: Query, Without>, ) { for (orphan, maybe_children) in removed_parents.read().filter_map(|r| orphaned.get(r).ok()) { apply_orphan_cleanup( @@ -712,14 +693,14 @@ fn handle_orphaned_nonpropagators( /// Applies propagation for `handle_orphaned_nonpropagators`. fn apply_orphan_cleanup( commands: &mut Commands, - updated_entities: &mut PropagateRenderGroupsEntityCache, - nonpropagators: &Query, Without>, + updated_entities: &mut PropagateRenderLayersEntityCache, + nonpropagators: &Query, Without>, entity: Entity, maybe_children: Option<&Children>, ) { - // Remove InheritedRenderGroups. + // Remove InheritedRenderLayers. if let Some(mut entity) = commands.get_entity(entity) { - entity.remove::(); + entity.remove::(); } // Mark as updated. @@ -730,7 +711,7 @@ fn apply_orphan_cleanup( return; }; for child in children.iter().copied() { - // Ignore children that have PropagateRenderGroups. + // Ignore children that have PropagateRenderLayers. let Ok(maybe_children) = nonpropagators.get(child) else { continue; }; @@ -745,27 +726,27 @@ fn apply_orphan_cleanup( } } -/// Handles entities that lost the `PropagateRenderGroups` component. +/// Handles entities that lost the `PropagateRenderLayers` component. fn handle_lost_propagator( mut commands: Commands, - mut updated_entities: ResMut, - // Entities that lost PropagateRenderGroups - mut removed_propagate: RemovedComponents, - unpropagated: Query<(Entity, Option<&Parent>), Without>, + mut updated_entities: ResMut, + // Entities that lost PropagateRenderLayers + mut removed_propagate: RemovedComponents, + unpropagated: Query<(Entity, Option<&Parent>), Without>, // Query for accessing propagators all_propagators: Query<( Entity, - Option<&RenderGroups>, - Option<&CameraView>, + Option<&RenderLayers>, + Option<&CameraLayer>, Has, - &PropagateRenderGroups, + &PropagateRenderLayers, )>, // Query for getting Children. children_query: Query<&Children>, - // Query for updating InheritedRenderGroups on non-propagator entities. + // Query for updating InheritedRenderLayers on non-propagator entities. mut maybe_inherited: Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, ) { for (entity, maybe_parent) in removed_propagate @@ -782,10 +763,10 @@ fn handle_lost_propagator( // Case 1: no parent // - Policy: remove all - // Case 2: parent is a non-propagator without InheritedRenderGroups (not marked updated) + // Case 2: parent is a non-propagator without InheritedRenderLayers (not marked updated) // - Policy: remove all - // Case 3: parent is a non-propagator with InheritedRenderGroups (not marked updated) + // Case 3: parent is a non-propagator with InheritedRenderLayers (not marked updated) // - Subcase 1: Parent's propagator doesn't exit // - Policy: remove all (note: this is not an error, because the propagation step to hendle it may not // have executed yet) @@ -793,11 +774,11 @@ fn handle_lost_propagator( // - Policy: Compute propagation value from parent's propagator // Case 4: parent is a non-propagator marked updated - // - Subcase 1: Self doesn't have InheritedRenderGroups + // - Subcase 1: Self doesn't have InheritedRenderLayers // - Policy: remove all - // - Subcase 2: Propagator stored in self's InheritedRenderGroups doesn't exist + // - Subcase 2: Propagator stored in self's InheritedRenderLayers doesn't exist // - Policy: remove all - // - Subcase 3: Recalculate InheritedRenderGroups with self-stored propagator + // - Subcase 3: Recalculate InheritedRenderLayers with self-stored propagator // - Policy: propagate value derived from propagator // Case 5: parent is a propagator @@ -849,23 +830,27 @@ fn handle_lost_propagator( // Propagate if possible // - Case 3-2, 4-3 // - Cases 3-1, 4-2 are filtered out here. - if let Some((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = - propagator.and_then(|p| all_propagators.get(p).ok()) + if let Some(( + propagator, + maybe_render_layers, + maybe_camera_layer, + maybe_camera, + propagate, + )) = propagator.and_then(|p| all_propagators.get(p).ok()) { // Propagation value - let mut propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_layers( updated_entities.saved(), - propagator, - maybe_render_groups, - maybe_camera_view, + maybe_render_layers, + maybe_camera_layer, maybe_camera, ); // Pre-update the entity as a hack for case 4-3. If we don't do this then // the entity will be caught in "Leave if entity is non-updated and inherits a matching propagator." - // - Note: Case 4-3 is compensating for users manually inserting InheritedRenderGroups + // - Note: Case 4-3 is compensating for users manually inserting InheritedRenderLayers // components, so this could be simplified if that's deemed overkill (we don't fully compensate for - // manual messing with InheritedRenderGroups anyway, so there is no real reliability for doing so). + // manual messing with InheritedRenderLayers anyway, so there is no real reliability for doing so). updated_entities.insert(entity); apply_full_propagation_force_update( @@ -880,7 +865,7 @@ fn handle_lost_propagator( // Reclaim memory propagated.reclaim(updated_entities.saved()); - // In all other cases, remove all InheritedRenderGroups. + // In all other cases, remove all InheritedRenderLayers. } else { apply_full_propagation_force_remove( &mut commands, @@ -896,34 +881,34 @@ fn handle_lost_propagator( /// Applies propagation to entities for `handle_lost_propagator` and `handle_new_children_nonpropagator`. fn apply_full_propagation_force_update( commands: &mut Commands, - updated_entities: &mut PropagateRenderGroupsEntityCache, + updated_entities: &mut PropagateRenderLayersEntityCache, children_query: &Query<&Children>, maybe_inherited: &mut Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, propagator: Entity, - propagated: &RenderGroups, + propagated: &RenderLayers, entity: Entity, ) { - // Leave if entity doesn't exist or has PropagateRenderGroups. - let Ok((maybe_render_groups, maybe_inherited_groups)) = maybe_inherited.get_mut(entity) else { + // Leave if entity doesn't exist or has PropagateRenderLayers. + let Ok((maybe_render_layers, maybe_inherited_layers)) = maybe_inherited.get_mut(entity) else { return; }; // Leave if entity is non-updated and inherits a matching propagator. - if let Some(inherited) = maybe_inherited_groups.as_ref() { + if let Some(inherited) = maybe_inherited_layers.as_ref() { if (inherited.propagator == propagator) && !updated_entities.contains(entity) { return; } } // Force-update - let empty_render_groups = RenderGroups::empty(); - let initial_groups = maybe_render_groups.unwrap_or(&empty_render_groups); + let empty_render_layers = RenderLayers::empty(); + let initial_layers = maybe_render_layers.unwrap_or(&empty_render_layers); - let mut new = InheritedRenderGroups::empty(); - if let Some(mut inherited) = maybe_inherited_groups { + let mut new = InheritedRenderLayers::empty(); + if let Some(mut inherited) = maybe_inherited_layers { // Steal existing allocation if useful. if inherited.computed.is_allocated() { std::mem::swap(&mut inherited.computed, &mut new.computed); @@ -931,7 +916,7 @@ fn apply_full_propagation_force_update( } new.propagator = propagator; - new.computed.set_from(initial_groups); + new.computed.set_from(initial_layers); new.computed.merge(propagated); commands.entity(entity).insert(new); @@ -955,30 +940,30 @@ fn apply_full_propagation_force_update( } } -/// Applies `InheritedRenderGroups` removal to entities for `handle_lost_propagator`, +/// Applies `InheritedRenderLayers` removal to entities for `handle_lost_propagator`, /// `handle_new_children_nonpropagator`, and `handle_new_parent_nonpropagator`. fn apply_full_propagation_force_remove( commands: &mut Commands, - updated_entities: &mut PropagateRenderGroupsEntityCache, + updated_entities: &mut PropagateRenderLayersEntityCache, children_query: &Query<&Children>, maybe_inherited: &Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, entity: Entity, ) { - // Leave if entity doesn't exist or has PropagateRenderGroups. + // Leave if entity doesn't exist or has PropagateRenderLayers. let Ok((_, maybe_inherited_inner)) = maybe_inherited.get(entity) else { return; }; - // Leave if entity is non-updated and doesn't have InheritedRenderGroups. + // Leave if entity is non-updated and doesn't have InheritedRenderLayers. if maybe_inherited_inner.is_none() && !updated_entities.contains(entity) { return; } - // Force-remove InheritedRenderGroups - commands.entity(entity).remove::(); + // Force-remove InheritedRenderLayers + commands.entity(entity).remove::(); // Mark as updated. updated_entities.insert(entity); @@ -998,33 +983,33 @@ fn apply_full_propagation_force_remove( } } -/// Handles non-propagator entities with `InheritedRenderGroups` whose children changed. +/// Handles non-propagator entities with `InheritedRenderLayers` whose children changed. fn handle_new_children_nonpropagator( mut commands: Commands, - mut updated_entities: ResMut, - // Entities with InheritedRenderGroups that changed children + mut updated_entities: ResMut, + // Entities with InheritedRenderLayers that changed children inherited_with_children: Query< (Entity, &Children), ( Changed, - With, - Without, + With, + Without, ), >, // Query for accessing propagators all_propagators: Query<( Entity, - Option<&RenderGroups>, - Option<&CameraView>, + Option<&RenderLayers>, + Option<&CameraLayer>, Has, - &PropagateRenderGroups, + &PropagateRenderLayers, )>, // Query for getting Children. children_query: Query<&Children>, - // Query for updating InheritedRenderGroups on non-propagator entities. + // Query for updating InheritedRenderLayers on non-propagator entities. mut maybe_inherited: Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, ) { for (entity, children) in inherited_with_children.iter() { @@ -1036,17 +1021,17 @@ fn handle_new_children_nonpropagator( // Get the inherited component. We need to do a lookup due to query conflict (Error B0001). let inherited_propagator = maybe_inherited.get(entity).unwrap().1.unwrap().propagator; - let Ok((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = + let Ok((propagator, maybe_render_layers, maybe_camera_layer, maybe_camera, propagate)) = all_propagators.get(inherited_propagator) else { - // Remove InheritedRenderGroups from descendents if the propagator is missing - // - This is either an error caused by manually modifying InheritedRenderGroups, or is caused by a + // Remove InheritedRenderLayers from descendents if the propagator is missing + // - This is either an error caused by manually modifying InheritedRenderLayers, or is caused by a // reparenting + propagator despawn. // Iterate children for child in children.iter().copied() { // Skip children that were already updated. - // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. + // - Note that this can happen e.g. because the child lost the PropagateRenderLayers component. if updated_entities.contains(child) { continue; } @@ -1065,11 +1050,10 @@ fn handle_new_children_nonpropagator( }; // Get value to propagate. - let mut propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_layers( updated_entities.saved(), - propagator, - maybe_render_groups, - maybe_camera_view, + maybe_render_layers, + maybe_camera_layer, maybe_camera, ); @@ -1077,8 +1061,8 @@ fn handle_new_children_nonpropagator( for child in children.iter().copied() { // Skip children that were already updated. We only skip updated children of this initial high-level // loop, not children within the recursion which need to be force-updated. The 'span' of entities - // we update in this step starts at non-updated children of an entity with InheritedRenderGroups. - // - Note that this can happen e.g. because the child lost the PropagateRenderGroups component. + // we update in this step starts at non-updated children of an entity with InheritedRenderLayers. + // - Note that this can happen e.g. because the child lost the PropagateRenderLayers component. if updated_entities.contains(child) { continue; } @@ -1100,31 +1084,31 @@ fn handle_new_children_nonpropagator( } } -/// Handles non-propagator entities with `InheritedRenderGroups` whose parents changed. -/// - Since `handle_new_children_nonpropagator` handles all cases where the parent has `InheritedRenderGroups`, this -/// system just needs to remove `InheritedRenderGroups` from non-updated entities and their non-updated descendents -/// that have `InheritedRenderGroups` (stopping at propagators and non-updated descendents without -/// `InheritedRenderGroups`). -/// - We skip non-updated entities whose parents are updated, because that implies the current `InheritedRenderGroups` +/// Handles non-propagator entities with `InheritedRenderLayers` whose parents changed. +/// - Since `handle_new_children_nonpropagator` handles all cases where the parent has `InheritedRenderLayers`, this +/// system just needs to remove `InheritedRenderLayers` from non-updated entities and their non-updated descendents +/// that have `InheritedRenderLayers` (stopping at propagators and non-updated descendents without +/// `InheritedRenderLayers`). +/// - We skip non-updated entities whose parents are updated, because that implies the current `InheritedRenderLayers` /// propagator is accurate. fn handle_new_parent_nonpropagator( mut commands: Commands, - mut updated_entities: ResMut, - // Entities with InheritedRenderGroups that changed parents + mut updated_entities: ResMut, + // Entities with InheritedRenderLayers that changed parents inherited_with_parent: Query< (Entity, Option<&Children>, &Parent), ( Changed, - With, - Without, + With, + Without, ), >, // Query for Children children_query: Query<&Children>, - // Query for updating InheritedRenderGroups on non-propagator entities. + // Query for updating InheritedRenderLayers on non-propagator entities. maybe_inherited: Query< - (Option<&RenderGroups>, Option<&mut InheritedRenderGroups>), - Without, + (Option<&RenderLayers>, Option<&mut InheritedRenderLayers>), + Without, >, ) { for (entity, maybe_children, parent) in inherited_with_parent.iter() { @@ -1139,13 +1123,13 @@ fn handle_new_parent_nonpropagator( } // Remove from self. - commands.entity(entity).remove::(); + commands.entity(entity).remove::(); // Mark as updated. updated_entities.insert(entity); // Iterate children. - // - We assume the parent of this entity does NOT have InheritedRenderGroups, so neither should its + // - We assume the parent of this entity does NOT have InheritedRenderLayers, so neither should its // descendents. let Some(children) = maybe_children else { continue; @@ -1162,67 +1146,66 @@ fn handle_new_parent_nonpropagator( } } -/// Handles added/removed/changed `RenderGroups` for entities with existing `InheritedRenderGroups`. -fn handle_modified_rendergroups( - mut updated_entities: ResMut, - // Entities with InheritedRenderGroups that changed RenderGroups +/// Handles added/removed/changed `RenderLayers` for entities with existing `InheritedRenderLayers`. +fn handle_modified_renderlayers( + mut updated_entities: ResMut, + // Entities with InheritedRenderLayers that changed RenderLayers inherited_changed: Query< Entity, ( - Changed, - With, - Without, + Changed, + With, + Without, ), >, - // RenderGroups removals. - mut removed_rendergroups: RemovedComponents, + // RenderLayers removals. + mut removed_renderlayers: RemovedComponents, // Query for accessing propagators all_propagators: Query<( Entity, - Option<&RenderGroups>, - Option<&CameraView>, + Option<&RenderLayers>, + Option<&CameraLayer>, Has, - &PropagateRenderGroups, + &PropagateRenderLayers, )>, - // Query for updating InheritedRenderGroups on non-propagator entities. + // Query for updating InheritedRenderLayers on non-propagator entities. mut inherited: Query< - (Option<&RenderGroups>, &mut InheritedRenderGroups), - Without, + (Option<&RenderLayers>, &mut InheritedRenderLayers), + Without, >, ) { - for entity in inherited_changed.iter().chain(removed_rendergroups.read()) { + for entity in inherited_changed.iter().chain(removed_renderlayers.read()) { // Skip entity if already updated. if updated_entities.contains(entity) { continue; } // Skip entity if it's a propagator or doesn't exist. - let Ok((entity_render_groups, mut inherited)) = inherited.get_mut(entity) else { + let Ok((entity_render_layers, mut inherited)) = inherited.get_mut(entity) else { continue; }; // Skip entity if propagator is missing. // - This is an error, hierarchy steps should have marked this entity as updated. - let Ok((propagator, maybe_render_groups, maybe_camera_view, maybe_camera, propagate)) = + let Ok((_propagator, maybe_render_layers, maybe_camera_layer, maybe_camera, propagate)) = all_propagators.get(inherited.propagator) else { - error_once!("hierarchy error: propagator missing for {entity} in `handle_modified_rendergroups`"); + error_once!("hierarchy error: propagator missing for {entity} in `handle_modified_renderlayers`"); continue; }; // Get propagated value. - let mut propagated = propagate.get_render_groups( + let mut propagated = propagate.get_render_layers( updated_entities.saved(), - propagator, - maybe_render_groups, - maybe_camera_view, + maybe_render_layers, + maybe_camera_layer, maybe_camera, ); // Update entity value. inherited .computed - .set_from(entity_render_groups.unwrap_or(&RenderGroups::empty())); + .set_from(entity_render_layers.unwrap_or(&RenderLayers::empty())); inherited.computed.merge(&propagated); // Mark updated (in case of duplicates due to removals). diff --git a/crates/bevy_render/src/view/visibility/render_groups.rs b/crates/bevy_render/src/view/visibility/render_layers.rs similarity index 52% rename from crates/bevy_render/src/view/visibility/render_groups.rs rename to crates/bevy_render/src/view/visibility/render_layers.rs index 10d9d10f17513..3d34b3eff8a03 100644 --- a/crates/bevy_render/src/view/visibility/render_groups.rs +++ b/crates/bevy_render/src/view/visibility/render_layers.rs @@ -13,7 +13,7 @@ use std::ops::Deref; /// /// We issue a warning because [`RenderLayers`] allocates in order to have enough room for a given /// [`RenderLayer`], which is an index into a growable bitset. Large [`RenderLayer`] values can consume -/// a lot of memory since [`RenderGroups`] and [`InheritedRenderGroups`] are potentially on many entities. +/// a lot of memory since [`RenderLayers`] and [`InheritedRenderLayers`] are potentially on many entities. pub const RENDER_LAYERS_WARNING_LIMIT: usize = 1024; /// Records the highest [`RenderLayer`] that can be added to a [`RenderLayers`] before @@ -21,7 +21,7 @@ pub const RENDER_LAYERS_WARNING_LIMIT: usize = 1024; /// /// We panic because [`RenderLayers`] allocates in order to have enough room for a given /// [`RenderLayer`], which is an index into a growable bitset. Large [`RenderLayer`] values can consume -/// a lot of memory since [`RenderGroups`] and [`InheritedRenderGroups`] are potentially on many entities. +/// a lot of memory since [`RenderLayers`] and [`InheritedRenderLayers`] are potentially on many entities. pub const RENDER_LAYERS_PANIC_LIMIT: usize = 1_000_000; /// The default [`RenderLayer`]. @@ -31,7 +31,8 @@ pub static DEFAULT_RENDER_LAYER: RenderLayer = RenderLayer(0); /// /// Stores an index into the [`RenderLayers`] internal bitmask. //todo: Upper limit policy for render layer indices. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deref, DerefMut)] +#[derive(Reflect, Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deref, DerefMut)] +#[reflect(Default, PartialEq)] pub struct RenderLayer(pub usize); impl RenderLayer { @@ -53,15 +54,48 @@ impl Default for RenderLayer { } } -/// Records a growable bitmask of flags for controlling which entities -/// are visible to which cameras. +/// Component on an entity that controls which cameras can see it. /// +/// Records a growable bitmask of flags. /// Individual render layers can be defined with [`RenderLayer`], which is an index -/// into the internal `RenderLayers` bitmask. +/// into the internal bitmask. /// /// `RenderLayers::default()` starts with [`DEFAULT_RENDER_LAYER`], which is the global default /// layer. /// +/// See [`CameraLayer`] for camera-specific details. +/// +/// ### Entity default behavior +/// +/// All entities without a [`RenderLayers`] component are in [`DEFAULT_RENDER_LAYER`] by +/// default (layer 0). If you add a [`RenderLayers`] component to an entity, it may no longer +/// be in the default layer if the [`RenderLayers`] component doesn't include it. +/// +/// For example, if you do `entity.insert(RenderLayers::from(RenderLayer(1)))`, then `entity` +/// will only be in layer 1. You can instead do: +/// +/** +```ignore +// Option 1: default +let mut layers = RenderLayers::default(); +layers.add(RenderLayer(1)); +entity.insert(layers); + +// Option 2: explicit +let mut layers = RenderLayers::from(0); +layers.add(RenderLayer(1)); +entity.insert(layers); + +// Option 3: manual +let layers = RenderLayers::from_layers(&[0, 1]); +entity.insert(layers); +``` +*/ +/// +/// Similarly, if an entity without [`RenderLayers`] inherits from an entity with [`PropagateRenderLayers`] that +/// doesn't propagate layer 0, then the entity's computed [`InheritedRenderLayers`] won't have layer 0 and the +/// entity won't be visible to layer 0. +/// /// ### Performance /// /// `RenderLayers` occupies 24 bytes on the stack. @@ -70,8 +104,8 @@ impl Default for RenderLayer { /// increments, so the second allocation will occur after `RenderLayer(127)`, and so on. /// /// See [`RENDER_LAYERS_WARNING_LIMIT`] and [`RENDER_LAYERS_PANIC_LIMIT`] for `RenderLayers` restrictions. -#[derive(Clone, PartialEq, Reflect)] -#[reflect(Default, PartialEq)] +#[derive(Component, Clone, PartialEq, Reflect)] +#[reflect(Component, Default, PartialEq)] pub struct RenderLayers { layers: SmallVec<[u64; 1]>, } @@ -151,7 +185,7 @@ impl RenderLayers { /// Gets the number of stored layers. /// /// Equivalent to `self.iter().count()`. - pub fn num_layers(&self) -> usize { + pub fn len(&self) -> usize { self.iter().count() } @@ -180,6 +214,15 @@ impl RenderLayers { false } + /// Returns `true` if `Self` intersects with an [`ExtractedRenderLayers`]. + /// + /// If `extracted` is `None`, then intersections is tested using [`RenderLayers::default`]. + pub fn intersects_extracted(&self, extracted: Option<&ExtractedRenderLayers>) -> bool { + let default_render_layers = RenderLayers::default(); + let layers = extracted.map(|i| &**i).unwrap_or(&default_render_layers); + self.intersects(layers) + } + /// Gets the bitmask representation of the contained layers /// as a slice of bitmasks. pub fn bits(&self) -> &[u64] { @@ -252,258 +295,28 @@ impl std::fmt::Debug for RenderLayers { } } -/// Component on an entity that controls which cameras can see it. -/// -/// There are two kinds of render groups: -/// - [`RenderLayers`]: These are grouping categories that many cameras can view (see [`CameraView`]). -/// - *Camera entity*: This is a specific camera that the entity is affiliated with. This is especially -/// useful for UI in combination with [`PropagateRenderGroups`]. -/// -/// An entity can be a member of multiple [`RenderLayers`] in addition to having a camera affiliation. -/// -/// ### Default behavior -/// -/// A default-constructed `RenderGroups` will include [`DEFAULT_RENDER_LAYER`]. -/// If you don't want that, then use [`Self::empty`], [`Self::new_with_camera`], or -/// [`Self::from::`]. -/// -/// ### Entity default behavior -/// -/// All entities without a [`RenderGroups`] component are in [`DEFAULT_RENDER_LAYER`] by -/// default (layer 0). If you add a [`RenderGroups`] component to an entity, it may no longer -/// be in the default layer if the [`RenderGroups`] component doesn't include it. -/// -/// For example, if you do `entity.insert(RenderGroups::from(RenderLayer(1)))`, then `entity` -/// will only be in layer 1. You can instead do: -/** -```ignore -// Option 1: default -let mut groups = RenderGroups::default(); -groups.add(RenderLayer(1)); -entity.insert(groups); - -// Option 2: explicit -let mut groups = RenderGroups::from(0); -groups.add(RenderLayer(1)); -entity.insert(groups); - -// Option 3: manual -let groups = RenderGroups::from(RenderLayers::from_layers(&[0, 1])); -entity.insert(groups); -``` -/// -/// Similarly, if an entity without [`RenderGroups`] inherits from an entity with [`PropagateRenderGroups`] that -/// doesn't propagate layer 0, then the entity's computed [`InheritedRenderGroups`] won't have layer 0 and the -/// entity won't be visible to layer 0. -*/ -#[derive(Component, Debug, Clone, Reflect, PartialEq)] -#[reflect(Component, Default, PartialEq)] -pub struct RenderGroups { - layers: RenderLayers, - camera: Option, -} - -impl RenderGroups { - /// Makes a new `RenderGroups` with no groups. - pub fn empty() -> Self { - Self { - layers: RenderLayers::empty(), - camera: None, - } - } - - /// Makes a new `RenderGroups` with just a camera and no [`RenderLayers`]. - pub fn new_with_camera(camera: Entity) -> Self { - Self { - layers: RenderLayers::empty(), - camera: Some(camera), - } - } - - /// Makes a new `RenderGroups` with a camera and the [`DEFAULT_RENDER_LAYER`]. - pub fn default_with_camera(camera: Entity) -> Self { - Self { - layers: RenderLayers::default(), - camera: Some(camera), - } - } - - /// Makes a new `RenderGroups` with a single [`RenderLayer`] and no camera. - pub fn from_layer(layer: impl Into) -> Self { - Self { - layers: RenderLayers::from_layer(layer), - camera: None, - } - } - - /// Makes a new `RenderGroups` with an array of [`RenderLayer`] and no camera. - pub fn from_layers + Copy>(layers: &[T]) -> Self { - Self { - layers: RenderLayers::from_layers(layers), - camera: None, - } - } - - /// Adds a [`RenderLayer`]. - /// - /// See [`RenderLayers::add`]. - pub fn add(&mut self, layer: impl Into) -> &mut Self { - self.layers.add(layer); - self - } - - /// Removes a [`RenderLayer`]. - /// - /// See [`RenderLayers::remove`]. - pub fn remove(&mut self, layer: impl Into) -> &mut Self { - self.layers.remove(layer); - self - } - - /// Clears all stored render layers without deallocating, and unsets the camera affiliation. - pub fn clear(&mut self) { - self.layers.clear(); - self.camera = None; - } - - /// Merges `other` into `Self`. - /// - /// After merging, `Self` will include all [`RenderLayers`] from `other` and `Self`. - /// If both `Self` and `other` have a camera affiliation, then the `Self` camera - /// will be in the merged result. Otherwise the `other` camera will be in the result. - /// - /// Will allocate if necessary to include all [`RenderLayers`] of `other`. - pub fn merge(&mut self, other: &Self) { - self.layers.merge(&other.layers); - self.camera = self.camera.or(other.camera); - } - - /// Copies `other` into `Self`. - /// - /// This is more efficient than cloning `other` if you want to reuse a `RenderGroups` - /// that is potentially allocated. - pub fn set_from(&mut self, other: &Self) { - self.layers.set_from(&other.layers); - self.camera = other.camera; - } - - /// Overwrites self with internal parts. - /// - /// This is more efficient than cloning `layers` if you want to reuse a `RenderLayers` - /// that is potentially allocated. - pub fn set_from_parts(&mut self, camera: Option, layers: &RenderLayers) { - self.layers.set_from(layers); - self.camera = camera; - } - - /// Sets the camera affiliation. - /// - /// Returns the previous camera. - pub fn set_camera(&mut self, camera: Entity) -> Option { - self.camera.replace(camera) - } - - /// Removes the current camera affiliation. - /// - /// Returns the removed camera. - pub fn remove_camera(&mut self) -> Option { - self.camera.take() - } - - /// Returns an iterator over [`RenderLayer`]. - pub fn iter_layers(&self) -> impl Iterator + '_ { - self.layers.iter() - } - - /// Returns `true` if the specified render layer is included in this - /// `RenderGroups`. - pub fn contains_layer(&self, layer: impl Into) -> bool { - self.layers.contains(layer) - } - - /// Returns `true` if `Self` intersects with `other`. - /// - /// Checks both camera affiliation and [`RenderLayers`] intersection. - pub fn intersects(&self, other: &Self) -> bool { - self.intersects_parts(other.camera, &other.layers) - } - - /// Returns `true` if `Self` intersects with the input parts. - /// - /// Checks both camera affiliation and [`RenderLayers`] intersection. - pub fn intersects_parts(&self, camera: Option, layers: &RenderLayers) -> bool { - if let (Some(a), Some(b)) = (self.camera, camera) { - if a == b { - return true; - } - } - self.layers.intersects(layers) - } - - /// Returns `true` if `Self` intersects with an [`ExtractedRenderGroups`]. - /// - /// If `extracted` is `None`, then intersections is tested using [`RenderGroups::default`]. - pub fn intersects_extracted(&self, extracted: Option<&ExtractedRenderGroups>) -> bool { - let default_render_groups = RenderGroups::default(); - let render_groups = extracted.map(|i| &**i).unwrap_or(&default_render_groups); - self.intersects(render_groups) - } - - /// Gets the camera affiliation. - pub fn camera(&self) -> Option { - self.camera - } - - /// Gets the internal [`RenderLayers`]. - pub fn layers(&self) -> &RenderLayers { - &self.layers - } - - /// Returns `true` if the internal [`RenderLayers`] is on the heap. - pub fn is_allocated(&self) -> bool { - self.layers.is_allocated() - } -} - -impl From for RenderGroups { - /// Makes a new `RenderGroups` from a [`RenderLayers`]. - fn from(layers: RenderLayers) -> Self { - Self { - layers, - camera: None, - } - } -} - -impl Default for RenderGroups { - /// Equivalent to `Self::from_layer(DEFAULT_RENDER_LAYER)`. - fn default() -> Self { - Self::from_layer(DEFAULT_RENDER_LAYER) - } -} - -/// Stores a [`RenderGroups`] reference or value. +/// Stores a [`RenderLayers`] reference or value. /// /// Useful for unwrapping an optional reference with fallback to a default value. -pub enum RenderGroupsRef<'a> { +pub enum RenderLayersRef<'a> { None, - Ref(&'a RenderGroups), - Val(RenderGroups), + Ref(&'a RenderLayers), + Val(RenderLayers), } -impl<'a> RenderGroupsRef<'a> { +impl<'a> RenderLayersRef<'a> { /// Moves `self` into `other` if `self` is on the heap and `other` is not. /// /// Sets self to [`Self::None`]. /// /// Returns `true` if reclamation occurred. - pub(crate) fn reclaim(&mut self, other: &mut RenderGroups) -> bool { + pub(crate) fn reclaim(&mut self, other: &mut RenderLayers) -> bool { match self { - Self::Val(groups) => { - if !groups.is_allocated() || other.is_allocated() { + Self::Val(layers) => { + if !layers.is_allocated() || other.is_allocated() { return false; } - *other = std::mem::take(groups); + *other = std::mem::take(layers); *self = Self::None; true } @@ -512,168 +325,130 @@ impl<'a> RenderGroupsRef<'a> { } } -impl<'a> Deref for RenderGroupsRef<'a> { - type Target = RenderGroups; +impl<'a> Deref for RenderLayersRef<'a> { + type Target = RenderLayers; fn deref(&self) -> &Self::Target { self.get() } } -impl<'a> RenderGroupsRef<'a> { - /// Gets a reference to the internal [`RenderGroups`]. +impl<'a> RenderLayersRef<'a> { + /// Gets a reference to the internal [`RenderLayers`]. /// /// Panices if in state [`Self::None`]. - pub fn get(&self) -> &RenderGroups { + pub fn get(&self) -> &RenderLayers { match self { Self::None => { - panic!("RenderGroupsRef cannot be dereferenced when empty"); + panic!("RenderLayersRef cannot be dereferenced when empty"); } - Self::Ref(groups) => groups, - Self::Val(groups) => groups, + Self::Ref(layers) => layers, + Self::Val(layers) => layers, } } } -/// Stores a [`RenderGroups`] pointer or value. +/// Stores a [`RenderLayers`] pointer or value. /// -/// Useful as an alternative to [`RenderGroupsRef`] when you can't store a reference, for example within a [`Local`] +/// Useful as an alternative to [`RenderLayersRef`] when you can't store a reference, for example within a [`Local`] /// that buffers a cache that is rewritten every system call. -pub enum RenderGroupsPtr { - Ptr(*const RenderGroups), - Val(RenderGroups), +pub enum RenderLayersPtr { + Ptr(*const RenderLayers), + Val(RenderLayers), } -impl RenderGroupsPtr { - /// Gets a reference to the internal [`RenderGroups`]. +impl RenderLayersPtr { + /// Gets a reference to the internal [`RenderLayers`]. /// /// # Safety /// Safety must be established by the user. - pub unsafe fn get(&self) -> &RenderGroups { + pub unsafe fn get(&self) -> &RenderLayers { match self { // SAFETY: Safety is established by the caller. - Self::Ptr(groups) => unsafe { groups.as_ref().unwrap() }, - Self::Val(groups) => groups, + Self::Ptr(layers) => unsafe { layers.as_ref().unwrap() }, + Self::Val(layers) => layers, } } } -/// Component on camera entities that controls which [`RenderLayers`] are visible to -/// the camera. +/// Component on camera entities that controls which [`RenderLayer`] is visible to the camera. /// -/// A camera will see any entity that satisfies either of these conditions: -/// - The entity is in a [`RenderLayer`] visible to the camera. -/// - The entity has a [`RenderGroups`] component with camera affiliation equal to the camera. +/// Cameras can see *at most* one [`RenderLayer`]. +/// A camera without the `CameraLayer` component will see the [`DEFAULT_RENDER_LAYER`] layer. +/// A camera with [`CameraLayer::empty`] will see no entities. /// -/// Cameras use entities' [`InheritedRenderGroups`] to determine visibility, with a fall-back to the -/// entity's [`RenderGroups`]. If an entity does not have [`InheritedRenderGroups`] -/// or [`RenderGroups`] components, then the camera will only see it if the camera can +/// Cameras use entities' [`InheritedRenderLayers`] to determine visibility, with a fall-back to the +/// entity's [`RenderLayers`]. If an entity does not have [`InheritedRenderLayers`] +/// or [`RenderLayers`] components, then the camera will only see it if the camera can /// view the [`DEFAULT_RENDER_LAYER`] layer. /// -/// A camera without the `CameraView` component will see the [`DEFAULT_RENDER_LAYER`] -/// layer, in addition to any affiliated entities. -/// -/// A default `CameraView` will include the [`DEFAULT_RENDER_LAYER`]. +/// A default `CameraLayer` will contain [`DEFAULT_RENDER_LAYER`]. #[derive(Component, Debug, Clone, PartialEq, Reflect)] #[reflect(Component, Default, PartialEq)] -pub struct CameraView { - layers: RenderLayers, +pub struct CameraLayer { + layer: Option, } -impl CameraView { - /// Makes a new `CameraView` with no visible [`RenderLayer`]. +impl CameraLayer { + /// Makes a new `CameraLayer` with no visible [`RenderLayer`]. pub fn empty() -> Self { - Self { - layers: RenderLayers::empty(), - } + Self { layer: None } } - /// Makes a new `CameraView` with a single [`RenderLayer`] and no camera. - pub fn from_layer(layer: impl Into) -> Self { - Self { - layers: RenderLayers::from_layer(layer), - } - } - - /// Makes a new `CameraView` with an array of [`RenderLayer`] and no camera. - pub fn from_layers + Copy>(layers: &[T]) -> Self { + /// Makes a new `CameraLayer` with a [`RenderLayer`]. + pub fn new(layer: impl Into) -> Self { Self { - layers: RenderLayers::from_layers(layers), + layer: Some(layer.into()), } } - /// Adds a [`RenderLayer`]. - /// - /// See [`RenderLayers::add`]. - pub fn add(&mut self, layer: impl Into) -> &mut Self { - self.layers.add(layer); + /// Sets the [`RenderLayer`]. + pub fn set(&mut self, layer: impl Into) -> &mut Self { + self.layer = Some(layer.into()); self } - /// Removes a [`RenderLayer`]. + /// Removes the current [`RenderLayer`]. /// - /// See [`RenderLayers::remove`]. - pub fn remove(&mut self, layer: impl Into) -> &mut Self { - self.layers.remove(layer); - self - } - - /// Clears all stored render layers without deallocating. + /// The camera will see nothing after this is called. pub fn clear(&mut self) { - self.layers.clear(); + self.layer = None; } - /// Returns a reference to the internal [`RenderLayers`]. - pub fn layers(&self) -> &RenderLayers { - &self.layers + /// Returns the current [`RenderLayer`] if there is one. + pub fn layer(&self) -> Option { + self.layer } - /// Returns an iterator over [`RenderLayer`]. - pub fn iter_layers(&self) -> impl Iterator + '_ { - self.layers.iter() + /// Returns `true` if the specified render layer equals this `CameraLayer`. + pub fn equals(&self, layer: impl Into) -> bool { + self.layer == Some(layer.into()) } - /// Returns `true` if the specified render layer is included in this `CameraView`. - pub fn contains_layer(&self, layer: impl Into) -> bool { - self.layers.contains(layer) + /// Returns `true` if the entity with the specified [`RenderLayers`] is visible + /// to the `camera` that has this `CameraLayer`. + pub fn entity_is_visible(&self, layers: &RenderLayers) -> bool { + let Some(layer) = self.layer else { + return false; + }; + layers.contains(layer) } - /// Returns `true` if the entity with the specified [`RenderGroups`] is visible - /// to the `camera` that has this `CameraView`. + /// Converts the internal [`RenderLayer`] into a [`RenderLayers`]. /// - /// Checks both camera affiliation and [`RenderLayers`] intersection. - pub fn entity_is_visible(&self, camera: Entity, groups: &RenderGroups) -> bool { - if Some(camera) == groups.camera { - return true; + /// Returns an empty [`RenderLayers`] if there is no stored layer. + pub fn get_layers(&self) -> RenderLayers { + match self.layer { + Some(layer) => RenderLayers::from_layer(layer), + None => RenderLayers::empty(), } - self.layers.intersects(&groups.layers) - } - - /// Converts the internal [`RenderLayers`] into a [`RenderGroups`] affiliated - /// with the camera that has this `CameraView`. - pub fn get_groups(&self, camera: Entity) -> RenderGroups { - let mut groups = RenderGroups::from(self.layers.clone()); - groups.set_camera(camera); - groups - } - - /// Returns `true` if the internal [`RenderLayers`] is on the heap. - pub fn is_allocated(&self) -> bool { - self.layers.is_allocated() } } -impl From for CameraView { - /// Makes a new `CameraView` from a [`RenderLayers`]. - fn from(layers: RenderLayers) -> Self { - Self { layers } - } -} - -impl Default for CameraView { - /// Equivalent to `Self::from_layer(DEFAULT_RENDER_LAYER)`. +impl Default for CameraLayer { + /// Equivalent to `Self::new(DEFAULT_RENDER_LAYER)`. fn default() -> Self { - Self::from_layer(DEFAULT_RENDER_LAYER) + Self::new(DEFAULT_RENDER_LAYER) } } @@ -685,7 +460,7 @@ mod rendering_mask_tests { #[test] fn rendering_mask_sanity() { assert_eq!( - RenderLayers::default().num_layers(), + RenderLayers::default().len(), 1, "default layer contains only one layer" ); @@ -694,7 +469,7 @@ mod rendering_mask_tests { "default layer contains default" ); assert_eq!( - RenderLayers::from_layer(1).num_layers(), + RenderLayers::from_layer(1).len(), 1, "from contains 1 layer" ); diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index e398a46d93d24..878c49514ecf3 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -1,7 +1,7 @@ use std::ops::Range; use super::{UiBatch, UiImageBindGroups, UiMeta}; -use crate::DefaultCameraView; +use crate::DefaultCameraLayer; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem}, @@ -26,7 +26,7 @@ pub struct UiPassNode { ), With, >, - default_camera_view_query: QueryState<&'static DefaultCameraView>, + default_camera_view_query: QueryState<&'static DefaultCameraLayer>, } impl UiPassNode { diff --git a/examples/2d/pixel_grid_snap.rs b/examples/2d/pixel_grid_snap.rs index 30a52f0bc8551..0eb143cad15b8 100644 --- a/examples/2d/pixel_grid_snap.rs +++ b/examples/2d/pixel_grid_snap.rs @@ -7,7 +7,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::{CameraView, RenderGroups, RenderLayer}, + view::{CameraLayer, RenderLayer, RenderLayers}, }, sprite::MaterialMesh2dBundle, window::WindowResized, @@ -60,7 +60,7 @@ fn setup_sprite(mut commands: Commands, asset_server: Res) { ..default() }, Rotate, - RenderGroups::from_layer(PIXEL_PERFECT_LAYER), + RenderLayers::from_layer(PIXEL_PERFECT_LAYER), )); // the sample sprite that will be rendered to the high-res "outer world" @@ -71,7 +71,7 @@ fn setup_sprite(mut commands: Commands, asset_server: Res) { ..default() }, Rotate, - RenderGroups::from_layer(HIGH_RES_LAYER), + RenderLayers::from_layer(HIGH_RES_LAYER), )); } @@ -89,7 +89,7 @@ fn setup_mesh( ..default() }, Rotate, - RenderGroups::from_layer(PIXEL_PERFECT_LAYER), + RenderLayers::from_layer(PIXEL_PERFECT_LAYER), )); } @@ -134,7 +134,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { ..default() }, InGameCamera, - CameraView::from_layer(PIXEL_PERFECT_LAYER), + CameraLayer::new(PIXEL_PERFECT_LAYER), )); // spawn the canvas @@ -144,7 +144,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { ..default() }, Canvas, - RenderGroups::from_layer(HIGH_RES_LAYER), + RenderLayers::from_layer(HIGH_RES_LAYER), )); // the "outer" camera renders whatever is on `HIGH_RES_LAYER` to the screen. @@ -152,7 +152,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut>) { commands.spawn(( Camera2dBundle::default(), OuterCamera, - CameraView::from_layer(HIGH_RES_LAYER), + CameraLayer::new(HIGH_RES_LAYER), )); } diff --git a/examples/3d/render_groups.rs b/examples/3d/render_layers.rs similarity index 91% rename from examples/3d/render_groups.rs rename to examples/3d/render_layers.rs index 3a6160b2546e7..7feee93d3d9b9 100644 --- a/examples/3d/render_groups.rs +++ b/examples/3d/render_layers.rs @@ -1,11 +1,11 @@ -//! Load a scene from a glTF file and render it with different render groups. +//! Load a scene from a glTF file and render it with different render layers. use bevy::{ color::palettes, pbr::DirectionalLightShadowMap, prelude::*, render::camera::Viewport, - render::view::{CameraView, PropagateRenderGroups, RenderGroups}, + render::view::{CameraLayer, PropagateRenderLayers, RenderLayers}, window::PrimaryWindow, }; @@ -59,7 +59,7 @@ fn setup( specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 1500.0, }, - CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), + CameraLayer::new(0), Camera1, )) .id(); @@ -92,7 +92,7 @@ fn setup( specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"), intensity: 1500.0, }, - CameraView::from_layers(&[0, 1, 2, 3, 4, 5, 6]), + CameraLayer::new(0), Camera2, )) .id(); @@ -104,7 +104,7 @@ fn setup( material: materials.add(Color::srgb(0.3, 0.5, 0.3)), ..default() }, - RenderGroups::from_layer(0), + RenderLayers::from_layer(0), )); // Text (camera 1) @@ -147,7 +147,7 @@ fn setup( TargetCamera(camera2), )); - // Spawn three copies of the scene, each with a different render group. + // Spawn three copies of the scene, each with a different render layer. for i in 0..3 { commands.spawn(( SceneBundle { @@ -155,12 +155,12 @@ fn setup( scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"), ..default() }, - RenderGroups::from_layer(i + 1), - PropagateRenderGroups::Auto, + RenderLayers::from_layer(i + 1), + PropagateRenderLayers::Auto, )); } - // Spawn three directional lights, each with a different render group. + // Spawn three directional lights, each with a different render layer. let colors = [ palettes::basic::RED, palettes::basic::GREEN, @@ -178,13 +178,13 @@ fn setup( }, ..default() }, - RenderGroups::from_layer(i + 4), + RenderLayers::from_layer(i + 4), )); } } fn toggle_layers_camera1( - mut query_camera: Query<&mut CameraView, With>, + mut query_camera: Query<&mut CameraLayer, With>, keyboard: Res>, ) { let Ok(mut camera_view) = query_camera.get_single_mut() else { @@ -212,7 +212,7 @@ fn toggle_layers_camera1( } fn toggle_layers_camera2( - mut query_camera: Query<&mut CameraView, With>, + mut query_camera: Query<&mut CameraLayer, With>, keyboard: Res>, ) { let Ok(mut camera_view) = query_camera.get_single_mut() else { @@ -239,10 +239,10 @@ fn toggle_layers_camera2( } } -fn toggle_camera_layer(camera_view: &mut CameraView, layer: usize) { - if camera_view.contains_layer(layer) { - camera_view.remove(layer); +fn toggle_camera_layer(camera_view: &mut CameraLayer, layer: usize) { + if camera_view.equals(layer) { + camera_view.clear(); } else { - camera_view.add(layer); + camera_view.set(layer); } } diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 459fd57c81c28..48c302edc8401 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -8,7 +8,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::RenderGroups, + view::RenderLayers, }, }; @@ -70,8 +70,8 @@ fn setup( ..default() }); - // This specifies the group used for the first pass, which will be attached to the first pass camera and cube. - let first_pass_group = RenderGroups::from_layer(1); + // This specifies the layer used for the first pass, which will be attached to the first pass camera and cube. + let first_pass_layer = RenderLayers::from_layer(1); // The cube that will be rendered to the texture. commands.spawn(( @@ -82,19 +82,19 @@ fn setup( ..default() }, FirstPassCube, - first_pass_group.clone(), + first_pass_layer.clone(), )); // Light - // NOTE: we add the light to all groups so it affects both the rendered-to-texture cube, and the cube on which we display the texture - // Setting the group to RenderGroups::from(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. - // Setting the group to RenderGroups::from(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. + // NOTE: we add the light to all layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture + // Setting the layer to RenderLayers::from(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. + // Setting the layer to RenderLayers::from(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. commands.spawn(( PointLightBundle { transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }, - RenderGroups::from_layers(&[0, 1]), + RenderLayers::from_layers(&[0, 1]), )); commands.spawn(( @@ -110,7 +110,7 @@ fn setup( .looking_at(Vec3::ZERO, Vec3::Y), ..default() }, - first_pass_group, + first_pass_layer, )); let cube_size = 4.0; diff --git a/examples/README.md b/examples/README.md index 96e784cbb57e3..59695c266c411 100644 --- a/examples/README.md +++ b/examples/README.md @@ -143,7 +143,7 @@ Example | Description [Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations [Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties [Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes -[Render Groups](../examples/3d/render_groups.rs) | Spawning multiple copies of a scene from a GLTF file, each with a different render group +[Render Layers](../examples/3d/render_layers.rs) | Load a scene from a glTF file and render it with different render layers. [Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images [Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion [Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene From 48dc2ee6b69f68d77e415c9924fc18ee0e36e711 Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 25 Mar 2024 20:31:28 -0500 Subject: [PATCH 51/54] fix examples --- examples/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 59695c266c411..3f749784e7780 100644 --- a/examples/README.md +++ b/examples/README.md @@ -143,7 +143,6 @@ Example | Description [Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations [Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties [Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes -[Render Layers](../examples/3d/render_layers.rs) | Load a scene from a glTF file and render it with different render layers. [Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images [Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion [Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene From fb9d4173ebf0905c8454b453fdd8ffb6e610ecda Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 25 Mar 2024 20:45:00 -0500 Subject: [PATCH 52/54] try to fix render_to_texture example --- Cargo.toml | 4 ++-- examples/3d/render_to_texture.rs | 27 ++++++++++++++++++--------- examples/README.md | 1 + 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8023f0ddaddb0..2eb436375d52d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1994,8 +1994,8 @@ category = "Scene" wasm = false [[example]] -name = "render_groups" -path = "examples/3d/render_groups.rs" +name = "render_layers" +path = "examples/3d/render_layers.rs" doc-scrape-examples = true [package.metadata.example.render_layers] diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 48c302edc8401..593bcb37a1fd5 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -8,7 +8,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, - view::RenderLayers, + view::{CameraLayer, RenderLayer, RenderLayers}, }, }; @@ -71,7 +71,7 @@ fn setup( }); // This specifies the layer used for the first pass, which will be attached to the first pass camera and cube. - let first_pass_layer = RenderLayers::from_layer(1); + let first_pass_layer = RenderLayer(1); // The cube that will be rendered to the texture. commands.spawn(( @@ -82,19 +82,17 @@ fn setup( ..default() }, FirstPassCube, - first_pass_layer.clone(), + RenderLayers::from_layer(first_pass_layer), )); - // Light - // NOTE: we add the light to all layers so it affects both the rendered-to-texture cube, and the cube on which we display the texture - // Setting the layer to RenderLayers::from(0) would cause the main view to be lit, but the rendered-to-texture cube to be unlit. - // Setting the layer to RenderLayers::from(1) would cause the rendered-to-texture cube to be lit, but the main view to be unlit. + // Light for the first pass + // NOTE: Lights only work properly when in one render layer. commands.spawn(( PointLightBundle { transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }, - RenderLayers::from_layers(&[0, 1]), + RenderLayers::from_layer(first_pass_layer) )); commands.spawn(( @@ -110,7 +108,8 @@ fn setup( .looking_at(Vec3::ZERO, Vec3::Y), ..default() }, - first_pass_layer, + // view the first pass layer + CameraLayer::new(first_pass_layer), )); let cube_size = 4.0; @@ -136,6 +135,16 @@ fn setup( MainPassCube, )); + // Light for the main pass cube + // NOTE: Lights only work properly when in one render layer, so we need separate lights for the first and + // main passes. + commands.spawn(( + PointLightBundle { + transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), + ..default() + }, + )); + // The main pass camera. commands.spawn(Camera3dBundle { transform: Transform::from_xyz(0.0, 0.0, 15.0).looking_at(Vec3::ZERO, Vec3::Y), diff --git a/examples/README.md b/examples/README.md index 3f749784e7780..59695c266c411 100644 --- a/examples/README.md +++ b/examples/README.md @@ -143,6 +143,7 @@ Example | Description [Parenting](../examples/3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations [Physically Based Rendering](../examples/3d/pbr.rs) | Demonstrates use of Physically Based Rendering (PBR) properties [Reflection Probes](../examples/3d/reflection_probes.rs) | Demonstrates reflection probes +[Render Layers](../examples/3d/render_layers.rs) | Load a scene from a glTF file and render it with different render layers. [Render to Texture](../examples/3d/render_to_texture.rs) | Shows how to render to a texture, useful for mirrors, UI, or exporting images [Screen Space Ambient Occlusion](../examples/3d/ssao.rs) | A scene showcasing screen space ambient occlusion [Shadow Biases](../examples/3d/shadow_biases.rs) | Demonstrates how shadow biases affect shadows in a 3d scene From 9c826d8784e79da530c386df79a2738f392f145e Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 25 Mar 2024 20:58:03 -0500 Subject: [PATCH 53/54] fmt --- Cargo.toml | 22 +++++++++++----------- examples/3d/render_to_texture.rs | 12 +++++------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2eb436375d52d..d48b179f8d58c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -815,6 +815,17 @@ description = "Demonstrates use of a normal map and depth map for parallax mappi category = "3D Rendering" wasm = true +[[example]] +name = "render_layers" +path = "examples/3d/render_layers.rs" +doc-scrape-examples = true + +[package.metadata.example.render_layers] +name = "Render Layers" +description = "Load a scene from a glTF file and render it with different render layers." +category = "3D Rendering" +wasm = true + [[example]] name = "render_to_texture" path = "examples/3d/render_to_texture.rs" @@ -1993,17 +2004,6 @@ description = "Demonstrates loading from and saving scenes to files" category = "Scene" wasm = false -[[example]] -name = "render_layers" -path = "examples/3d/render_layers.rs" -doc-scrape-examples = true - -[package.metadata.example.render_layers] -name = "Render Layers" -description = "Load a scene from a glTF file and render it with different render layers." -category = "3D Rendering" -wasm = true - # Shaders [[package.metadata.example_category]] name = "Shaders" diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 593bcb37a1fd5..7effd275dea5c 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -92,7 +92,7 @@ fn setup( transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), ..default() }, - RenderLayers::from_layer(first_pass_layer) + RenderLayers::from_layer(first_pass_layer), )); commands.spawn(( @@ -138,12 +138,10 @@ fn setup( // Light for the main pass cube // NOTE: Lights only work properly when in one render layer, so we need separate lights for the first and // main passes. - commands.spawn(( - PointLightBundle { - transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), - ..default() - }, - )); + commands.spawn((PointLightBundle { + transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)), + ..default() + },)); // The main pass camera. commands.spawn(Camera3dBundle { From 6f3909c8fef1a2dfd5317a875171e18d9a27278a Mon Sep 17 00:00:00 2001 From: koe Date: Mon, 1 Apr 2024 08:01:54 -0500 Subject: [PATCH 54/54] remove unsafe blocker from bevy_pbr --- crates/bevy_pbr/src/lib.rs | 1 - crates/bevy_pbr/src/light/mod.rs | 4 ++++ crates/bevy_ui/src/render/render_pass.rs | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index c87f14c6afe05..dd85da675df67 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -1,7 +1,6 @@ // FIXME(3492): remove once docs are ready #![allow(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![forbid(unsafe_code)] #![doc( html_logo_url = "https://bevyengine.org/assets/icon.png", html_favicon_url = "https://bevyengine.org/assets/icon.png" diff --git a/crates/bevy_pbr/src/light/mod.rs b/crates/bevy_pbr/src/light/mod.rs index fdfdb724c5dce..6a0438d6e7569 100644 --- a/crates/bevy_pbr/src/light/mod.rs +++ b/crates/bevy_pbr/src/light/mod.rs @@ -1028,9 +1028,11 @@ impl PointLightAssignmentData { // SAFETY: `PointLightAssignmentData` is only used in `assign_lights_to_clusters`, where it is not reused // between system calls. +#[allow(unsafe_code)] unsafe impl Send for PointLightAssignmentData {} // SAFETY: `PointLightAssignmentData` is only used in `assign_lights_to_clusters`, where it is not reused // between system calls. +#[allow(unsafe_code)] unsafe impl Sync for PointLightAssignmentData {} #[derive(Resource, Default)] @@ -1428,6 +1430,7 @@ pub(crate) fn assign_lights_to_clusters( // check if the light groups overlap the view groups // SAFETY: `lights` is cleared at the start of this system call, and is populated from // immutable queries. + #[allow(unsafe_code)] let light_renderlayers = unsafe { light.render_layers.get() }; if !view_layer.intersects(light_renderlayers) { continue; @@ -2152,6 +2155,7 @@ fn derive_render_layers_ptr_for_light( ) -> RenderLayersPtr { let render_layers = derive_render_layers_ptr(maybe_inherited, maybe_layers); // SAFETY: The pointer points to references within the lifetime of this function. + #[allow(unsafe_code)] let len = unsafe { render_layers.get().len() }; if len > 1 { warn_once!( diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 878c49514ecf3..e398a46d93d24 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -1,7 +1,7 @@ use std::ops::Range; use super::{UiBatch, UiImageBindGroups, UiMeta}; -use crate::DefaultCameraLayer; +use crate::DefaultCameraView; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem}, @@ -26,7 +26,7 @@ pub struct UiPassNode { ), With, >, - default_camera_view_query: QueryState<&'static DefaultCameraLayer>, + default_camera_view_query: QueryState<&'static DefaultCameraView>, } impl UiPassNode {