Remove the type parameter from check_visibility, and only invoke it once. (#16812)
Currently, `check_visibility` is parameterized over a query filter that specifies the type of potentially-visible object. This has the unfortunate side effect that we need a separate system, `mark_view_visibility_as_changed_if_necessary`, to trigger view visibility change detection. That system is quite slow because it must iterate sequentially over all entities in the scene. This PR moves the query filter from `check_visibility` to a new component, `VisibilityClass`. `VisibilityClass` stores a list of type IDs, each corresponding to one of the query filters we used to use. Because `check_visibility` is no longer specialized to the query filter at the type level, Bevy now only needs to invoke it once, leading to better performance as `check_visibility` can do change detection on the fly rather than delegating it to a separate system. This commit also has ergonomic improvements, as there's no need for applications that want to add their own custom renderable components to add specializations of the `check_visibility` system to the schedule. Instead, they only need to ensure that the `ViewVisibility` component is properly kept up to date. The recommended way to do this, and the way that's demonstrated in the `custom_phase_item` and `specialized_mesh_pipeline` examples, is to make `ViewVisibility` a required component and to add the type ID to it in a component add hook. This patch does this for `Mesh3d`, `Mesh2d`, `Sprite`, `Light`, and `Node`, which means that most app code doesn't need to change at all. Note that, although this patch has a large impact on the performance of visibility determination, it doesn't actually improve the end-to-end frame time of `many_cubes`. That's because the render world was already effectively hiding the latency from `mark_view_visibility_as_changed_if_necessary`. This patch is, however, necessary for *further* improvements to `many_cubes` performance. `many_cubes` trace before:  `many_cubes` trace after:  ## Migration Guide * `check_visibility` no longer takes a `QueryFilter`, and there's no need to add it manually to your app schedule anymore for custom rendering items. Instead, entities with custom renderable components should add the appropriate type IDs to `VisibilityClass`. See `custom_phase_item` for an example.
This commit is contained in:
parent
ac1faf073f
commit
40df1ea4b6
@ -132,7 +132,7 @@ use bevy_render::{
|
|||||||
render_resource::Shader,
|
render_resource::Shader,
|
||||||
sync_component::SyncComponentPlugin,
|
sync_component::SyncComponentPlugin,
|
||||||
texture::GpuImage,
|
texture::GpuImage,
|
||||||
view::{check_visibility, VisibilitySystems},
|
view::VisibilitySystems,
|
||||||
ExtractSchedule, Render, RenderApp, RenderSet,
|
ExtractSchedule, Render, RenderApp, RenderSet,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -383,8 +383,7 @@ impl Plugin for PbrPlugin {
|
|||||||
.in_set(SimulationLightSystems::AssignLightsToClusters)
|
.in_set(SimulationLightSystems::AssignLightsToClusters)
|
||||||
.after(TransformSystem::TransformPropagate)
|
.after(TransformSystem::TransformPropagate)
|
||||||
.after(VisibilitySystems::CheckVisibility)
|
.after(VisibilitySystems::CheckVisibility)
|
||||||
.after(CameraUpdateSystem)
|
.after(CameraUpdateSystem),
|
||||||
.ambiguous_with(VisibilitySystems::VisibilityChangeDetect),
|
|
||||||
clear_directional_light_cascades
|
clear_directional_light_cascades
|
||||||
.in_set(SimulationLightSystems::UpdateDirectionalLightCascades)
|
.in_set(SimulationLightSystems::UpdateDirectionalLightCascades)
|
||||||
.after(TransformSystem::TransformPropagate)
|
.after(TransformSystem::TransformPropagate)
|
||||||
@ -398,8 +397,7 @@ impl Plugin for PbrPlugin {
|
|||||||
// We assume that no entity will be both a directional light and a spot light,
|
// We assume that no entity will be both a directional light and a spot light,
|
||||||
// so these systems will run independently of one another.
|
// so these systems will run independently of one another.
|
||||||
// FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481.
|
// FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481.
|
||||||
.ambiguous_with(update_spot_light_frusta)
|
.ambiguous_with(update_spot_light_frusta),
|
||||||
.ambiguous_with(VisibilitySystems::VisibilityChangeDetect),
|
|
||||||
update_point_light_frusta
|
update_point_light_frusta
|
||||||
.in_set(SimulationLightSystems::UpdateLightFrusta)
|
.in_set(SimulationLightSystems::UpdateLightFrusta)
|
||||||
.after(TransformSystem::TransformPropagate)
|
.after(TransformSystem::TransformPropagate)
|
||||||
@ -408,7 +406,6 @@ impl Plugin for PbrPlugin {
|
|||||||
.in_set(SimulationLightSystems::UpdateLightFrusta)
|
.in_set(SimulationLightSystems::UpdateLightFrusta)
|
||||||
.after(TransformSystem::TransformPropagate)
|
.after(TransformSystem::TransformPropagate)
|
||||||
.after(SimulationLightSystems::AssignLightsToClusters),
|
.after(SimulationLightSystems::AssignLightsToClusters),
|
||||||
check_visibility::<WithLight>.in_set(VisibilitySystems::CheckVisibility),
|
|
||||||
(
|
(
|
||||||
check_dir_light_mesh_visibility,
|
check_dir_light_mesh_visibility,
|
||||||
check_point_light_mesh_visibility,
|
check_point_light_mesh_visibility,
|
||||||
@ -420,8 +417,7 @@ impl Plugin for PbrPlugin {
|
|||||||
// NOTE: This MUST be scheduled AFTER the core renderer visibility check
|
// NOTE: This MUST be scheduled AFTER the core renderer visibility check
|
||||||
// because that resets entity `ViewVisibility` for the first view
|
// because that resets entity `ViewVisibility` for the first view
|
||||||
// which would override any results from this otherwise
|
// which would override any results from this otherwise
|
||||||
.after(VisibilitySystems::CheckVisibility)
|
.after(VisibilitySystems::CheckVisibility),
|
||||||
.ambiguous_with(VisibilitySystems::VisibilityChangeDetect),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use bevy_render::view::Visibility;
|
use bevy_render::view::{self, Visibility};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -57,8 +57,10 @@ use super::*;
|
|||||||
CascadeShadowConfig,
|
CascadeShadowConfig,
|
||||||
CascadesVisibleEntities,
|
CascadesVisibleEntities,
|
||||||
Transform,
|
Transform,
|
||||||
Visibility
|
Visibility,
|
||||||
|
VisibilityClass
|
||||||
)]
|
)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<LightVisibilityClass>)]
|
||||||
pub struct DirectionalLight {
|
pub struct DirectionalLight {
|
||||||
/// The color of the light.
|
/// The color of the light.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -13,8 +13,8 @@ use bevy_render::{
|
|||||||
mesh::Mesh3d,
|
mesh::Mesh3d,
|
||||||
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, Sphere},
|
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, Sphere},
|
||||||
view::{
|
view::{
|
||||||
InheritedVisibility, NoFrustumCulling, RenderLayers, ViewVisibility, VisibilityRange,
|
InheritedVisibility, NoFrustumCulling, RenderLayers, ViewVisibility, VisibilityClass,
|
||||||
VisibleEntityRanges,
|
VisibilityRange, VisibleEntityRanges,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::{GlobalTransform, Transform};
|
use bevy_transform::components::{GlobalTransform, Transform};
|
||||||
@ -503,6 +503,9 @@ pub enum ShadowFilteringMethod {
|
|||||||
Temporal,
|
Temporal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The [`VisibilityClass`] used for all lights (point, directional, and spot).
|
||||||
|
pub struct LightVisibilityClass;
|
||||||
|
|
||||||
/// System sets used to run light-related systems.
|
/// System sets used to run light-related systems.
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
|
||||||
pub enum SimulationLightSystems {
|
pub enum SimulationLightSystems {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use bevy_render::view::Visibility;
|
use bevy_render::view::{self, Visibility};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -21,7 +21,14 @@ use super::*;
|
|||||||
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lumen_(unit)#Lighting)
|
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lumen_(unit)#Lighting)
|
||||||
#[derive(Component, Debug, Clone, Copy, Reflect)]
|
#[derive(Component, Debug, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component, Default, Debug)]
|
#[reflect(Component, Default, Debug)]
|
||||||
#[require(CubemapFrusta, CubemapVisibleEntities, Transform, Visibility)]
|
#[require(
|
||||||
|
CubemapFrusta,
|
||||||
|
CubemapVisibleEntities,
|
||||||
|
Transform,
|
||||||
|
Visibility,
|
||||||
|
VisibilityClass
|
||||||
|
)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<LightVisibilityClass>)]
|
||||||
pub struct PointLight {
|
pub struct PointLight {
|
||||||
/// The color of this light source.
|
/// The color of this light source.
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use bevy_render::view::Visibility;
|
use bevy_render::view::{self, Visibility};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -9,7 +9,8 @@ use super::*;
|
|||||||
/// the transform, and can be specified with [`Transform::looking_at`](Transform::looking_at).
|
/// the transform, and can be specified with [`Transform::looking_at`](Transform::looking_at).
|
||||||
#[derive(Component, Debug, Clone, Copy, Reflect)]
|
#[derive(Component, Debug, Clone, Copy, Reflect)]
|
||||||
#[reflect(Component, Default, Debug)]
|
#[reflect(Component, Default, Debug)]
|
||||||
#[require(Frustum, VisibleMeshEntities, Transform, Visibility)]
|
#[require(Frustum, VisibleMeshEntities, Transform, Visibility, VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<LightVisibilityClass>)]
|
||||||
pub struct SpotLight {
|
pub struct SpotLight {
|
||||||
/// The color of the light.
|
/// The color of the light.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -780,7 +780,7 @@ pub fn queue_material_meshes<M: Material>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let rangefinder = view.rangefinder3d();
|
let rangefinder = view.rangefinder3d();
|
||||||
for (render_entity, visible_entity) in visible_entities.iter::<With<Mesh3d>>() {
|
for (render_entity, visible_entity) in visible_entities.iter::<Mesh3d>() {
|
||||||
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -59,7 +59,7 @@ use self::{
|
|||||||
visibility_buffer_raster_node::MeshletVisibilityBufferRasterPassNode,
|
visibility_buffer_raster_node::MeshletVisibilityBufferRasterPassNode,
|
||||||
};
|
};
|
||||||
use crate::{graph::NodePbr, Material, MeshMaterial3d, PreviousGlobalTransform};
|
use crate::{graph::NodePbr, Material, MeshMaterial3d, PreviousGlobalTransform};
|
||||||
use bevy_app::{App, Plugin, PostUpdate};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::{load_internal_asset, AssetApp, AssetId, Handle};
|
use bevy_asset::{load_internal_asset, AssetApp, AssetId, Handle};
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_3d::graph::{Core3d, Node3d},
|
core_3d::graph::{Core3d, Node3d},
|
||||||
@ -70,7 +70,6 @@ use bevy_ecs::{
|
|||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
component::{require, Component},
|
component::{require, Component},
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
prelude::With,
|
|
||||||
query::Has,
|
query::Has,
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
schedule::IntoSystemConfigs,
|
schedule::IntoSystemConfigs,
|
||||||
@ -83,8 +82,8 @@ use bevy_render::{
|
|||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
settings::WgpuFeatures,
|
settings::WgpuFeatures,
|
||||||
view::{
|
view::{
|
||||||
check_visibility, prepare_view_targets, InheritedVisibility, Msaa, ViewVisibility,
|
self, prepare_view_targets, InheritedVisibility, Msaa, ViewVisibility, Visibility,
|
||||||
Visibility, VisibilitySystems,
|
VisibilityClass,
|
||||||
},
|
},
|
||||||
ExtractSchedule, Render, RenderApp, RenderSet,
|
ExtractSchedule, Render, RenderApp, RenderSet,
|
||||||
};
|
};
|
||||||
@ -219,11 +218,7 @@ impl Plugin for MeshletPlugin {
|
|||||||
);
|
);
|
||||||
|
|
||||||
app.init_asset::<MeshletMesh>()
|
app.init_asset::<MeshletMesh>()
|
||||||
.register_asset_loader(MeshletMeshLoader)
|
.register_asset_loader(MeshletMeshLoader);
|
||||||
.add_systems(
|
|
||||||
PostUpdate,
|
|
||||||
check_visibility::<With<MeshletMesh3d>>.in_set(VisibilitySystems::CheckVisibility),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(&self, app: &mut App) {
|
fn finish(&self, app: &mut App) {
|
||||||
@ -303,7 +298,8 @@ impl Plugin for MeshletPlugin {
|
|||||||
/// The meshlet mesh equivalent of [`bevy_render::mesh::Mesh3d`].
|
/// The meshlet mesh equivalent of [`bevy_render::mesh::Mesh3d`].
|
||||||
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
#[require(Transform, PreviousGlobalTransform, Visibility)]
|
#[require(Transform, PreviousGlobalTransform, Visibility, VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<MeshletMesh3d>)]
|
||||||
pub struct MeshletMesh3d(pub Handle<MeshletMesh>);
|
pub struct MeshletMesh3d(pub Handle<MeshletMesh>);
|
||||||
|
|
||||||
impl From<MeshletMesh3d> for AssetId<MeshletMesh> {
|
impl From<MeshletMesh3d> for AssetId<MeshletMesh> {
|
||||||
|
|||||||
@ -842,7 +842,7 @@ pub fn queue_prepass_material_meshes<M: Material>(
|
|||||||
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
|
view_key |= MeshPipelineKey::MOTION_VECTOR_PREPASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (render_entity, visible_entity) in visible_entities.iter::<With<Mesh3d>>() {
|
for (render_entity, visible_entity) in visible_entities.iter::<Mesh3d>() {
|
||||||
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
use crate::{mesh::Mesh, view::Visibility};
|
use crate::{
|
||||||
|
mesh::Mesh,
|
||||||
|
view::{self, Visibility, VisibilityClass},
|
||||||
|
};
|
||||||
use bevy_asset::{AssetId, Handle};
|
use bevy_asset::{AssetId, Handle};
|
||||||
use bevy_derive::{Deref, DerefMut};
|
use bevy_derive::{Deref, DerefMut};
|
||||||
use bevy_ecs::{component::Component, prelude::require, reflect::ReflectComponent};
|
use bevy_ecs::{component::Component, prelude::require, reflect::ReflectComponent};
|
||||||
@ -35,7 +38,8 @@ use derive_more::derive::From;
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
#[require(Transform, Visibility)]
|
#[require(Transform, Visibility, VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<Mesh2d>)]
|
||||||
pub struct Mesh2d(pub Handle<Mesh>);
|
pub struct Mesh2d(pub Handle<Mesh>);
|
||||||
|
|
||||||
impl From<Mesh2d> for AssetId<Mesh> {
|
impl From<Mesh2d> for AssetId<Mesh> {
|
||||||
@ -82,7 +86,8 @@ impl From<&Mesh2d> for AssetId<Mesh> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
#[require(Transform, Visibility)]
|
#[require(Transform, Visibility, VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<Mesh3d>)]
|
||||||
pub struct Mesh3d(pub Handle<Mesh>);
|
pub struct Mesh3d(pub Handle<Mesh>);
|
||||||
|
|
||||||
impl From<Mesh3d> for AssetId<Mesh> {
|
impl From<Mesh3d> for AssetId<Mesh> {
|
||||||
|
|||||||
@ -5,18 +5,21 @@ mod render_layers;
|
|||||||
|
|
||||||
use core::any::TypeId;
|
use core::any::TypeId;
|
||||||
|
|
||||||
|
use bevy_ecs::component::ComponentId;
|
||||||
use bevy_ecs::entity::EntityHashSet;
|
use bevy_ecs::entity::EntityHashSet;
|
||||||
|
use bevy_ecs::world::DeferredWorld;
|
||||||
|
use derive_more::derive::{Deref, DerefMut};
|
||||||
pub use range::*;
|
pub use range::*;
|
||||||
pub use render_layers::*;
|
pub use render_layers::*;
|
||||||
|
|
||||||
use bevy_app::{Plugin, PostUpdate};
|
use bevy_app::{Plugin, PostUpdate};
|
||||||
use bevy_asset::Assets;
|
use bevy_asset::Assets;
|
||||||
use bevy_derive::{Deref, DerefMut};
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_ecs::{change_detection::DetectChangesMut as _, prelude::*, query::QueryFilter};
|
|
||||||
use bevy_hierarchy::{Children, Parent};
|
use bevy_hierarchy::{Children, Parent};
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||||
use bevy_transform::{components::GlobalTransform, TransformSystem};
|
use bevy_transform::{components::GlobalTransform, TransformSystem};
|
||||||
use bevy_utils::{Parallel, TypeIdMap};
|
use bevy_utils::{Parallel, TypeIdMap};
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use super::NoCpuCulling;
|
use super::NoCpuCulling;
|
||||||
use crate::sync_world::MainEntity;
|
use crate::sync_world::MainEntity;
|
||||||
@ -126,6 +129,34 @@ impl InheritedVisibility {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A bucket into which we group entities for the purposes of visibility.
|
||||||
|
///
|
||||||
|
/// Bevy's various rendering subsystems (3D, 2D, UI, etc.) want to be able to
|
||||||
|
/// quickly winnow the set of entities to only those that the subsystem is
|
||||||
|
/// tasked with rendering, to avoid spending time examining irrelevant entities.
|
||||||
|
/// At the same time, Bevy wants the [`check_visibility`] system to determine
|
||||||
|
/// all entities' visibilities at the same time, regardless of what rendering
|
||||||
|
/// subsystem is responsible for drawing them. Additionally, your application
|
||||||
|
/// may want to add more types of renderable objects that Bevy determines
|
||||||
|
/// visibility for just as it does for Bevy's built-in objects.
|
||||||
|
///
|
||||||
|
/// The solution to this problem is *visibility classes*. A visibility class is
|
||||||
|
/// a type, typically the type of a component, that represents the subsystem
|
||||||
|
/// that renders it: for example, `Mesh3d`, `Mesh2d`, and `Sprite`. The
|
||||||
|
/// [`VisibilityClass`] component stores the visibility class or classes that
|
||||||
|
/// the entity belongs to. (Generally, an object will belong to only one
|
||||||
|
/// visibility class, but in rare cases it may belong to multiple.)
|
||||||
|
///
|
||||||
|
/// When adding a new renderable component, you'll typically want to write an
|
||||||
|
/// add-component hook that adds the type ID of that component to the
|
||||||
|
/// [`VisibilityClass`] array. See `custom_phase_item` for an example.
|
||||||
|
//
|
||||||
|
// Note: This can't be a `ComponentId` because the visibility classes are copied
|
||||||
|
// into the render world, and component IDs are per-world.
|
||||||
|
#[derive(Clone, Component, Default, Reflect, Deref, DerefMut)]
|
||||||
|
#[reflect(Component, Default)]
|
||||||
|
pub struct VisibilityClass(pub SmallVec<[TypeId; 1]>);
|
||||||
|
|
||||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering.
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering.
|
||||||
///
|
///
|
||||||
/// Each frame, this will be reset to `false` during [`VisibilityPropagate`] systems in [`PostUpdate`].
|
/// Each frame, this will be reset to `false` during [`VisibilityPropagate`] systems in [`PostUpdate`].
|
||||||
@ -219,56 +250,42 @@ pub struct VisibleEntities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VisibleEntities {
|
impl VisibleEntities {
|
||||||
pub fn get<QF>(&self) -> &[Entity]
|
pub fn get(&self, type_id: TypeId) -> &[Entity] {
|
||||||
where
|
match self.entities.get(&type_id) {
|
||||||
QF: 'static,
|
|
||||||
{
|
|
||||||
match self.entities.get(&TypeId::of::<QF>()) {
|
|
||||||
Some(entities) => &entities[..],
|
Some(entities) => &entities[..],
|
||||||
None => &[],
|
None => &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mut<QF>(&mut self) -> &mut Vec<Entity>
|
pub fn get_mut(&mut self, type_id: TypeId) -> &mut Vec<Entity> {
|
||||||
where
|
self.entities.entry(type_id).or_default()
|
||||||
QF: 'static,
|
|
||||||
{
|
|
||||||
self.entities.entry(TypeId::of::<QF>()).or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter<QF>(&self) -> impl DoubleEndedIterator<Item = &Entity>
|
pub fn iter(&self, type_id: TypeId) -> impl DoubleEndedIterator<Item = &Entity> {
|
||||||
where
|
self.get(type_id).iter()
|
||||||
QF: 'static,
|
|
||||||
{
|
|
||||||
self.get::<QF>().iter()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len<QF>(&self) -> usize
|
pub fn len(&self, type_id: TypeId) -> usize {
|
||||||
where
|
self.get(type_id).len()
|
||||||
QF: 'static,
|
|
||||||
{
|
|
||||||
self.get::<QF>().len()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty<QF>(&self) -> bool
|
pub fn is_empty(&self, type_id: TypeId) -> bool {
|
||||||
where
|
self.get(type_id).is_empty()
|
||||||
QF: 'static,
|
|
||||||
{
|
|
||||||
self.get::<QF>().is_empty()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear<QF>(&mut self)
|
pub fn clear(&mut self, type_id: TypeId) {
|
||||||
where
|
self.get_mut(type_id).clear();
|
||||||
QF: 'static,
|
|
||||||
{
|
|
||||||
self.get_mut::<QF>().clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push<QF>(&mut self, entity: Entity)
|
pub fn clear_all(&mut self) {
|
||||||
where
|
// Don't just nuke the hash table; we want to reuse allocations.
|
||||||
QF: 'static,
|
for entities in self.entities.values_mut() {
|
||||||
{
|
entities.clear();
|
||||||
self.get_mut::<QF>().push(entity);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, entity: Entity, type_id: TypeId) {
|
||||||
|
self.get_mut(type_id).push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,10 +349,6 @@ pub enum VisibilitySystems {
|
|||||||
/// the order of systems within this set is irrelevant, as [`check_visibility`]
|
/// the order of systems within this set is irrelevant, as [`check_visibility`]
|
||||||
/// assumes that its operations are irreversible during the frame.
|
/// assumes that its operations are irreversible during the frame.
|
||||||
CheckVisibility,
|
CheckVisibility,
|
||||||
/// Label for the system that runs after visibility checking and marks
|
|
||||||
/// entities that have gone from visible to invisible, or vice versa, as
|
|
||||||
/// changed.
|
|
||||||
VisibilityChangeDetect,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct VisibilityPlugin;
|
pub struct VisibilityPlugin;
|
||||||
@ -344,24 +357,23 @@ impl Plugin for VisibilityPlugin {
|
|||||||
fn build(&self, app: &mut bevy_app::App) {
|
fn build(&self, app: &mut bevy_app::App) {
|
||||||
use VisibilitySystems::*;
|
use VisibilitySystems::*;
|
||||||
|
|
||||||
app.configure_sets(
|
app.register_type::<VisibilityClass>()
|
||||||
PostUpdate,
|
.configure_sets(
|
||||||
(CalculateBounds, UpdateFrusta, VisibilityPropagate)
|
PostUpdate,
|
||||||
.before(CheckVisibility)
|
(CalculateBounds, UpdateFrusta, VisibilityPropagate)
|
||||||
.after(TransformSystem::TransformPropagate),
|
.before(CheckVisibility)
|
||||||
)
|
.after(TransformSystem::TransformPropagate),
|
||||||
.configure_sets(PostUpdate, CheckVisibility.ambiguous_with(CheckVisibility))
|
)
|
||||||
.configure_sets(PostUpdate, VisibilityChangeDetect.after(CheckVisibility))
|
.init_resource::<PreviousVisibleEntities>()
|
||||||
.init_resource::<PreviousVisibleEntities>()
|
.add_systems(
|
||||||
.add_systems(
|
PostUpdate,
|
||||||
PostUpdate,
|
(
|
||||||
(
|
calculate_bounds.in_set(CalculateBounds),
|
||||||
calculate_bounds.in_set(CalculateBounds),
|
(visibility_propagate_system, reset_view_visibility)
|
||||||
(visibility_propagate_system, reset_view_visibility).in_set(VisibilityPropagate),
|
.in_set(VisibilityPropagate),
|
||||||
check_visibility::<With<Mesh3d>>.in_set(CheckVisibility),
|
check_visibility.in_set(CheckVisibility),
|
||||||
mark_view_visibility_as_changed_if_necessary.in_set(VisibilityChangeDetect),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,9 +477,6 @@ fn propagate_recursive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stores all entities that were visible in the previous frame.
|
/// Stores all entities that were visible in the previous frame.
|
||||||
///
|
|
||||||
/// At the end of visibility checking, we compare the visible entities against
|
|
||||||
/// these and set the [`ViewVisibility`] change flags accordingly.
|
|
||||||
#[derive(Resource, Default, Deref, DerefMut)]
|
#[derive(Resource, Default, Deref, DerefMut)]
|
||||||
pub struct PreviousVisibleEntities(EntityHashSet);
|
pub struct PreviousVisibleEntities(EntityHashSet);
|
||||||
|
|
||||||
@ -475,18 +484,16 @@ pub struct PreviousVisibleEntities(EntityHashSet);
|
|||||||
/// Entities that are visible will be marked as such later this frame
|
/// Entities that are visible will be marked as such later this frame
|
||||||
/// by a [`VisibilitySystems::CheckVisibility`] system.
|
/// by a [`VisibilitySystems::CheckVisibility`] system.
|
||||||
fn reset_view_visibility(
|
fn reset_view_visibility(
|
||||||
mut query: Query<(Entity, &mut ViewVisibility)>,
|
mut query: Query<(Entity, &ViewVisibility)>,
|
||||||
mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
|
mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
|
||||||
) {
|
) {
|
||||||
previous_visible_entities.clear();
|
previous_visible_entities.clear();
|
||||||
|
|
||||||
query.iter_mut().for_each(|(entity, mut view_visibility)| {
|
query.iter_mut().for_each(|(entity, view_visibility)| {
|
||||||
// Record the entities that were previously visible.
|
// Record the entities that were previously visible.
|
||||||
if view_visibility.get() {
|
if view_visibility.get() {
|
||||||
previous_visible_entities.insert(entity);
|
previous_visible_entities.insert(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
*view_visibility.bypass_change_detection() = ViewVisibility::HIDDEN;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,12 +503,10 @@ fn reset_view_visibility(
|
|||||||
/// frame, it updates the [`ViewVisibility`] of all entities, and for each view
|
/// frame, it updates the [`ViewVisibility`] of all entities, and for each view
|
||||||
/// also compute the [`VisibleEntities`] for that view.
|
/// also compute the [`VisibleEntities`] for that view.
|
||||||
///
|
///
|
||||||
/// This system needs to be run for each type of renderable entity. If you add a
|
/// To ensure that an entity is checked for visibility, make sure that it has a
|
||||||
/// new type of renderable entity, you'll need to add an instantiation of this
|
/// [`VisibilityClass`] component and that that component is nonempty.
|
||||||
/// system to the [`VisibilitySystems::CheckVisibility`] set so that Bevy will
|
pub fn check_visibility(
|
||||||
/// detect visibility properly for those entities.
|
mut thread_queues: Local<Parallel<TypeIdMap<Vec<Entity>>>>,
|
||||||
pub fn check_visibility<QF>(
|
|
||||||
mut thread_queues: Local<Parallel<Vec<Entity>>>,
|
|
||||||
mut view_query: Query<(
|
mut view_query: Query<(
|
||||||
Entity,
|
Entity,
|
||||||
&mut VisibleEntities,
|
&mut VisibleEntities,
|
||||||
@ -510,23 +515,20 @@ pub fn check_visibility<QF>(
|
|||||||
&Camera,
|
&Camera,
|
||||||
Has<NoCpuCulling>,
|
Has<NoCpuCulling>,
|
||||||
)>,
|
)>,
|
||||||
mut visible_aabb_query: Query<
|
mut visible_aabb_query: Query<(
|
||||||
(
|
Entity,
|
||||||
Entity,
|
&InheritedVisibility,
|
||||||
&InheritedVisibility,
|
&mut ViewVisibility,
|
||||||
&mut ViewVisibility,
|
&VisibilityClass,
|
||||||
Option<&RenderLayers>,
|
Option<&RenderLayers>,
|
||||||
Option<&Aabb>,
|
Option<&Aabb>,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
Has<NoFrustumCulling>,
|
Has<NoFrustumCulling>,
|
||||||
Has<VisibilityRange>,
|
Has<VisibilityRange>,
|
||||||
),
|
)>,
|
||||||
QF,
|
|
||||||
>,
|
|
||||||
visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
|
visible_entity_ranges: Option<Res<VisibleEntityRanges>>,
|
||||||
) where
|
mut previous_visible_entities: ResMut<PreviousVisibleEntities>,
|
||||||
QF: QueryFilter + 'static,
|
) {
|
||||||
{
|
|
||||||
let visible_entity_ranges = visible_entity_ranges.as_deref();
|
let visible_entity_ranges = visible_entity_ranges.as_deref();
|
||||||
|
|
||||||
for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling) in
|
for (view, mut visible_entities, frustum, maybe_view_mask, camera, no_cpu_culling) in
|
||||||
@ -545,6 +547,7 @@ pub fn check_visibility<QF>(
|
|||||||
entity,
|
entity,
|
||||||
inherited_visibility,
|
inherited_visibility,
|
||||||
mut view_visibility,
|
mut view_visibility,
|
||||||
|
visibility_class,
|
||||||
maybe_entity_mask,
|
maybe_entity_mask,
|
||||||
maybe_model_aabb,
|
maybe_model_aabb,
|
||||||
transform,
|
transform,
|
||||||
@ -592,34 +595,70 @@ pub fn check_visibility<QF>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we don't trigger changed notifications
|
// Make sure we don't trigger changed notifications
|
||||||
// unnecessarily.
|
// unnecessarily by checking whether the flag is set before
|
||||||
view_visibility.bypass_change_detection().set();
|
// setting it.
|
||||||
|
if !**view_visibility {
|
||||||
|
view_visibility.set();
|
||||||
|
}
|
||||||
|
|
||||||
queue.push(entity);
|
// Add the entity to the queue for all visibility classes the
|
||||||
|
// entity is in.
|
||||||
|
for visibility_class_id in visibility_class.iter() {
|
||||||
|
queue.entry(*visibility_class_id).or_default().push(entity);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
visible_entities.clear::<QF>();
|
visible_entities.clear_all();
|
||||||
thread_queues.drain_into(visible_entities.get_mut::<QF>());
|
|
||||||
|
// Drain all the thread queues into the `visible_entities` list.
|
||||||
|
for class_queues in thread_queues.iter_mut() {
|
||||||
|
for (class, entities) in class_queues {
|
||||||
|
let visible_entities_for_class = visible_entities.get_mut(*class);
|
||||||
|
for entity in entities.drain(..) {
|
||||||
|
// As we mark entities as visible, we remove them from the
|
||||||
|
// `previous_visible_entities` list. At the end, all of the
|
||||||
|
// entities remaining in `previous_visible_entities` will be
|
||||||
|
// entities that were visible last frame but are no longer
|
||||||
|
// visible this frame.
|
||||||
|
previous_visible_entities.remove(&entity);
|
||||||
|
|
||||||
|
visible_entities_for_class.push(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now whatever previous visible entities are left are entities that were
|
||||||
|
// visible last frame but just became invisible.
|
||||||
|
for entity in previous_visible_entities.drain() {
|
||||||
|
if let Ok((_, _, mut view_visibility, _, _, _, _, _, _)) =
|
||||||
|
visible_aabb_query.get_mut(entity)
|
||||||
|
{
|
||||||
|
*view_visibility = ViewVisibility::HIDDEN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A system that marks [`ViewVisibility`] components as changed if their
|
/// A generic component add hook that automatically adds the appropriate
|
||||||
/// visibility changed this frame.
|
/// [`VisibilityClass`] to an entity.
|
||||||
///
|
///
|
||||||
/// Ordinary change detection doesn't work for this use case because we use
|
/// This can be handy when creating custom renderable components. To use this
|
||||||
/// multiple [`check_visibility`] systems, and visibility is set if *any one* of
|
/// hook, add it to your renderable component like this:
|
||||||
/// those systems judges the entity to be visible. Thus, in order to perform
|
///
|
||||||
/// change detection, we need this system, which is a follow-up pass that has a
|
/// ```ignore
|
||||||
/// global view of whether the entity became visible or not.
|
/// #[derive(Component)]
|
||||||
fn mark_view_visibility_as_changed_if_necessary(
|
/// #[component(on_add = add_visibility_class::<MyComponent>)]
|
||||||
mut query: Query<(Entity, &mut ViewVisibility)>,
|
/// struct MyComponent {
|
||||||
previous_visible_entities: Res<PreviousVisibleEntities>,
|
/// ...
|
||||||
) {
|
/// }
|
||||||
for (entity, mut view_visibility) in query.iter_mut() {
|
/// ```
|
||||||
if previous_visible_entities.contains(&entity) != view_visibility.get() {
|
pub fn add_visibility_class<C>(mut world: DeferredWorld<'_>, entity: Entity, _: ComponentId)
|
||||||
view_visibility.set_changed();
|
where
|
||||||
}
|
C: 'static,
|
||||||
|
{
|
||||||
|
if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) {
|
||||||
|
visibility_class.push(TypeId::of::<C>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,7 +27,6 @@ use super::{check_visibility, VisibilitySystems};
|
|||||||
use crate::sync_world::{MainEntity, MainEntityHashMap};
|
use crate::sync_world::{MainEntity, MainEntityHashMap};
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::Camera,
|
camera::Camera,
|
||||||
mesh::Mesh3d,
|
|
||||||
primitives::Aabb,
|
primitives::Aabb,
|
||||||
render_resource::BufferVec,
|
render_resource::BufferVec,
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
@ -59,7 +58,7 @@ impl Plugin for VisibilityRangePlugin {
|
|||||||
PostUpdate,
|
PostUpdate,
|
||||||
check_visibility_ranges
|
check_visibility_ranges
|
||||||
.in_set(VisibilitySystems::CheckVisibility)
|
.in_set(VisibilitySystems::CheckVisibility)
|
||||||
.before(check_visibility::<With<Mesh3d>>),
|
.before(check_visibility),
|
||||||
);
|
);
|
||||||
|
|
||||||
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
|||||||
@ -59,7 +59,7 @@ use bevy_render::{
|
|||||||
primitives::Aabb,
|
primitives::Aabb,
|
||||||
render_phase::AddRenderCommand,
|
render_phase::AddRenderCommand,
|
||||||
render_resource::{Shader, SpecializedRenderPipelines},
|
render_resource::{Shader, SpecializedRenderPipelines},
|
||||||
view::{check_visibility, NoFrustumCulling, VisibilitySystems},
|
view::{self, NoFrustumCulling, VisibilityClass, VisibilitySystems},
|
||||||
ExtractSchedule, Render, RenderApp, RenderSet,
|
ExtractSchedule, Render, RenderApp, RenderSet,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -96,12 +96,10 @@ pub enum SpriteSystem {
|
|||||||
/// Right now, this is used for `Text`.
|
/// Right now, this is used for `Text`.
|
||||||
#[derive(Component, Reflect, Clone, Copy, Debug, Default)]
|
#[derive(Component, Reflect, Clone, Copy, Debug, Default)]
|
||||||
#[reflect(Component, Default, Debug)]
|
#[reflect(Component, Default, Debug)]
|
||||||
|
#[require(VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<Sprite>)]
|
||||||
pub struct SpriteSource;
|
pub struct SpriteSource;
|
||||||
|
|
||||||
/// A convenient alias for `Or<With<Sprite>, With<SpriteSource>>`, for use with
|
|
||||||
/// [`bevy_render::view::VisibleEntities`].
|
|
||||||
pub type WithSprite = Or<(With<Sprite>, With<SpriteSource>)>;
|
|
||||||
|
|
||||||
impl Plugin for SpritePlugin {
|
impl Plugin for SpritePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
load_internal_asset!(
|
load_internal_asset!(
|
||||||
@ -139,11 +137,6 @@ impl Plugin for SpritePlugin {
|
|||||||
compute_slices_on_sprite_change,
|
compute_slices_on_sprite_change,
|
||||||
)
|
)
|
||||||
.in_set(SpriteSystem::ComputeSlices),
|
.in_set(SpriteSystem::ComputeSlices),
|
||||||
(
|
|
||||||
check_visibility::<With<Mesh2d>>,
|
|
||||||
check_visibility::<WithSprite>,
|
|
||||||
)
|
|
||||||
.in_set(VisibilitySystems::CheckVisibility),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -525,7 +525,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
|
|||||||
view_key |= Mesh2dPipelineKey::DEBAND_DITHER;
|
view_key |= Mesh2dPipelineKey::DEBAND_DITHER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (render_entity, visible_entity) in visible_entities.iter::<With<Mesh2d>>() {
|
for (render_entity, visible_entity) in visible_entities.iter::<Mesh2d>() {
|
||||||
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
let Some(material_asset_id) = render_material_instances.get(visible_entity) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
use core::ops::Range;
|
use core::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
texture_atlas::TextureAtlasLayout, ComputedTextureSlices, Sprite, WithSprite,
|
texture_atlas::TextureAtlasLayout, ComputedTextureSlices, Sprite, SPRITE_SHADER_HANDLE,
|
||||||
SPRITE_SHADER_HANDLE,
|
|
||||||
};
|
};
|
||||||
use bevy_asset::{AssetEvent, AssetId, Assets};
|
use bevy_asset::{AssetEvent, AssetId, Assets};
|
||||||
use bevy_color::{ColorToComponents, LinearRgba};
|
use bevy_color::{ColorToComponents, LinearRgba};
|
||||||
@ -551,7 +550,7 @@ pub fn queue_sprites(
|
|||||||
view_entities.clear();
|
view_entities.clear();
|
||||||
view_entities.extend(
|
view_entities.extend(
|
||||||
visible_entities
|
visible_entities
|
||||||
.iter::<WithSprite>()
|
.iter::<Sprite>()
|
||||||
.map(|(_, e)| e.index() as usize),
|
.map(|(_, e)| e.index() as usize),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -7,15 +7,19 @@ use bevy_ecs::{
|
|||||||
use bevy_image::Image;
|
use bevy_image::Image;
|
||||||
use bevy_math::{Rect, UVec2, Vec2};
|
use bevy_math::{Rect, UVec2, Vec2};
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||||
use bevy_render::{sync_world::SyncToRenderWorld, view::Visibility};
|
use bevy_render::{
|
||||||
|
sync_world::SyncToRenderWorld,
|
||||||
|
view::{self, Visibility, VisibilityClass},
|
||||||
|
};
|
||||||
use bevy_transform::components::Transform;
|
use bevy_transform::components::Transform;
|
||||||
|
|
||||||
use crate::{TextureAtlas, TextureAtlasLayout, TextureSlicer};
|
use crate::{TextureAtlas, TextureAtlasLayout, TextureSlicer};
|
||||||
|
|
||||||
/// Describes a sprite to be rendered to a 2D camera
|
/// Describes a sprite to be rendered to a 2D camera
|
||||||
#[derive(Component, Debug, Default, Clone, Reflect)]
|
#[derive(Component, Debug, Default, Clone, Reflect)]
|
||||||
#[require(Transform, Visibility, SyncToRenderWorld)]
|
#[require(Transform, Visibility, SyncToRenderWorld, VisibilityClass)]
|
||||||
#[reflect(Component, Default, Debug)]
|
#[reflect(Component, Default, Debug)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<Sprite>)]
|
||||||
pub struct Sprite {
|
pub struct Sprite {
|
||||||
/// The image used to render the sprite
|
/// The image used to render the sprite
|
||||||
pub image: Handle<Image>,
|
pub image: Handle<Image>,
|
||||||
|
|||||||
@ -72,11 +72,7 @@ pub mod prelude {
|
|||||||
use bevy_app::{prelude::*, Animation};
|
use bevy_app::{prelude::*, Animation};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_input::InputSystem;
|
use bevy_input::InputSystem;
|
||||||
use bevy_render::{
|
use bevy_render::{camera::CameraUpdateSystem, RenderApp};
|
||||||
camera::CameraUpdateSystem,
|
|
||||||
view::{check_visibility, VisibilitySystems},
|
|
||||||
RenderApp,
|
|
||||||
};
|
|
||||||
use bevy_transform::TransformSystem;
|
use bevy_transform::TransformSystem;
|
||||||
use layout::ui_surface::UiSurface;
|
use layout::ui_surface::UiSurface;
|
||||||
use stack::ui_stack_system;
|
use stack::ui_stack_system;
|
||||||
@ -203,7 +199,6 @@ impl Plugin for UiPlugin {
|
|||||||
app.add_systems(
|
app.add_systems(
|
||||||
PostUpdate,
|
PostUpdate,
|
||||||
(
|
(
|
||||||
check_visibility::<With<Node>>.in_set(VisibilitySystems::CheckVisibility),
|
|
||||||
update_target_camera_system.in_set(UiSystem::Prepare),
|
update_target_camera_system.in_set(UiSystem::Prepare),
|
||||||
ui_layout_system_config,
|
ui_layout_system_config,
|
||||||
ui_stack_system
|
ui_stack_system
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use bevy_math::{vec4, Rect, Vec2, Vec4Swizzles};
|
|||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
camera::{Camera, RenderTarget},
|
camera::{Camera, RenderTarget},
|
||||||
view::Visibility,
|
view::{self, Visibility, VisibilityClass},
|
||||||
};
|
};
|
||||||
use bevy_sprite::BorderRect;
|
use bevy_sprite::BorderRect;
|
||||||
use bevy_transform::components::Transform;
|
use bevy_transform::components::Transform;
|
||||||
@ -328,9 +328,11 @@ impl From<Vec2> for ScrollPosition {
|
|||||||
ScrollPosition,
|
ScrollPosition,
|
||||||
Transform,
|
Transform,
|
||||||
Visibility,
|
Visibility,
|
||||||
|
VisibilityClass,
|
||||||
ZIndex
|
ZIndex
|
||||||
)]
|
)]
|
||||||
#[reflect(Component, Default, PartialEq, Debug)]
|
#[reflect(Component, Default, PartialEq, Debug)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<Node>)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serialize",
|
feature = "serialize",
|
||||||
derive(serde::Serialize, serde::Deserialize),
|
derive(serde::Serialize, serde::Deserialize),
|
||||||
|
|||||||
@ -386,7 +386,7 @@ pub fn queue_colored_mesh2d(
|
|||||||
| Mesh2dPipelineKey::from_hdr(view.hdr);
|
| Mesh2dPipelineKey::from_hdr(view.hdr);
|
||||||
|
|
||||||
// Queue all entities visible to that view
|
// Queue all entities visible to that view
|
||||||
for (render_entity, visible_entity) in visible_entities.iter::<With<Mesh2d>>() {
|
for (render_entity, visible_entity) in visible_entities.iter::<Mesh2d>() {
|
||||||
if let Some(mesh_instance) = render_mesh_instances.get(visible_entity) {
|
if let Some(mesh_instance) = render_mesh_instances.get(visible_entity) {
|
||||||
let mesh2d_handle = mesh_instance.mesh_asset_id;
|
let mesh2d_handle = mesh_instance.mesh_asset_id;
|
||||||
let mesh2d_transforms = &mesh_instance.transforms;
|
let mesh2d_transforms = &mesh_instance.transforms;
|
||||||
|
|||||||
@ -29,7 +29,7 @@ use bevy::{
|
|||||||
VertexFormat, VertexState, VertexStepMode,
|
VertexFormat, VertexState, VertexStepMode,
|
||||||
},
|
},
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
view::{self, ExtractedView, RenderVisibleEntities, VisibilitySystems},
|
view::{self, ExtractedView, RenderVisibleEntities, VisibilityClass},
|
||||||
Render, RenderApp, RenderSet,
|
Render, RenderApp, RenderSet,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -38,9 +38,13 @@ use bytemuck::{Pod, Zeroable};
|
|||||||
/// A marker component that represents an entity that is to be rendered using
|
/// A marker component that represents an entity that is to be rendered using
|
||||||
/// our custom phase item.
|
/// our custom phase item.
|
||||||
///
|
///
|
||||||
/// Note the [`ExtractComponent`] trait implementation. This is necessary to
|
/// Note the [`ExtractComponent`] trait implementation: this is necessary to
|
||||||
/// tell Bevy that this object should be pulled into the render world.
|
/// tell Bevy that this object should be pulled into the render world. Also note
|
||||||
|
/// the `on_add` hook, which is needed to tell Bevy's `check_visibility` system
|
||||||
|
/// that entities with this component need to be examined for visibility.
|
||||||
#[derive(Clone, Component, ExtractComponent)]
|
#[derive(Clone, Component, ExtractComponent)]
|
||||||
|
#[require(VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<CustomRenderedEntity>)]
|
||||||
struct CustomRenderedEntity;
|
struct CustomRenderedEntity;
|
||||||
|
|
||||||
/// Holds a reference to our shader.
|
/// Holds a reference to our shader.
|
||||||
@ -152,10 +156,6 @@ impl Vertex {
|
|||||||
/// the render phase.
|
/// the render phase.
|
||||||
type DrawCustomPhaseItemCommands = (SetItemPipeline, DrawCustomPhaseItem);
|
type DrawCustomPhaseItemCommands = (SetItemPipeline, DrawCustomPhaseItem);
|
||||||
|
|
||||||
/// A query filter that tells [`view::check_visibility`] about our custom
|
|
||||||
/// rendered entity.
|
|
||||||
type WithCustomRenderedEntity = With<CustomRenderedEntity>;
|
|
||||||
|
|
||||||
/// A single triangle's worth of vertices, for demonstration purposes.
|
/// A single triangle's worth of vertices, for demonstration purposes.
|
||||||
static VERTICES: [Vertex; 3] = [
|
static VERTICES: [Vertex; 3] = [
|
||||||
Vertex::new(vec3(-0.866, -0.5, 0.5), vec3(1.0, 0.0, 0.0)),
|
Vertex::new(vec3(-0.866, -0.5, 0.5), vec3(1.0, 0.0, 0.0)),
|
||||||
@ -168,14 +168,7 @@ fn main() {
|
|||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
app.add_plugins(DefaultPlugins)
|
app.add_plugins(DefaultPlugins)
|
||||||
.add_plugins(ExtractComponentPlugin::<CustomRenderedEntity>::default())
|
.add_plugins(ExtractComponentPlugin::<CustomRenderedEntity>::default())
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup);
|
||||||
// Make sure to tell Bevy to check our entity for visibility. Bevy won't
|
|
||||||
// do this by default, for efficiency reasons.
|
|
||||||
.add_systems(
|
|
||||||
PostUpdate,
|
|
||||||
view::check_visibility::<WithCustomRenderedEntity>
|
|
||||||
.in_set(VisibilitySystems::CheckVisibility),
|
|
||||||
);
|
|
||||||
|
|
||||||
// We make sure to add these to the render app, not the main app.
|
// We make sure to add these to the render app, not the main app.
|
||||||
app.get_sub_app_mut(RenderApp)
|
app.get_sub_app_mut(RenderApp)
|
||||||
@ -246,10 +239,7 @@ fn queue_custom_phase_item(
|
|||||||
|
|
||||||
// Find all the custom rendered entities that are visible from this
|
// Find all the custom rendered entities that are visible from this
|
||||||
// view.
|
// view.
|
||||||
for &entity in view_visible_entities
|
for &entity in view_visible_entities.get::<CustomRenderedEntity>().iter() {
|
||||||
.get::<WithCustomRenderedEntity>()
|
|
||||||
.iter()
|
|
||||||
{
|
|
||||||
// Ordinarily, the [`SpecializedRenderPipeline::Key`] would contain
|
// Ordinarily, the [`SpecializedRenderPipeline::Key`] would contain
|
||||||
// some per-view settings, such as whether the view is HDR, but for
|
// some per-view settings, such as whether the view is HDR, but for
|
||||||
// simplicity's sake we simply hard-code the view's characteristics,
|
// simplicity's sake we simply hard-code the view's characteristics,
|
||||||
|
|||||||
@ -28,7 +28,7 @@ use bevy::{
|
|||||||
RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
RenderPipelineDescriptor, SpecializedMeshPipeline, SpecializedMeshPipelineError,
|
||||||
SpecializedMeshPipelines, TextureFormat, VertexState,
|
SpecializedMeshPipelines, TextureFormat, VertexState,
|
||||||
},
|
},
|
||||||
view::{self, ExtractedView, RenderVisibleEntities, ViewTarget, VisibilitySystems},
|
view::{self, ExtractedView, RenderVisibleEntities, ViewTarget, VisibilityClass},
|
||||||
Render, RenderApp, RenderSet,
|
Render, RenderApp, RenderSet,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -97,15 +97,7 @@ fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
|
|||||||
struct CustomRenderedMeshPipelinePlugin;
|
struct CustomRenderedMeshPipelinePlugin;
|
||||||
impl Plugin for CustomRenderedMeshPipelinePlugin {
|
impl Plugin for CustomRenderedMeshPipelinePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_plugins(ExtractComponentPlugin::<CustomRenderedEntity>::default())
|
app.add_plugins(ExtractComponentPlugin::<CustomRenderedEntity>::default());
|
||||||
.add_systems(
|
|
||||||
PostUpdate,
|
|
||||||
// Make sure to tell Bevy to check our entity for visibility. Bevy won't
|
|
||||||
// do this by default, for efficiency reasons.
|
|
||||||
// This will do things like frustum culling and hierarchy visibility
|
|
||||||
view::check_visibility::<WithCustomRenderedEntity>
|
|
||||||
.in_set(VisibilitySystems::CheckVisibility),
|
|
||||||
);
|
|
||||||
|
|
||||||
// We make sure to add these to the render app, not the main app.
|
// We make sure to add these to the render app, not the main app.
|
||||||
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
@ -132,9 +124,13 @@ impl Plugin for CustomRenderedMeshPipelinePlugin {
|
|||||||
/// A marker component that represents an entity that is to be rendered using
|
/// A marker component that represents an entity that is to be rendered using
|
||||||
/// our specialized pipeline.
|
/// our specialized pipeline.
|
||||||
///
|
///
|
||||||
/// Note the [`ExtractComponent`] trait implementation. This is necessary to
|
/// Note the [`ExtractComponent`] trait implementation: this is necessary to
|
||||||
/// tell Bevy that this object should be pulled into the render world.
|
/// tell Bevy that this object should be pulled into the render world. Also note
|
||||||
|
/// the `on_add` hook, which is needed to tell Bevy's `check_visibility` system
|
||||||
|
/// that entities with this component need to be examined for visibility.
|
||||||
#[derive(Clone, Component, ExtractComponent)]
|
#[derive(Clone, Component, ExtractComponent)]
|
||||||
|
#[require(VisibilityClass)]
|
||||||
|
#[component(on_add = view::add_visibility_class::<CustomRenderedEntity>)]
|
||||||
struct CustomRenderedEntity;
|
struct CustomRenderedEntity;
|
||||||
|
|
||||||
/// The custom draw commands that Bevy executes for each entity we enqueue into
|
/// The custom draw commands that Bevy executes for each entity we enqueue into
|
||||||
@ -150,10 +146,6 @@ type DrawSpecializedPipelineCommands = (
|
|||||||
DrawMesh,
|
DrawMesh,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// A query filter that tells [`view::check_visibility`] about our custom
|
|
||||||
/// rendered entity.
|
|
||||||
type WithCustomRenderedEntity = With<CustomRenderedEntity>;
|
|
||||||
|
|
||||||
// This contains the state needed to specialize a mesh pipeline
|
// This contains the state needed to specialize a mesh pipeline
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
struct CustomMeshPipeline {
|
struct CustomMeshPipeline {
|
||||||
@ -299,9 +291,8 @@ fn queue_custom_mesh_pipeline(
|
|||||||
|
|
||||||
// Find all the custom rendered entities that are visible from this
|
// Find all the custom rendered entities that are visible from this
|
||||||
// view.
|
// view.
|
||||||
for &(render_entity, visible_entity) in view_visible_entities
|
for &(render_entity, visible_entity) in
|
||||||
.get::<WithCustomRenderedEntity>()
|
view_visible_entities.get::<CustomRenderedEntity>().iter()
|
||||||
.iter()
|
|
||||||
{
|
{
|
||||||
// Get the mesh instance
|
// Get the mesh instance
|
||||||
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(visible_entity)
|
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(visible_entity)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user