Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective Fixes #4907. Fixes #838. Fixes #5089. Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114 Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities. Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information. ## Solution Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function. Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate. This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views. Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice. Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.  Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale. ## Follow up work * Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?. * Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset. * Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc). --- ## Changelog * ComputedVisibility now takes hierarchy visibility into account. * 2D, UI and Light entities now use the ComputedVisibility component. ## Migration Guide If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead: ```rust // before (0.7) fn system(query: Query<&Visibility>) { for visibility in query.iter() { if visibility.is_visible { log!("found visible entity"); } } } // after (0.8) fn system(query: Query<&ComputedVisibility>) { for visibility in query.iter() { if visibility.is_visible() { log!("found visible entity"); } } } ``` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
c8aa047cca
commit
40d4992401
@ -73,6 +73,8 @@ pub struct PointLightBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Enables or disables the light
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
/// A component bundle for spot light entities
|
||||
@ -85,6 +87,8 @@ pub struct SpotLightBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Enables or disables the light
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
/// A component bundle for [`DirectionalLight`] entities.
|
||||
@ -97,4 +101,6 @@ pub struct DirectionalLightBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Enables or disables the light
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
@ -150,6 +150,7 @@ impl Plugin for PbrPlugin {
|
||||
assign_lights_to_clusters
|
||||
.label(SimulationLightSystems::AssignLightsToClusters)
|
||||
.after(TransformSystem::TransformPropagate)
|
||||
.after(VisibilitySystems::CheckVisibility)
|
||||
.after(CameraUpdateSystem)
|
||||
.after(ModifiesWindows),
|
||||
)
|
||||
@ -157,6 +158,8 @@ impl Plugin for PbrPlugin {
|
||||
CoreStage::PostUpdate,
|
||||
update_directional_light_frusta
|
||||
.label(SimulationLightSystems::UpdateLightFrusta)
|
||||
// This must run after CheckVisibility because it relies on ComputedVisibility::is_visible()
|
||||
.after(VisibilitySystems::CheckVisibility)
|
||||
.after(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
|
@ -10,7 +10,7 @@ use bevy_render::{
|
||||
primitives::{Aabb, CubemapFrusta, Frustum, Plane, Sphere},
|
||||
render_resource::BufferBindingType,
|
||||
renderer::RenderDevice,
|
||||
view::{ComputedVisibility, RenderLayers, Visibility, VisibleEntities},
|
||||
view::{ComputedVisibility, RenderLayers, VisibleEntities},
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::tracing::warn;
|
||||
@ -793,8 +793,8 @@ pub(crate) fn assign_lights_to_clusters(
|
||||
&mut Clusters,
|
||||
Option<&mut VisiblePointLights>,
|
||||
)>,
|
||||
point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &Visibility)>,
|
||||
spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &Visibility)>,
|
||||
point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &ComputedVisibility)>,
|
||||
spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &ComputedVisibility)>,
|
||||
mut lights: Local<Vec<PointLightAssignmentData>>,
|
||||
mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>,
|
||||
mut max_point_lights_warning_emitted: Local<bool>,
|
||||
@ -811,7 +811,7 @@ pub(crate) fn assign_lights_to_clusters(
|
||||
lights.extend(
|
||||
point_lights_query
|
||||
.iter()
|
||||
.filter(|(.., visibility)| visibility.is_visible)
|
||||
.filter(|(.., visibility)| visibility.is_visible())
|
||||
.map(
|
||||
|(entity, transform, point_light, _visibility)| PointLightAssignmentData {
|
||||
entity,
|
||||
@ -826,7 +826,7 @@ pub(crate) fn assign_lights_to_clusters(
|
||||
lights.extend(
|
||||
spot_lights_query
|
||||
.iter()
|
||||
.filter(|(.., visibility)| visibility.is_visible)
|
||||
.filter(|(.., visibility)| visibility.is_visible())
|
||||
.map(
|
||||
|(entity, transform, spot_light, _visibility)| PointLightAssignmentData {
|
||||
entity,
|
||||
@ -1415,7 +1415,7 @@ pub fn update_directional_light_frusta(
|
||||
&GlobalTransform,
|
||||
&DirectionalLight,
|
||||
&mut Frustum,
|
||||
&Visibility,
|
||||
&ComputedVisibility,
|
||||
),
|
||||
Or<(Changed<GlobalTransform>, Changed<DirectionalLight>)>,
|
||||
>,
|
||||
@ -1424,7 +1424,7 @@ pub fn update_directional_light_frusta(
|
||||
// The frustum is used for culling meshes to the light for shadow mapping
|
||||
// so if shadow mapping is disabled for this light, then the frustum is
|
||||
// not needed.
|
||||
if !directional_light.shadows_enabled || !visibility.is_visible {
|
||||
if !directional_light.shadows_enabled || !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1541,45 +1541,43 @@ pub fn check_light_mesh_visibility(
|
||||
&Frustum,
|
||||
&mut VisibleEntities,
|
||||
Option<&RenderLayers>,
|
||||
&Visibility,
|
||||
&ComputedVisibility,
|
||||
),
|
||||
Without<SpotLight>,
|
||||
>,
|
||||
mut visible_entity_query: Query<
|
||||
(
|
||||
Entity,
|
||||
&Visibility,
|
||||
&mut ComputedVisibility,
|
||||
Option<&RenderLayers>,
|
||||
Option<&Aabb>,
|
||||
Option<&GlobalTransform>,
|
||||
),
|
||||
Without<NotShadowCaster>,
|
||||
(Without<NotShadowCaster>, Without<DirectionalLight>),
|
||||
>,
|
||||
) {
|
||||
// Directonal lights
|
||||
for (directional_light, frustum, mut visible_entities, maybe_view_mask, visibility) in
|
||||
&mut directional_lights
|
||||
// Directional lights
|
||||
for (
|
||||
directional_light,
|
||||
frustum,
|
||||
mut visible_entities,
|
||||
maybe_view_mask,
|
||||
light_computed_visibility,
|
||||
) in &mut directional_lights
|
||||
{
|
||||
visible_entities.entities.clear();
|
||||
|
||||
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
|
||||
if !directional_light.shadows_enabled || !visibility.is_visible {
|
||||
if !directional_light.shadows_enabled || !light_computed_visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
||||
|
||||
for (
|
||||
entity,
|
||||
visibility,
|
||||
mut computed_visibility,
|
||||
maybe_entity_mask,
|
||||
maybe_aabb,
|
||||
maybe_transform,
|
||||
) in &mut visible_entity_query
|
||||
for (entity, mut computed_visibility, maybe_entity_mask, maybe_aabb, maybe_transform) in
|
||||
&mut visible_entity_query
|
||||
{
|
||||
if !visibility.is_visible {
|
||||
if !computed_visibility.is_visible_in_hierarchy() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1595,7 +1593,7 @@ pub fn check_light_mesh_visibility(
|
||||
}
|
||||
}
|
||||
|
||||
computed_visibility.is_visible = true;
|
||||
computed_visibility.set_visible_in_view();
|
||||
visible_entities.entities.push(entity);
|
||||
}
|
||||
|
||||
@ -1631,14 +1629,13 @@ pub fn check_light_mesh_visibility(
|
||||
|
||||
for (
|
||||
entity,
|
||||
visibility,
|
||||
mut computed_visibility,
|
||||
maybe_entity_mask,
|
||||
maybe_aabb,
|
||||
maybe_transform,
|
||||
) in &mut visible_entity_query
|
||||
{
|
||||
if !visibility.is_visible {
|
||||
if !computed_visibility.is_visible_in_hierarchy() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1660,12 +1657,12 @@ pub fn check_light_mesh_visibility(
|
||||
.zip(cubemap_visible_entities.iter_mut())
|
||||
{
|
||||
if frustum.intersects_obb(aabb, &model_to_world, true) {
|
||||
computed_visibility.is_visible = true;
|
||||
computed_visibility.set_visible_in_view();
|
||||
visible_entities.entities.push(entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
computed_visibility.is_visible = true;
|
||||
computed_visibility.set_visible_in_view();
|
||||
for visible_entities in cubemap_visible_entities.iter_mut() {
|
||||
visible_entities.entities.push(entity);
|
||||
}
|
||||
@ -1695,14 +1692,13 @@ pub fn check_light_mesh_visibility(
|
||||
|
||||
for (
|
||||
entity,
|
||||
visibility,
|
||||
mut computed_visibility,
|
||||
maybe_entity_mask,
|
||||
maybe_aabb,
|
||||
maybe_transform,
|
||||
) in visible_entity_query.iter_mut()
|
||||
{
|
||||
if !visibility.is_visible {
|
||||
if !computed_visibility.is_visible_in_hierarchy() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1720,11 +1716,11 @@ pub fn check_light_mesh_visibility(
|
||||
}
|
||||
|
||||
if frustum.intersects_obb(aabb, &model_to_world, true) {
|
||||
computed_visibility.is_visible = true;
|
||||
computed_visibility.set_visible_in_view();
|
||||
visible_entities.entities.push(entity);
|
||||
}
|
||||
} else {
|
||||
computed_visibility.is_visible = true;
|
||||
computed_visibility.set_visible_in_view();
|
||||
visible_entities.entities.push(entity);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,8 @@ use bevy_render::{
|
||||
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||
texture::*,
|
||||
view::{
|
||||
ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility, VisibleEntities,
|
||||
ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms,
|
||||
VisibleEntities,
|
||||
},
|
||||
Extract,
|
||||
};
|
||||
@ -411,8 +412,22 @@ pub fn extract_lights(
|
||||
point_light_shadow_map: Extract<Res<PointLightShadowMap>>,
|
||||
directional_light_shadow_map: Extract<Res<DirectionalLightShadowMap>>,
|
||||
global_point_lights: Extract<Res<GlobalVisiblePointLights>>,
|
||||
point_lights: Extract<Query<(&PointLight, &CubemapVisibleEntities, &GlobalTransform)>>,
|
||||
spot_lights: Extract<Query<(&SpotLight, &VisibleEntities, &GlobalTransform)>>,
|
||||
point_lights: Extract<
|
||||
Query<(
|
||||
&PointLight,
|
||||
&CubemapVisibleEntities,
|
||||
&GlobalTransform,
|
||||
&ComputedVisibility,
|
||||
)>,
|
||||
>,
|
||||
spot_lights: Extract<
|
||||
Query<(
|
||||
&SpotLight,
|
||||
&VisibleEntities,
|
||||
&GlobalTransform,
|
||||
&ComputedVisibility,
|
||||
)>,
|
||||
>,
|
||||
directional_lights: Extract<
|
||||
Query<
|
||||
(
|
||||
@ -420,7 +435,7 @@ pub fn extract_lights(
|
||||
&DirectionalLight,
|
||||
&VisibleEntities,
|
||||
&GlobalTransform,
|
||||
&Visibility,
|
||||
&ComputedVisibility,
|
||||
),
|
||||
Without<SpotLight>,
|
||||
>,
|
||||
@ -447,7 +462,12 @@ pub fn extract_lights(
|
||||
|
||||
let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len);
|
||||
for entity in global_point_lights.iter().copied() {
|
||||
if let Ok((point_light, cubemap_visible_entities, transform)) = point_lights.get(entity) {
|
||||
if let Ok((point_light, cubemap_visible_entities, transform, visibility)) =
|
||||
point_lights.get(entity)
|
||||
{
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
// TODO: This is very much not ideal. We should be able to re-use the vector memory.
|
||||
// However, since exclusive access to the main world in extract is ill-advised, we just clone here.
|
||||
let render_cubemap_visible_entities = cubemap_visible_entities.clone();
|
||||
@ -481,7 +501,10 @@ pub fn extract_lights(
|
||||
|
||||
let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len);
|
||||
for entity in global_point_lights.iter().copied() {
|
||||
if let Ok((spot_light, visible_entities, transform)) = spot_lights.get(entity) {
|
||||
if let Ok((spot_light, visible_entities, transform, visibility)) = spot_lights.get(entity) {
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
// TODO: This is very much not ideal. We should be able to re-use the vector memory.
|
||||
// However, since exclusive access to the main world in extract is ill-advised, we just clone here.
|
||||
let render_visible_entities = visible_entities.clone();
|
||||
@ -522,7 +545,7 @@ pub fn extract_lights(
|
||||
for (entity, directional_light, visible_entities, transform, visibility) in
|
||||
directional_lights.iter()
|
||||
{
|
||||
if !visibility.is_visible {
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ pub fn extract_meshes(
|
||||
) {
|
||||
let mut caster_commands = Vec::with_capacity(*prev_caster_commands_len);
|
||||
let mut not_caster_commands = Vec::with_capacity(*prev_not_caster_commands_len);
|
||||
let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.is_visible);
|
||||
let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.is_visible());
|
||||
|
||||
for (entity, _, transform, handle, not_receiver, not_caster) in visible_meshes {
|
||||
let transform = transform.compute_matrix();
|
||||
@ -224,7 +224,7 @@ pub fn extract_skinned_meshes(
|
||||
let mut last_start = 0;
|
||||
|
||||
for (entity, computed_visibility, skin) in query.iter() {
|
||||
if !computed_visibility.is_visible {
|
||||
if !computed_visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
// PERF: This can be expensive, can we move this to prepare?
|
||||
|
@ -34,6 +34,7 @@ bevy_core = { path = "../bevy_core", version = "0.8.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
|
||||
bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.8.0-dev" }
|
||||
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.8.0-dev" }
|
||||
bevy_log = { path = "../bevy_log", version = "0.8.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.8.0-dev" }
|
||||
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.8.0-dev" }
|
||||
|
@ -200,7 +200,7 @@ fn extract_visible_components<C: ExtractComponent>(
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, computed_visibility, query_item) in query.iter_mut() {
|
||||
if computed_visibility.is_visible {
|
||||
if computed_visibility.is_visible() {
|
||||
values.push((entity, (C::extract_component(query_item),)));
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ pub use render_layers::*;
|
||||
use bevy_app::{CoreStage, Plugin};
|
||||
use bevy_asset::{Assets, Handle};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_hierarchy::{Children, Parent};
|
||||
use bevy_reflect::std_traits::ReflectDefault;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
@ -19,10 +20,16 @@ use crate::{
|
||||
primitives::{Aabb, Frustum, Sphere},
|
||||
};
|
||||
|
||||
/// User indication of whether an entity is visible
|
||||
/// User indication of whether an entity is visible. Propagates down the entity hierarchy.
|
||||
|
||||
/// If an entity is hidden in this way, all [`Children`] (and all of their children and so on) will also be hidden.
|
||||
/// This is done by setting the values of their [`ComputedVisibility`] component.
|
||||
#[derive(Component, Clone, Reflect, Debug)]
|
||||
#[reflect(Component, Default)]
|
||||
pub struct Visibility {
|
||||
/// Indicates whether this entity is visible. Hidden values will propagate down the entity hierarchy.
|
||||
/// If this entity is hidden, all of its descendants will be hidden as well. See [`Children`] and [`Parent`] for
|
||||
/// hierarchy info.
|
||||
pub is_visible: bool,
|
||||
}
|
||||
|
||||
@ -33,15 +40,51 @@ impl Default for Visibility {
|
||||
}
|
||||
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
#[derive(Component, Clone, Reflect, Debug)]
|
||||
#[derive(Component, Clone, Reflect, Debug, Eq, PartialEq, Default)]
|
||||
#[reflect(Component)]
|
||||
pub struct ComputedVisibility {
|
||||
pub is_visible: bool,
|
||||
is_visible_in_hierarchy: bool,
|
||||
is_visible_in_view: bool,
|
||||
}
|
||||
|
||||
impl Default for ComputedVisibility {
|
||||
fn default() -> Self {
|
||||
Self { is_visible: true }
|
||||
impl ComputedVisibility {
|
||||
/// Whether this entity is visible to something this frame. This is true if and only if [`Self::is_visible_in_hierarchy`] and [`Self::is_visible_in_view`]
|
||||
/// are true. This is the canonical method to call to determine if an entity should be drawn.
|
||||
/// This value is updated in [`CoreStage::PostUpdate`] during the [`VisibilitySystems::CheckVisibility`] system label. Reading it from the
|
||||
/// [`CoreStage::Update`] stage will yield the value from the previous frame.
|
||||
#[inline]
|
||||
pub fn is_visible(&self) -> bool {
|
||||
self.is_visible_in_hierarchy && self.is_visible_in_view
|
||||
}
|
||||
|
||||
/// Whether this entity is visible in the entity hierarchy, which is determined by the [`Visibility`] component.
|
||||
/// This takes into account "visibility inheritance". If any of this entity's ancestors (see [`Parent`]) are hidden, this entity
|
||||
/// will be hidden as well. This value is updated in the [`CoreStage::PostUpdate`] stage in the
|
||||
/// [`VisibilitySystems::VisibilityPropagate`] system label.
|
||||
#[inline]
|
||||
pub fn is_visible_in_hierarchy(&self) -> bool {
|
||||
self.is_visible_in_hierarchy
|
||||
}
|
||||
|
||||
/// Whether this entity is visible in _any_ view (Cameras, Lights, etc). Each entity type (and view type) should choose how to set this
|
||||
/// value. For cameras and drawn entities, this will take into account [`RenderLayers`].
|
||||
///
|
||||
/// This value is reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] during [`CoreStage::PostUpdate`].
|
||||
/// Each entity type then chooses how to set this field in the [`CoreStage::PostUpdate`] stage in the
|
||||
/// [`VisibilitySystems::CheckVisibility`] system label. Meshes might use frustum culling to decide if they are visible in a view.
|
||||
/// Other entities might just set this to `true` every frame.
|
||||
#[inline]
|
||||
pub fn is_visible_in_view(&self) -> bool {
|
||||
self.is_visible_in_view
|
||||
}
|
||||
|
||||
/// Sets `is_visible_in_view` to `true`. This is not reversible for a given frame, as it encodes whether or not this is visible in
|
||||
/// _any_ view. This will be automatically reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] and then set
|
||||
/// to the proper value in [`VisibilitySystems::CheckVisibility`]. This should _only_ be set in systems with the [`VisibilitySystems::CheckVisibility`]
|
||||
/// label. Don't call this unless you are defining a custom visibility system. For normal user-defined entity visibility, see [`Visibility`].
|
||||
#[inline]
|
||||
pub fn set_visible_in_view(&mut self) {
|
||||
self.is_visible_in_view = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +131,7 @@ pub enum VisibilitySystems {
|
||||
UpdateOrthographicFrusta,
|
||||
UpdatePerspectiveFrusta,
|
||||
UpdateProjectionFrusta,
|
||||
VisibilityPropagate,
|
||||
/// Label for the [`check_visibility()`] system updating each frame the [`ComputedVisibility`]
|
||||
/// of each entity and the [`VisibleEntities`] of each view.
|
||||
CheckVisibility,
|
||||
@ -121,6 +165,10 @@ impl Plugin for VisibilityPlugin {
|
||||
.label(UpdateProjectionFrusta)
|
||||
.after(TransformSystem::TransformPropagate),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
visibility_propagate_system.label(VisibilityPropagate),
|
||||
)
|
||||
.add_system_to_stage(
|
||||
CoreStage::PostUpdate,
|
||||
check_visibility
|
||||
@ -129,6 +177,7 @@ impl Plugin for VisibilityPlugin {
|
||||
.after(UpdateOrthographicFrusta)
|
||||
.after(UpdatePerspectiveFrusta)
|
||||
.after(UpdateProjectionFrusta)
|
||||
.after(VisibilityPropagate)
|
||||
.after(TransformSystem::TransformPropagate),
|
||||
);
|
||||
}
|
||||
@ -163,6 +212,68 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
|
||||
}
|
||||
}
|
||||
|
||||
fn visibility_propagate_system(
|
||||
mut root_query: Query<
|
||||
(
|
||||
Option<&Children>,
|
||||
&Visibility,
|
||||
&mut ComputedVisibility,
|
||||
Entity,
|
||||
),
|
||||
Without<Parent>,
|
||||
>,
|
||||
mut visibility_query: Query<(&Visibility, &mut ComputedVisibility, &Parent)>,
|
||||
children_query: Query<&Children, (With<Parent>, With<Visibility>, With<ComputedVisibility>)>,
|
||||
) {
|
||||
for (children, visibility, mut computed_visibility, entity) in root_query.iter_mut() {
|
||||
computed_visibility.is_visible_in_hierarchy = visibility.is_visible;
|
||||
// reset "view" visibility here ... if this entity should be drawn a future system should set this to true
|
||||
computed_visibility.is_visible_in_view = false;
|
||||
if let Some(children) = children {
|
||||
for child in children.iter() {
|
||||
let _ = propagate_recursive(
|
||||
computed_visibility.is_visible_in_hierarchy,
|
||||
&mut visibility_query,
|
||||
&children_query,
|
||||
*child,
|
||||
entity,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn propagate_recursive(
|
||||
parent_visible: bool,
|
||||
visibility_query: &mut Query<(&Visibility, &mut ComputedVisibility, &Parent)>,
|
||||
children_query: &Query<&Children, (With<Parent>, With<Visibility>, With<ComputedVisibility>)>,
|
||||
entity: Entity,
|
||||
expected_parent: Entity,
|
||||
// BLOCKED: https://github.com/rust-lang/rust/issues/31436
|
||||
// We use a result here to use the `?` operator. Ideally we'd use a try block instead
|
||||
) -> Result<(), ()> {
|
||||
let is_visible = {
|
||||
let (visibility, mut computed_visibility, child_parent) =
|
||||
visibility_query.get_mut(entity).map_err(drop)?;
|
||||
assert_eq!(
|
||||
child_parent.get(), expected_parent,
|
||||
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||
);
|
||||
computed_visibility.is_visible_in_hierarchy = visibility.is_visible && parent_visible;
|
||||
// reset "view" visibility here ... if this entity should be drawn a future system should set this to true
|
||||
computed_visibility.is_visible_in_view = false;
|
||||
computed_visibility.is_visible_in_hierarchy
|
||||
};
|
||||
|
||||
for child in children_query.get(entity).map_err(drop)?.iter() {
|
||||
let _ = propagate_recursive(is_visible, visibility_query, children_query, *child, entity);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// the batch size used for check_visibility, chosen because this number tends to perform well
|
||||
const VISIBLE_ENTITIES_QUERY_BATCH_SIZE: usize = 1024;
|
||||
|
||||
/// System updating the visibility of entities each frame.
|
||||
///
|
||||
/// The system is labelled with [`VisibilitySystems::CheckVisibility`]. Each frame, it updates the
|
||||
@ -171,39 +282,35 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
|
||||
pub fn check_visibility(
|
||||
mut thread_queues: Local<ThreadLocal<Cell<Vec<Entity>>>>,
|
||||
mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With<Camera>>,
|
||||
mut visible_entity_query: ParamSet<(
|
||||
Query<&mut ComputedVisibility>,
|
||||
Query<(
|
||||
Entity,
|
||||
&Visibility,
|
||||
&mut ComputedVisibility,
|
||||
Option<&RenderLayers>,
|
||||
Option<&Aabb>,
|
||||
Option<&NoFrustumCulling>,
|
||||
Option<&GlobalTransform>,
|
||||
)>,
|
||||
mut visible_aabb_query: Query<(
|
||||
Entity,
|
||||
&mut ComputedVisibility,
|
||||
Option<&RenderLayers>,
|
||||
&Aabb,
|
||||
&GlobalTransform,
|
||||
Option<&NoFrustumCulling>,
|
||||
)>,
|
||||
mut visible_no_aabb_query: Query<
|
||||
(Entity, &mut ComputedVisibility, Option<&RenderLayers>),
|
||||
Without<Aabb>,
|
||||
>,
|
||||
) {
|
||||
// Reset the computed visibility to false
|
||||
for mut computed_visibility in visible_entity_query.p0().iter_mut() {
|
||||
computed_visibility.is_visible = false;
|
||||
}
|
||||
|
||||
for (mut visible_entities, frustum, maybe_view_mask) in &mut view_query {
|
||||
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
||||
visible_entities.entities.clear();
|
||||
visible_entity_query.p1().par_for_each_mut(
|
||||
1024,
|
||||
visible_aabb_query.par_for_each_mut(
|
||||
VISIBLE_ENTITIES_QUERY_BATCH_SIZE,
|
||||
|(
|
||||
entity,
|
||||
visibility,
|
||||
mut computed_visibility,
|
||||
maybe_entity_mask,
|
||||
maybe_aabb,
|
||||
model_aabb,
|
||||
transform,
|
||||
maybe_no_frustum_culling,
|
||||
maybe_transform,
|
||||
)| {
|
||||
if !visibility.is_visible {
|
||||
// skip computing visibility for entities that are configured to be hidden. is_visible_in_view has already been set to false
|
||||
// in visibility_propagate_system
|
||||
if !computed_visibility.is_visible_in_hierarchy() {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -213,9 +320,7 @@ pub fn check_visibility(
|
||||
}
|
||||
|
||||
// If we have an aabb and transform, do frustum culling
|
||||
if let (Some(model_aabb), None, Some(transform)) =
|
||||
(maybe_aabb, maybe_no_frustum_culling, maybe_transform)
|
||||
{
|
||||
if maybe_no_frustum_culling.is_none() {
|
||||
let model = transform.compute_matrix();
|
||||
let model_sphere = Sphere {
|
||||
center: model.transform_point3a(model_aabb.center),
|
||||
@ -231,7 +336,29 @@ pub fn check_visibility(
|
||||
}
|
||||
}
|
||||
|
||||
computed_visibility.is_visible = true;
|
||||
computed_visibility.is_visible_in_view = true;
|
||||
let cell = thread_queues.get_or_default();
|
||||
let mut queue = cell.take();
|
||||
queue.push(entity);
|
||||
cell.set(queue);
|
||||
},
|
||||
);
|
||||
|
||||
visible_no_aabb_query.par_for_each_mut(
|
||||
VISIBLE_ENTITIES_QUERY_BATCH_SIZE,
|
||||
|(entity, mut computed_visibility, maybe_entity_mask)| {
|
||||
// skip computing visibility for entities that are configured to be hidden. is_visible_in_view has already been set to false
|
||||
// in visibility_propagate_system
|
||||
if !computed_visibility.is_visible_in_hierarchy() {
|
||||
return;
|
||||
}
|
||||
|
||||
let entity_mask = maybe_entity_mask.copied().unwrap_or_default();
|
||||
if !view_mask.intersects(&entity_mask) {
|
||||
return;
|
||||
}
|
||||
|
||||
computed_visibility.is_visible_in_view = true;
|
||||
let cell = thread_queues.get_or_default();
|
||||
let mut queue = cell.take();
|
||||
queue.push(entity);
|
||||
@ -244,3 +371,151 @@ pub fn check_visibility(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
use bevy_hierarchy::BuildWorldChildren;
|
||||
|
||||
#[test]
|
||||
fn visibility_propagation() {
|
||||
let mut app = App::new();
|
||||
app.add_system(visibility_propagate_system);
|
||||
|
||||
let root1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((
|
||||
Visibility { is_visible: false },
|
||||
ComputedVisibility::default(),
|
||||
))
|
||||
.id();
|
||||
let root1_child1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
let root1_child2 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((
|
||||
Visibility { is_visible: false },
|
||||
ComputedVisibility::default(),
|
||||
))
|
||||
.id();
|
||||
let root1_child1_grandchild1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
let root1_child2_grandchild1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
|
||||
app.world
|
||||
.entity_mut(root1)
|
||||
.push_children(&[root1_child1, root1_child2]);
|
||||
app.world
|
||||
.entity_mut(root1_child1)
|
||||
.push_children(&[root1_child1_grandchild1]);
|
||||
app.world
|
||||
.entity_mut(root1_child2)
|
||||
.push_children(&[root1_child2_grandchild1]);
|
||||
|
||||
let root2 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
let root2_child1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
let root2_child2 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((
|
||||
Visibility { is_visible: false },
|
||||
ComputedVisibility::default(),
|
||||
))
|
||||
.id();
|
||||
let root2_child1_grandchild1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
let root2_child2_grandchild1 = app
|
||||
.world
|
||||
.spawn()
|
||||
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||
.id();
|
||||
|
||||
app.world
|
||||
.entity_mut(root2)
|
||||
.push_children(&[root2_child1, root2_child2]);
|
||||
app.world
|
||||
.entity_mut(root2_child1)
|
||||
.push_children(&[root2_child1_grandchild1]);
|
||||
app.world
|
||||
.entity_mut(root2_child2)
|
||||
.push_children(&[root2_child2_grandchild1]);
|
||||
|
||||
app.update();
|
||||
|
||||
let is_visible = |e: Entity| {
|
||||
app.world
|
||||
.entity(e)
|
||||
.get::<ComputedVisibility>()
|
||||
.unwrap()
|
||||
.is_visible_in_hierarchy
|
||||
};
|
||||
assert!(
|
||||
!is_visible(root1),
|
||||
"invisibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
!is_visible(root1_child1),
|
||||
"invisibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
!is_visible(root1_child2),
|
||||
"invisibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
!is_visible(root1_child1_grandchild1),
|
||||
"invisibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
!is_visible(root1_child2_grandchild1),
|
||||
"invisibility propagates down tree from root"
|
||||
);
|
||||
|
||||
assert!(
|
||||
is_visible(root2),
|
||||
"visibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
is_visible(root2_child1),
|
||||
"visibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
!is_visible(root2_child2),
|
||||
"visibility propagates down tree from root, but local invisibility is preserved"
|
||||
);
|
||||
assert!(
|
||||
is_visible(root2_child1_grandchild1),
|
||||
"visibility propagates down tree from root"
|
||||
);
|
||||
assert!(
|
||||
!is_visible(root2_child2_grandchild1),
|
||||
"child's invisibility propagates down to grandchild"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
|
||||
|
||||
# other
|
||||
bytemuck = { version = "1.5", features = ["derive"] }
|
||||
fixedbitset = "0.4"
|
||||
guillotiere = "0.6.0"
|
||||
thiserror = "1.0"
|
||||
rectangle-pack = "0.4"
|
||||
|
@ -6,7 +6,7 @@ use bevy_asset::Handle;
|
||||
use bevy_ecs::bundle::Bundle;
|
||||
use bevy_render::{
|
||||
texture::{Image, DEFAULT_IMAGE_HANDLE},
|
||||
view::Visibility,
|
||||
view::{ComputedVisibility, Visibility},
|
||||
};
|
||||
use bevy_transform::components::{GlobalTransform, Transform};
|
||||
|
||||
@ -18,6 +18,8 @@ pub struct SpriteBundle {
|
||||
pub texture: Handle<Image>,
|
||||
/// User indication of whether an entity is visible
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
impl Default for SpriteBundle {
|
||||
@ -28,6 +30,7 @@ impl Default for SpriteBundle {
|
||||
global_transform: Default::default(),
|
||||
texture: DEFAULT_IMAGE_HANDLE.typed(),
|
||||
visibility: Default::default(),
|
||||
computed_visibility: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,4 +47,6 @@ pub struct SpriteSheetBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// User indication of whether an entity is visible
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ pub fn extract_mesh2d(
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, computed_visibility, transform, handle) in query.iter() {
|
||||
if !computed_visibility.is_visible {
|
||||
if !computed_visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
let transform = transform.compute_matrix();
|
||||
|
@ -22,7 +22,9 @@ use bevy_render::{
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, Image},
|
||||
view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility},
|
||||
view::{
|
||||
ComputedVisibility, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities,
|
||||
},
|
||||
Extract,
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
@ -30,6 +32,7 @@ use bevy_utils::FloatOrd;
|
||||
use bevy_utils::HashMap;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use copyless::VecHelper;
|
||||
use fixedbitset::FixedBitSet;
|
||||
|
||||
pub struct SpritePipeline {
|
||||
view_layout: BindGroupLayout,
|
||||
@ -172,6 +175,7 @@ impl SpecializedRenderPipeline for SpritePipeline {
|
||||
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct ExtractedSprite {
|
||||
pub entity: Entity,
|
||||
pub transform: GlobalTransform,
|
||||
pub color: Color,
|
||||
/// Select an area of the texture
|
||||
@ -222,10 +226,19 @@ pub fn extract_sprite_events(
|
||||
pub fn extract_sprites(
|
||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||
sprite_query: Extract<Query<(&Visibility, &Sprite, &GlobalTransform, &Handle<Image>)>>,
|
||||
sprite_query: Extract<
|
||||
Query<(
|
||||
Entity,
|
||||
&ComputedVisibility,
|
||||
&Sprite,
|
||||
&GlobalTransform,
|
||||
&Handle<Image>,
|
||||
)>,
|
||||
>,
|
||||
atlas_query: Extract<
|
||||
Query<(
|
||||
&Visibility,
|
||||
Entity,
|
||||
&ComputedVisibility,
|
||||
&TextureAtlasSprite,
|
||||
&GlobalTransform,
|
||||
&Handle<TextureAtlas>,
|
||||
@ -233,12 +246,13 @@ pub fn extract_sprites(
|
||||
>,
|
||||
) {
|
||||
extracted_sprites.sprites.clear();
|
||||
for (visibility, sprite, transform, handle) in sprite_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
for (entity, visibility, sprite, transform, handle) in sprite_query.iter() {
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
// PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
|
||||
extracted_sprites.sprites.alloc().init(ExtractedSprite {
|
||||
entity,
|
||||
color: sprite.color,
|
||||
transform: *transform,
|
||||
// Use the full texture
|
||||
@ -251,13 +265,14 @@ pub fn extract_sprites(
|
||||
anchor: sprite.anchor.as_vec(),
|
||||
});
|
||||
}
|
||||
for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
for (entity, visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) {
|
||||
let rect = Some(texture_atlas.textures[atlas_sprite.index as usize]);
|
||||
extracted_sprites.sprites.alloc().init(ExtractedSprite {
|
||||
entity,
|
||||
color: atlas_sprite.color,
|
||||
transform: *transform,
|
||||
// Select the area in the texture atlas
|
||||
@ -334,6 +349,7 @@ pub struct ImageBindGroups {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn queue_sprites(
|
||||
mut commands: Commands,
|
||||
mut view_entities: Local<FixedBitSet>,
|
||||
draw_functions: Res<DrawFunctions<Transparent2d>>,
|
||||
render_device: Res<RenderDevice>,
|
||||
render_queue: Res<RenderQueue>,
|
||||
@ -346,7 +362,7 @@ pub fn queue_sprites(
|
||||
gpu_images: Res<RenderAssets<Image>>,
|
||||
msaa: Res<Msaa>,
|
||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||
mut views: Query<&mut RenderPhase<Transparent2d>>,
|
||||
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
|
||||
events: Res<SpriteAssetEvents>,
|
||||
) {
|
||||
// If an image has changed, the GpuImage has (probably) changed
|
||||
@ -388,26 +404,27 @@ pub fn queue_sprites(
|
||||
let mut index = 0;
|
||||
let mut colored_index = 0;
|
||||
|
||||
// FIXME: VisibleEntities is ignored
|
||||
for mut transparent_phase in &mut views {
|
||||
let extracted_sprites = &mut extracted_sprites.sprites;
|
||||
let image_bind_groups = &mut *image_bind_groups;
|
||||
let extracted_sprites = &mut extracted_sprites.sprites;
|
||||
// Sort sprites by z for correct transparency and then by handle to improve batching
|
||||
// NOTE: This can be done independent of views by reasonably assuming that all 2D views look along the negative-z axis in world space
|
||||
extracted_sprites.sort_unstable_by(|a, b| {
|
||||
match a
|
||||
.transform
|
||||
.translation
|
||||
.z
|
||||
.partial_cmp(&b.transform.translation.z)
|
||||
{
|
||||
Some(Ordering::Equal) | None => a.image_handle_id.cmp(&b.image_handle_id),
|
||||
Some(other) => other,
|
||||
}
|
||||
});
|
||||
let image_bind_groups = &mut *image_bind_groups;
|
||||
|
||||
for (visible_entities, mut transparent_phase) in &mut views {
|
||||
view_entities.clear();
|
||||
view_entities.extend(visible_entities.entities.iter().map(|e| e.id() as usize));
|
||||
transparent_phase.items.reserve(extracted_sprites.len());
|
||||
|
||||
// Sort sprites by z for correct transparency and then by handle to improve batching
|
||||
extracted_sprites.sort_unstable_by(|a, b| {
|
||||
match a
|
||||
.transform
|
||||
.translation
|
||||
.z
|
||||
.partial_cmp(&b.transform.translation.z)
|
||||
{
|
||||
Some(Ordering::Equal) | None => a.image_handle_id.cmp(&b.image_handle_id),
|
||||
Some(other) => other,
|
||||
}
|
||||
});
|
||||
|
||||
// Impossible starting values that will be replaced on the first iteration
|
||||
let mut current_batch = SpriteBatch {
|
||||
image_handle_id: HandleId::Id(Uuid::nil(), u64::MAX),
|
||||
@ -420,7 +437,10 @@ pub fn queue_sprites(
|
||||
// Compatible items share the same entity.
|
||||
// Batches are merged later (in `batch_phase_system()`), so that they can be interrupted
|
||||
// by any other phase item (and they can interrupt other items from batching).
|
||||
for extracted_sprite in extracted_sprites {
|
||||
for extracted_sprite in extracted_sprites.iter() {
|
||||
if !view_entities.contains(extracted_sprite.entity.id() as usize) {
|
||||
continue;
|
||||
}
|
||||
let new_batch = SpriteBatch {
|
||||
image_handle_id: extracted_sprite.image_handle_id,
|
||||
colored: extracted_sprite.color != Color::WHITE,
|
||||
|
@ -10,7 +10,11 @@ use bevy_ecs::{
|
||||
};
|
||||
use bevy_math::{Vec2, Vec3};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_render::{texture::Image, view::Visibility, Extract};
|
||||
use bevy_render::{
|
||||
texture::Image,
|
||||
view::{ComputedVisibility, Visibility},
|
||||
Extract,
|
||||
};
|
||||
use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas};
|
||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
use bevy_utils::HashSet;
|
||||
@ -58,6 +62,7 @@ pub struct Text2dBundle {
|
||||
pub text_2d_size: Text2dSize,
|
||||
pub text_2d_bounds: Text2dBounds,
|
||||
pub visibility: Visibility,
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
pub fn extract_text2d_sprite(
|
||||
@ -65,11 +70,20 @@ pub fn extract_text2d_sprite(
|
||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||
text_pipeline: Extract<Res<DefaultTextPipeline>>,
|
||||
windows: Extract<Res<Windows>>,
|
||||
text2d_query: Extract<Query<(Entity, &Visibility, &Text, &GlobalTransform, &Text2dSize)>>,
|
||||
text2d_query: Extract<
|
||||
Query<(
|
||||
Entity,
|
||||
&ComputedVisibility,
|
||||
&Text,
|
||||
&GlobalTransform,
|
||||
&Text2dSize,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
|
||||
for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
|
||||
for (entity, computed_visibility, text, transform, calculated_size) in text2d_query.iter() {
|
||||
if !computed_visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
let (width, height) = (calculated_size.size.x, calculated_size.size.y);
|
||||
@ -108,6 +122,7 @@ pub fn extract_text2d_sprite(
|
||||
let transform = text_transform.mul_transform(glyph_transform);
|
||||
|
||||
extracted_sprites.sprites.push(ExtractedSprite {
|
||||
entity,
|
||||
transform,
|
||||
color,
|
||||
rect,
|
||||
|
@ -9,7 +9,10 @@ use bevy_ecs::{
|
||||
prelude::{Component, With},
|
||||
query::QueryItem,
|
||||
};
|
||||
use bevy_render::{camera::Camera, extract_component::ExtractComponent, view::Visibility};
|
||||
use bevy_render::{
|
||||
camera::Camera, extract_component::ExtractComponent, prelude::ComputedVisibility,
|
||||
view::Visibility,
|
||||
};
|
||||
use bevy_text::Text;
|
||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
|
||||
@ -32,6 +35,8 @@ pub struct NodeBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Describes the visibility properties of the node
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
/// A UI node that is an image
|
||||
@ -57,6 +62,8 @@ pub struct ImageBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Describes the visibility properties of the node
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
/// A UI node that is text
|
||||
@ -78,6 +85,8 @@ pub struct TextBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Describes the visibility properties of the node
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
impl Default for TextBundle {
|
||||
@ -91,6 +100,7 @@ impl Default for TextBundle {
|
||||
transform: Default::default(),
|
||||
global_transform: Default::default(),
|
||||
visibility: Default::default(),
|
||||
computed_visibility: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,6 +128,8 @@ pub struct ButtonBundle {
|
||||
pub global_transform: GlobalTransform,
|
||||
/// Describes the visibility properties of the node
|
||||
pub visibility: Visibility,
|
||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||
pub computed_visibility: ComputedVisibility,
|
||||
}
|
||||
|
||||
impl Default for ButtonBundle {
|
||||
@ -133,6 +145,7 @@ impl Default for ButtonBundle {
|
||||
transform: Default::default(),
|
||||
global_transform: Default::default(),
|
||||
visibility: Default::default(),
|
||||
computed_visibility: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use bevy_render::{
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::Image,
|
||||
view::{ExtractedView, ViewUniforms, Visibility},
|
||||
view::{ComputedVisibility, ExtractedView, ViewUniforms},
|
||||
Extract, RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas};
|
||||
@ -182,14 +182,14 @@ pub fn extract_uinodes(
|
||||
&GlobalTransform,
|
||||
&UiColor,
|
||||
&UiImage,
|
||||
&Visibility,
|
||||
&ComputedVisibility,
|
||||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
extracted_uinodes.uinodes.clear();
|
||||
for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
let image = image.0.clone_weak();
|
||||
@ -277,14 +277,14 @@ pub fn extract_text_uinodes(
|
||||
&Node,
|
||||
&GlobalTransform,
|
||||
&Text,
|
||||
&Visibility,
|
||||
&ComputedVisibility,
|
||||
Option<&CalculatedClip>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
|
||||
for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() {
|
||||
if !visibility.is_visible {
|
||||
if !visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
// Skip if size is set to zero (e.g. when a parent is set to `Display::None`)
|
||||
|
@ -292,7 +292,7 @@ pub fn extract_colored_mesh2d(
|
||||
) {
|
||||
let mut values = Vec::with_capacity(*previous_len);
|
||||
for (entity, computed_visibility) in query.iter() {
|
||||
if !computed_visibility.is_visible {
|
||||
if !computed_visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
values.push((entity, (ColoredMesh2d,)));
|
||||
|
@ -173,7 +173,7 @@ fn print_mesh_count(
|
||||
info!(
|
||||
"Meshes: {} - Visible Meshes {}",
|
||||
sprites.iter().len(),
|
||||
sprites.iter().filter(|(_, cv)| cv.is_visible).count(),
|
||||
sprites.iter().filter(|(_, cv)| cv.is_visible()).count(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user