Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 88 additions & 2 deletions crates/bevy_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ pub mod prelude {
//! The Bevy Core Prelude.
#[doc(hidden)]
pub use crate::{
DebugName, FrameCountPlugin, Name, TaskPoolOptions, TaskPoolPlugin, TypeRegistrationPlugin,
Cached, DebugName, FrameCountPlugin, Name, Static, TaskPoolOptions, TaskPoolPlugin,
TypeRegistrationPlugin,
};
}

Expand All @@ -26,7 +27,7 @@ use bevy_utils::{Duration, HashSet, Instant, Uuid};
use std::borrow::Cow;
use std::ffi::OsString;
use std::marker::PhantomData;
use std::ops::Range;
use std::ops::{Deref, Range};
use std::path::{Path, PathBuf};

#[cfg(not(target_arch = "wasm32"))]
Expand Down Expand Up @@ -139,6 +140,91 @@ fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
tick_global_task_pools_on_main_thread();
}

/// Static is a special marker component. Bevy assumes that entities with a Static component do not change
/// during play.
///
/// * They do not move. When [`Transform`] changes, [`GlobalTransform`] is not propagated.
/// * They do not change apperence. Data is cached for render and not updated.
///
/// The [`Cached`] component is added to static entities after they are rendered for the first time (when using
/// the [`StaticPlugin`]).
///
/// # Examples
///
/// These sorts of entities should be marked with a Static component.
///
/// * Terrain.
/// * Background titles (in a 2d game).
/// * Large props that never move, like buildings.
/// * Everything with properties that don't change during play.
///
/// These sorts of entities should not be marked with a Static component.
///
/// * Dynamic physics bodies.
/// * Animated characters.
/// * Things that change color, texture, or material.
/// * Anything with properties you expect to change during play.
///
#[derive(Component, Default, Debug, Clone, Copy)]
#[component(storage = "Table")]
pub struct Static;

/// Cached is a special marker component closely related to [`Static`].
///
/// Systems in the [`PostUpdate`] schedule that query for cached entities should run after [`refresh_cached`]
/// to avoid a possible one frame lag when [`Static`] is removed.
Comment on lines +172 to +175
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be a warning against manually inserting Cached into an entity? From your description, it seems that only render extraction should be adding it.

#[derive(Component, Default, Debug, Clone, Copy)]
#[component(storage = "Table")]
pub struct Cached;

/// The vector of entities that have had [`Cached`] removed since the last frame was drawn.
#[derive(Debug, Default, Resource, Clone)]
pub struct Invalidated {
entities: Vec<Entity>,
}

impl Deref for Invalidated {
type Target = [Entity];

fn deref(&self) -> &Self::Target {
&self.entities
}
}

/// Removes [`Cached`] from entities that are no longer [`Static`]. This is run in the [`PostUpdate`] schedule, so it
/// executes between the `Update` schedule and start of render-world extraction.
pub fn remove_cached_dynamics(
cached: Query<Entity, (With<Cached>, Without<Static>)>,
mut commands: Commands,
) {
// FIXME: This should eventually become an unsafe exclusive system operating directly on architypes.
for entity in cached.iter() {
commands.entity(entity).remove::<Cached>();
}
}

/// Writes a vec of entities to be droped from caches in the render world. This is necessary because
/// extraction systems can't mutate the main world, and so cannot read component removal events.
pub fn write_cache_invalidation(
mut removed: RemovedComponents<Cached>,
mut invalidated: ResMut<Invalidated>,
) {
invalidated.entities.clear();
invalidated.entities.extend(removed.read());
}

/// Adds static entities and caching to Apps. See [`Static`] and [`Cached`] for details.
#[derive(Default)]
pub struct StaticPlugin;

impl Plugin for StaticPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Invalidated>()
.add_systems(PostUpdate, remove_cached_dynamics)
.add_systems(Last, write_cache_invalidation);
}
}

/// Maintains a count of frames rendered since the start of the application.
///
/// [`FrameCount`] is incremented during [`Last`], providing predictable
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_internal/src/default_plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl PluginGroup for DefaultPlugins {
.add(bevy_core::TaskPoolPlugin::default())
.add(bevy_core::TypeRegistrationPlugin)
.add(bevy_core::FrameCountPlugin)
.add(bevy_core::StaticPlugin)
.add(bevy_time::TimePlugin)
.add(bevy_transform::TransformPlugin)
.add(bevy_hierarchy::HierarchyPlugin)
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pbr_transmission_textures = []
# bevy
bevy_app = { path = "../bevy_app", version = "0.12.0" }
bevy_asset = { path = "../bevy_asset", version = "0.12.0" }
bevy_core = { path = "../bevy_core", version = "0.12.0" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.12.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0" }
bevy_math = { path = "../bevy_math", version = "0.12.0" }
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::*;
use bevy_app::{App, Plugin};
use bevy_asset::{Asset, AssetApp, AssetEvent, AssetId, AssetServer, Assets, Handle};
use bevy_core::Cached;
use bevy_core_pipeline::{
core_3d::{
AlphaMask3d, Camera3d, Opaque3d, ScreenSpaceTransmissionQuality, Transmissive3d,
Expand Down
30 changes: 17 additions & 13 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ use crate::{
};
use bevy_app::{Plugin, PostUpdate};
use bevy_asset::{load_internal_asset, AssetId, Handle};
use bevy_core::{Cached, Invalidated};
use bevy_core_pipeline::{
core_3d::{AlphaMask3d, Opaque3d, Transmissive3d, Transparent3d, CORE_3D_DEPTH_FORMAT},
deferred::{AlphaMask3dDeferred, Opaque3dDeferred},
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::{
entity::EntityHashMap,
prelude::*,
query::ROQueryItem,
system::{lifetimeless::*, SystemParamItem, SystemState},
Expand Down Expand Up @@ -267,17 +268,20 @@ pub fn extract_meshes(
mut render_mesh_instances: ResMut<RenderMeshInstances>,
mut thread_local_queues: Local<ThreadLocal<Cell<Vec<(Entity, RenderMeshInstance)>>>>,
meshes_query: Extract<
Query<(
Entity,
&ViewVisibility,
&GlobalTransform,
Option<&PreviousGlobalTransform>,
&Handle<Mesh>,
Has<NotShadowReceiver>,
Has<TransmittedShadowReceiver>,
Has<NotShadowCaster>,
Has<NoAutomaticBatching>,
)>,
Query<
(
Entity,
&ViewVisibility,
&GlobalTransform,
Option<&PreviousGlobalTransform>,
&Handle<Mesh>,
Has<NotShadowReceiver>,
Has<TransmittedShadowReceiver>,
Has<NotShadowCaster>,
Has<NoAutomaticBatching>,
),
Without<Cached>,
>,
>,
) {
meshes_query.par_iter().for_each(
Expand Down Expand Up @@ -313,6 +317,7 @@ pub fn extract_meshes(
previous_transform: (&previous_transform).into(),
flags: flags.bits(),
};

let tls = thread_local_queues.get_or_default();
let mut queue = tls.take();
queue.push((
Expand All @@ -329,7 +334,6 @@ pub fn extract_meshes(
},
);

render_mesh_instances.clear();
for queue in thread_local_queues.iter_mut() {
render_mesh_instances.extend(queue.get_mut().drain(..));
}
Expand Down
17 changes: 15 additions & 2 deletions crates/bevy_render/src/view/visibility/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
mod render_layers;

use bevy_core::{Cached, Static};
use bevy_derive::Deref;
pub use render_layers::*;

use bevy_app::{Plugin, PostUpdate};
use bevy_app::{First, Plugin, PostUpdate};
use bevy_asset::{Assets, Handle};
use bevy_ecs::prelude::*;
use bevy_hierarchy::{Children, Parent};
Expand Down Expand Up @@ -212,7 +213,7 @@ impl Plugin for VisibilityPlugin {
fn build(&self, app: &mut bevy_app::App) {
use VisibilitySystems::*;

app.add_systems(
app.add_systems(First, cache_viewed).add_systems(
PostUpdate,
(
calculate_bounds.in_set(CalculateBounds),
Expand Down Expand Up @@ -455,6 +456,18 @@ pub fn check_visibility(
}
}

/// Sets [`Static`] entities that were visible in the previous frame to [`Cached`].
fn cache_viewed(
visibilities: Query<(Entity, &ViewVisibility), (With<Static>, Without<Cached>)>,
mut commands: Commands,
) {
for (entity, vis) in visibilities.iter() {
if vis.get() {
commands.entity(entity).insert(Cached);
}
}
}

#[cfg(test)]
mod test {
use bevy_app::prelude::*;
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_transform/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ keywords = ["bevy"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.12.0" }
bevy_core = { path = "../bevy_core", version = "0.12.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0", features = [
"bevy_reflect",
] }
Expand Down
5 changes: 4 additions & 1 deletion crates/bevy_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod prelude {
}

use bevy_app::prelude::*;
use bevy_core::remove_cached_dynamics;
use bevy_ecs::prelude::*;
use bevy_hierarchy::ValidParentCheckPlugin;
use bevy_math::{Affine3A, Mat4, Vec3};
Expand Down Expand Up @@ -119,7 +120,9 @@ impl Plugin for TransformPlugin {
)
.configure_sets(
PostUpdate,
PropagateTransformsSet.in_set(TransformSystem::TransformPropagate),
PropagateTransformsSet
.in_set(TransformSystem::TransformPropagate)
.after(remove_cached_dynamics),
)
.add_systems(
PostUpdate,
Expand Down
25 changes: 19 additions & 6 deletions crates/bevy_transform/src/systems.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::components::{GlobalTransform, Transform};
use bevy_core::Cached;
use bevy_ecs::{
change_detection::Ref,
prelude::{Changed, DetectChanges, Entity, Query, With, Without},
Expand All @@ -8,7 +9,9 @@ use bevy_ecs::{
};
use bevy_hierarchy::{Children, Parent};

/// Update [`GlobalTransform`] component of entities that aren't in the hierarchy
/// Update [`GlobalTransform`] component of entities that aren't in the hierarchy.
///
/// Ignores entities marked as [`Cached`].
///
/// Third party plugins should ensure that this is used in concert with [`propagate_transforms`].
pub fn sync_simple_transforms(
Expand All @@ -19,9 +22,13 @@ pub fn sync_simple_transforms(
Or<(Changed<Transform>, Added<GlobalTransform>)>,
Without<Parent>,
Without<Children>,
Without<Cached>,
),
>,
Query<(Ref<Transform>, &mut GlobalTransform), (Without<Parent>, Without<Children>)>,
Query<
(Ref<Transform>, &mut GlobalTransform),
(Without<Parent>, Without<Children>, Without<Cached>),
>,
)>,
mut orphaned: RemovedComponents<Parent>,
) {
Expand All @@ -45,14 +52,20 @@ pub fn sync_simple_transforms(
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
/// [`Transform`] component.
///
/// This function will not propagate transforms to entities marked as [`Cached`], but it will visit them
/// in parallel to propagate the transforms of their children.
///
/// Third party plugins should ensure that this is used in concert with [`sync_simple_transforms`].
pub fn propagate_transforms(
mut root_query: Query<
(Entity, &Children, Ref<Transform>, &mut GlobalTransform),
Without<Parent>,
Or<(Without<Parent>, With<Cached>)>,
>,
mut orphaned: RemovedComponents<Parent>,
transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<&Children>), With<Parent>>,
transform_query: Query<
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
(With<Parent>, Without<Cached>),
>,
parent_query: Query<(Entity, Ref<Parent>)>,
mut orphaned_entities: Local<Vec<Entity>>,
) {
Expand Down Expand Up @@ -110,7 +123,7 @@ unsafe fn propagate_recursive(
parent: &GlobalTransform,
transform_query: &Query<
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
With<Parent>,
(With<Parent>, Without<Cached>),
>,
parent_query: &Query<(Entity, Ref<Parent>)>,
entity: Entity,
Expand Down Expand Up @@ -145,7 +158,7 @@ unsafe fn propagate_recursive(
// Even if these A and B start two separate tasks running in parallel, one of them will panic before attempting
// to mutably access E.
(unsafe { transform_query.get_unchecked(entity) }) else {
return;
return; // This happens when `entity` is marked as `Cached`.
};

changed |= transform.is_changed() || global_transform.is_added();
Expand Down
Loading