diff --git a/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs b/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs index 4991a0925bee9..459a072406c3b 100644 --- a/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs +++ b/crates/bevy_core_pipeline/src/auto_exposure/buffers.rs @@ -2,7 +2,7 @@ use bevy_ecs::prelude::*; use bevy_render::{ render_resource::{StorageBuffer, UniformBuffer}, renderer::{RenderDevice, RenderQueue}, - world_sync::RenderEntity, + sync_world::RenderEntity, Extract, }; use bevy_utils::{Entry, HashMap}; diff --git a/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs b/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs index 5e3ef674d3bdf..9f8073e3f51df 100644 --- a/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs +++ b/crates/bevy_core_pipeline/src/core_2d/camera_2d.rs @@ -6,7 +6,7 @@ use crate::{ }; use bevy_ecs::prelude::*; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render::world_sync::SyncToRenderWorld; +use bevy_render::sync_world::SyncToRenderWorld; use bevy_render::{ camera::{ Camera, CameraMainTextureUsages, CameraProjection, CameraRenderGraph, diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index 9f07f19bf8a81..c621ebc7fd215 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -55,9 +55,9 @@ use bevy_render::{ TextureFormat, TextureUsages, }, renderer::RenderDevice, + sync_world::RenderEntity, texture::TextureCache, view::{Msaa, ViewDepthTexture}, - world_sync::RenderEntity, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; diff --git a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs index 81955f76b361e..3b60d8fde6b06 100644 --- a/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs +++ b/crates/bevy_core_pipeline/src/core_3d/camera_3d.rs @@ -11,8 +11,8 @@ use bevy_render::{ extract_component::ExtractComponent, primitives::Frustum, render_resource::{LoadOp, TextureUsages}, + sync_world::SyncToRenderWorld, view::{ColorGrading, Msaa, VisibleEntities}, - world_sync::SyncToRenderWorld, }; use bevy_transform::prelude::{GlobalTransform, Transform}; use serde::{Deserialize, Serialize}; diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 0c94bc6767e38..5389e5c9bdf13 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -89,9 +89,9 @@ use bevy_render::{ Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, }, renderer::RenderDevice, + sync_world::RenderEntity, texture::{BevyDefault, ColorAttachment, Image, TextureCache}, view::{ExtractedView, ViewDepthTexture, ViewTarget}, - world_sync::RenderEntity, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::{tracing::warn, HashMap}; diff --git a/crates/bevy_core_pipeline/src/dof/mod.rs b/crates/bevy_core_pipeline/src/dof/mod.rs index 76087b63a69bd..c09373ee3ab10 100644 --- a/crates/bevy_core_pipeline/src/dof/mod.rs +++ b/crates/bevy_core_pipeline/src/dof/mod.rs @@ -46,12 +46,12 @@ use bevy_render::{ TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages, }, renderer::{RenderContext, RenderDevice}, + sync_world::RenderEntity, texture::{BevyDefault, CachedTexture, TextureCache}, view::{ prepare_view_targets, ExtractedView, Msaa, ViewDepthTexture, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, }, - world_sync::RenderEntity, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::{info_once, prelude::default, warn_once}; diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index d5b66a51b8d3c..af7aaea58889f 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -32,9 +32,9 @@ use bevy_render::{ TextureDimension, TextureFormat, TextureSampleType, TextureUsages, }, renderer::{RenderContext, RenderDevice}, + sync_world::RenderEntity, texture::{BevyDefault, CachedTexture, TextureCache}, view::{ExtractedView, Msaa, ViewTarget}, - world_sync::RenderEntity, ExtractSchedule, MainWorld, Render, RenderApp, RenderSet, }; use bevy_utils::tracing::warn; diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index b94930ee0331d..e5f8e7b086de9 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -105,7 +105,7 @@ use { ShaderStages, ShaderType, VertexFormat, }, renderer::RenderDevice, - world_sync::TemporaryRenderEntity, + sync_world::TemporaryRenderEntity, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }, bytemuck::cast_slice, diff --git a/crates/bevy_pbr/src/bundle.rs b/crates/bevy_pbr/src/bundle.rs index 55cdb3609a892..107c56f719838 100644 --- a/crates/bevy_pbr/src/bundle.rs +++ b/crates/bevy_pbr/src/bundle.rs @@ -15,8 +15,8 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ mesh::Mesh3d, primitives::{CascadesFrusta, CubemapFrusta, Frustum}, + sync_world::SyncToRenderWorld, view::{InheritedVisibility, ViewVisibility, Visibility}, - world_sync::SyncToRenderWorld, }; use bevy_transform::components::{GlobalTransform, Transform}; diff --git a/crates/bevy_pbr/src/cluster/mod.rs b/crates/bevy_pbr/src/cluster/mod.rs index 375861e294907..ce54c20a8acd2 100644 --- a/crates/bevy_pbr/src/cluster/mod.rs +++ b/crates/bevy_pbr/src/cluster/mod.rs @@ -20,7 +20,7 @@ use bevy_render::{ UniformBuffer, }, renderer::{RenderDevice, RenderQueue}, - world_sync::RenderEntity, + sync_world::RenderEntity, Extract, }; use bevy_utils::{hashbrown::HashSet, tracing::warn}; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 369efdcb59c0b..f2eb51a33e91c 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -127,6 +127,7 @@ use bevy_render::{ render_asset::prepare_assets, render_graph::RenderGraph, render_resource::Shader, + sync_component::SyncComponentPlugin, texture::{GpuImage, Image}, view::{check_visibility, VisibilitySystems}, ExtractSchedule, Render, RenderApp, RenderSet, @@ -314,7 +315,6 @@ impl Plugin for PbrPlugin { .register_type::() .register_type::() .register_type::() - .register_type::() .register_type::() .init_resource::() .init_resource::() @@ -346,6 +346,11 @@ impl Plugin for PbrPlugin { VolumetricFogPlugin, ScreenSpaceReflectionsPlugin, )) + .add_plugins(( + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + )) .configure_sets( PostUpdate, ( diff --git a/crates/bevy_pbr/src/light/directional_light.rs b/crates/bevy_pbr/src/light/directional_light.rs index 1c53e924b2edb..3bc8c49cd2c88 100644 --- a/crates/bevy_pbr/src/light/directional_light.rs +++ b/crates/bevy_pbr/src/light/directional_light.rs @@ -1,4 +1,4 @@ -use bevy_render::{view::Visibility, world_sync::SyncToRenderWorld}; +use bevy_render::view::Visibility; use super::*; @@ -57,8 +57,7 @@ use super::*; CascadeShadowConfig, CascadesVisibleEntities, Transform, - Visibility, - SyncToRenderWorld + Visibility )] pub struct DirectionalLight { /// The color of the light. diff --git a/crates/bevy_pbr/src/light/point_light.rs b/crates/bevy_pbr/src/light/point_light.rs index 8b351985a0aa7..a351913aabd86 100644 --- a/crates/bevy_pbr/src/light/point_light.rs +++ b/crates/bevy_pbr/src/light/point_light.rs @@ -1,4 +1,4 @@ -use bevy_render::{view::Visibility, world_sync::SyncToRenderWorld}; +use bevy_render::view::Visibility; use super::*; @@ -21,13 +21,7 @@ use super::*; /// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lumen_(unit)#Lighting) #[derive(Component, Debug, Clone, Copy, Reflect)] #[reflect(Component, Default, Debug)] -#[require( - CubemapFrusta, - CubemapVisibleEntities, - Transform, - Visibility, - SyncToRenderWorld -)] +#[require(CubemapFrusta, CubemapVisibleEntities, Transform, Visibility)] pub struct PointLight { /// The color of this light source. pub color: Color, diff --git a/crates/bevy_pbr/src/light/spot_light.rs b/crates/bevy_pbr/src/light/spot_light.rs index b8345e13e00aa..9b78291b02c4c 100644 --- a/crates/bevy_pbr/src/light/spot_light.rs +++ b/crates/bevy_pbr/src/light/spot_light.rs @@ -1,4 +1,4 @@ -use bevy_render::{view::Visibility, world_sync::SyncToRenderWorld}; +use bevy_render::view::Visibility; use super::*; @@ -9,7 +9,7 @@ use super::*; /// the transform, and can be specified with [`Transform::looking_at`](Transform::looking_at). #[derive(Component, Debug, Clone, Copy, Reflect)] #[reflect(Component, Default, Debug)] -#[require(Frustum, VisibleMeshEntities, Transform, Visibility, SyncToRenderWorld)] +#[require(Frustum, VisibleMeshEntities, Transform, Visibility)] pub struct SpotLight { /// The color of the light. /// diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index 2bfd2408a9fe4..68a401c60ad27 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -21,9 +21,9 @@ use bevy_render::{ render_resource::{DynamicUniformBuffer, Sampler, Shader, ShaderType, TextureView}, renderer::{RenderDevice, RenderQueue}, settings::WgpuFeatures, + sync_world::RenderEntity, texture::{FallbackImage, GpuImage, Image}, view::ExtractedView, - world_sync::RenderEntity, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::{components::Transform, prelude::GlobalTransform}; diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index dc4bb77be1325..f3f6fcbb04d8e 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -3,7 +3,7 @@ mod prepass_bindings; use bevy_render::{ mesh::{Mesh3d, MeshVertexBufferLayoutRef, RenderMesh}, render_resource::binding_types::uniform_buffer, - world_sync::RenderEntity, + sync_world::RenderEntity, }; pub use prepass_bindings::*; diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 584c6f4fef433..18a5a0955d6a3 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -8,7 +8,7 @@ use bevy_ecs::{ system::lifetimeless::Read, }; use bevy_math::{ops, Mat4, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; -use bevy_render::world_sync::RenderEntity; +use bevy_render::sync_world::RenderEntity; use bevy_render::{ diagnostic::RecordDiagnostics, mesh::RenderMesh, diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index b81a013d65f33..eb9357278c72c 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -30,9 +30,9 @@ use bevy_render::{ *, }, renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue}, + sync_world::RenderEntity, texture::{CachedTexture, TextureCache}, view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms}, - world_sync::RenderEntity, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_utils::{ diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index 1636f8734bb8a..7a94a1e135797 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -51,6 +51,7 @@ use bevy_render::{ mesh::{Mesh, Meshable}, render_graph::{RenderGraphApp, ViewNodeRunner}, render_resource::{Shader, SpecializedRenderPipelines}, + sync_component::SyncComponentPlugin, texture::Image, view::{InheritedVisibility, ViewVisibility, Visibility}, ExtractSchedule, Render, RenderApp, RenderSet, @@ -231,6 +232,8 @@ impl Plugin for VolumetricFogPlugin { app.register_type::() .register_type::(); + app.add_plugins(SyncComponentPlugin::::default()); + let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index ae1e0e7f81bc3..d6b151fd95b91 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -36,9 +36,9 @@ use bevy_render::{ TextureSampleType, TextureUsages, VertexState, }, renderer::{RenderContext, RenderDevice, RenderQueue}, + sync_world::RenderEntity, texture::{BevyDefault as _, GpuImage, Image}, view::{ExtractedView, Msaa, ViewDepthTexture, ViewTarget, ViewUniformOffset}, - world_sync::RenderEntity, Extract, }; use bevy_transform::components::GlobalTransform; diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index b34dcb4717f65..a272df4e6c167 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -6,12 +6,12 @@ use crate::{ render_asset::RenderAssets, render_graph::{InternedRenderSubGraph, RenderSubGraph}, render_resource::TextureView, + sync_world::{RenderEntity, SyncToRenderWorld}, texture::GpuImage, view::{ ColorGrading, ExtractedView, ExtractedWindows, GpuCulling, Msaa, RenderLayers, Visibility, VisibleEntities, }, - world_sync::{RenderEntity, SyncToRenderWorld}, Extract, }; use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index cfbdaa4f1d078..29c9d229abf62 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -1,8 +1,9 @@ use crate::{ render_resource::{encase::internal::WriteInto, DynamicUniformBuffer, ShaderType}, renderer::{RenderDevice, RenderQueue}, + sync_component::SyncComponentPlugin, + sync_world::RenderEntity, view::ViewVisibility, - world_sync::{RenderEntity, SyncToRenderWorld}, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_app::{App, Plugin}; @@ -12,7 +13,6 @@ use bevy_ecs::{ prelude::*, query::{QueryFilter, QueryItem, ReadOnlyQueryData}, system::lifetimeless::Read, - world::OnAdd, }; use core::{marker::PhantomData, ops::Deref}; @@ -160,15 +160,6 @@ fn prepare_uniform_components( /// This plugin extracts the components into the render world for synced entities. /// /// To do so, it sets up the [`ExtractSchedule`] step for the specified [`ExtractComponent`]. -/// -/// # Warning -/// -/// Be careful when removing the [`ExtractComponent`] from an entity. When an [`ExtractComponent`] -/// is added to an entity, that entity is automatically synced with the render world (see also -/// [`WorldSyncPlugin`](crate::world_sync::WorldSyncPlugin)). When removing the entity in the main -/// world, the synced entity also gets removed. However, if only the [`ExtractComponent`] is removed -/// this *doesn't* happen, and the synced entity stays around with the old extracted data. -/// We recommend despawning the entire entity, instead of only removing [`ExtractComponent`]. pub struct ExtractComponentPlugin { only_extract_visible: bool, marker: PhantomData (C, F)>, @@ -194,10 +185,8 @@ impl ExtractComponentPlugin { impl Plugin for ExtractComponentPlugin { fn build(&self, app: &mut App) { - // TODO: use required components - app.observe(|trigger: Trigger, mut commands: Commands| { - commands.entity(trigger.entity()).insert(SyncToRenderWorld); - }); + app.add_plugins(SyncComponentPlugin::::default()); + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { if self.only_extract_visible { render_app.add_systems(ExtractSchedule, extract_visible_components::); @@ -219,7 +208,7 @@ impl ExtractComponent for Handle { } } -/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`SyncToRenderWorld`]. +/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`]. fn extract_components( mut commands: Commands, mut previous_len: Local, @@ -235,7 +224,7 @@ fn extract_components( commands.insert_or_spawn_batch(values); } -/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are visible and synced via [`SyncToRenderWorld`]. +/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are visible and synced via [`crate::sync_world::SyncToRenderWorld`]. fn extract_visible_components( mut commands: Commands, mut previous_len: Local, diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 801a6b2d1c20d..ca86ba3a40fe2 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -30,7 +30,7 @@ use core::ops::{Deref, DerefMut}; /// ``` /// use bevy_ecs::prelude::*; /// use bevy_render::Extract; -/// use bevy_render::world_sync::RenderEntity; +/// use bevy_render::sync_world::RenderEntity; /// # #[derive(Component)] /// // Do make sure to sync the cloud entities before extracting them. /// # struct Cloud; diff --git a/crates/bevy_render/src/gpu_readback.rs b/crates/bevy_render/src/gpu_readback.rs index ffbf83ab11731..434b4603cac7c 100644 --- a/crates/bevy_render/src/gpu_readback.rs +++ b/crates/bevy_render/src/gpu_readback.rs @@ -5,6 +5,7 @@ use crate::{ render_resource::{Buffer, BufferUsages, Extent3d, ImageDataLayout, Texture, TextureFormat}, renderer::{render_system, RenderDevice}, storage::{GpuShaderStorageBuffer, ShaderStorageBuffer}, + sync_world::MainEntity, texture::{GpuImage, TextureFormatPixelInfo}, ExtractSchedule, MainWorld, Render, RenderApp, RenderSet, }; @@ -232,7 +233,7 @@ fn prepare_buffers( mut buffer_pool: ResMut, gpu_images: Res>, ssbos: Res>, - handles: Query<(Entity, &Readback)>, + handles: Query<(&MainEntity, &Readback)>, ) { for (entity, readback) in handles.iter() { match readback { @@ -254,7 +255,7 @@ fn prepare_buffers( ); let (tx, rx) = async_channel::bounded(1); readbacks.requested.push(GpuReadback { - entity, + entity: entity.id(), src: ReadbackSource::Texture { texture: gpu_image.texture.clone(), layout, @@ -272,7 +273,7 @@ fn prepare_buffers( let buffer = buffer_pool.get(&render_device, size); let (tx, rx) = async_channel::bounded(1); readbacks.requested.push(GpuReadback { - entity, + entity: entity.id(), src: ReadbackSource::Buffer { src_start: 0, dst_start: 0, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 7ab9642ce5dbe..a682f2b954413 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -38,9 +38,10 @@ pub mod renderer; pub mod settings; mod spatial_bundle; pub mod storage; +pub mod sync_component; +pub mod sync_world; pub mod texture; pub mod view; -pub mod world_sync; /// The render prelude. /// @@ -77,8 +78,8 @@ use extract_resource::ExtractResourcePlugin; use globals::GlobalsPlugin; use render_asset::RenderAssetBytesPerFrame; use renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}; -use world_sync::{ - despawn_temporary_render_entities, entity_sync_system, SyncToRenderWorld, WorldSyncPlugin, +use sync_world::{ + despawn_temporary_render_entities, entity_sync_system, SyncToRenderWorld, SyncWorldPlugin, }; use crate::gpu_readback::GpuReadbackPlugin; @@ -371,7 +372,7 @@ impl Plugin for RenderPlugin { GlobalsPlugin, MorphPlugin, BatchingPlugin, - WorldSyncPlugin, + SyncWorldPlugin, StoragePlugin, GpuReadbackPlugin::default(), )); diff --git a/crates/bevy_render/src/pipelined_rendering.rs b/crates/bevy_render/src/pipelined_rendering.rs index f17209665b7b5..41279e7d25db1 100644 --- a/crates/bevy_render/src/pipelined_rendering.rs +++ b/crates/bevy_render/src/pipelined_rendering.rs @@ -81,7 +81,7 @@ impl Drop for RenderAppChannels { /// The plugin is dependent on the [`RenderApp`] added by [`crate::RenderPlugin`] and so must /// be added after that plugin. If it is not added after, the plugin will do nothing. /// -/// A single frame of execution looks something like below +/// A single frame of execution looks something like below /// /// ```text /// |---------------------------------------------------------------------------| @@ -92,7 +92,7 @@ impl Drop for RenderAppChannels { /// ``` /// /// - `sync` is the step where the entity-entity mapping between the main and render world is updated. -/// This is run on the main app's thread. For more information checkout [`WorldSyncPlugin`]. +/// This is run on the main app's thread. For more information checkout [`SyncWorldPlugin`]. /// - `extract` is the step where data is copied from the main world to the render world. /// This is run on the main app's thread. /// - On the render thread, we first apply the `extract commands`. This is not run during extract, so the @@ -104,7 +104,7 @@ impl Drop for RenderAppChannels { /// - And finally the `main app schedule` is run. /// - Once both the `main app schedule` and the `render schedule` are finished running, `extract` is run again. /// -/// [`WorldSyncPlugin`]: crate::world_sync::WorldSyncPlugin +/// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin #[derive(Default)] pub struct PipelinedRenderingPlugin; diff --git a/crates/bevy_render/src/sync_component.rs b/crates/bevy_render/src/sync_component.rs new file mode 100644 index 0000000000000..0fac10b409a17 --- /dev/null +++ b/crates/bevy_render/src/sync_component.rs @@ -0,0 +1,42 @@ +use core::marker::PhantomData; + +use bevy_app::{App, Plugin}; +use bevy_ecs::component::Component; + +use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}; + +/// Plugin that registers a component for automatic sync to the render world. See [`SyncWorldPlugin`] for more information. +/// +/// This plugin is automatically added by [`ExtractComponentPlugin`], and only needs to be added for manual extraction implementations. +/// +/// # Implementation details +/// +/// It adds [`SyncToRenderWorld`] as a required component to make the [`SyncWorldPlugin`] aware of the component, and +/// handles cleanup of the component in the render world when it is removed from an entity. +/// +/// # Warning +/// When the component is removed from the main world entity, all components are removed from the entity in the render world. +/// This is done in order to handle components with custom extraction logic and derived state. +/// +/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin +/// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin +pub struct SyncComponentPlugin(PhantomData); + +impl Default for SyncComponentPlugin { + fn default() -> Self { + Self(PhantomData) + } +} + +impl Plugin for SyncComponentPlugin { + fn build(&self, app: &mut App) { + app.register_required_components::(); + + app.world_mut().register_component_hooks::().on_remove( + |mut world, entity, _component_id| { + let mut pending = world.resource_mut::(); + pending.push(EntityRecord::ComponentRemoved(entity)); + }, + ); + } +} diff --git a/crates/bevy_render/src/world_sync.rs b/crates/bevy_render/src/sync_world.rs similarity index 79% rename from crates/bevy_render/src/world_sync.rs rename to crates/bevy_render/src/sync_world.rs index e0f81fdc02692..3ee9f25fbdbe5 100644 --- a/crates/bevy_render/src/world_sync.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -13,11 +13,18 @@ use bevy_reflect::Reflect; /// A plugin that synchronizes entities with [`SyncToRenderWorld`] between the main world and the render world. /// +/// All entities with the [`SyncToRenderWorld`] component are kept in sync. It +/// is automatically added as a required component by [`ExtractComponentPlugin`] +/// and [`SyncComponentPlugin`], so it doesn't need to be added manually when +/// spawning or as a required component when either of these plugins are used. +/// +/// # Implementation +/// /// Bevy's renderer is architected independently from the main app. /// It operates in its own separate ECS [`World`], so the renderer logic can run in parallel with the main world logic. /// This is called "Pipelined Rendering", see [`PipelinedRenderingPlugin`] for more information. /// -/// [`WorldSyncPlugin`] is the first thing that runs every frame and it maintains an entity-to-entity mapping +/// [`SyncWorldPlugin`] is the first thing that runs every frame and it maintains an entity-to-entity mapping /// between the main world and the render world. /// It does so by spawning and despawning entities in the render world, to match spawned and despawned entities in the main world. /// The link between synced entities is maintained by the [`RenderEntity`] and [`MainEntity`] components. @@ -66,14 +73,16 @@ use bevy_reflect::Reflect; /// The render world probably cares about a `Position` component, but not a `Velocity` component. /// The extraction happens in its own step, independently from, and after synchronization. /// -/// Moreover, [`WorldSyncPlugin`] only synchronizes *entities*. [`RenderAsset`](crate::render_asset::RenderAsset)s like meshes and textures are handled +/// Moreover, [`SyncWorldPlugin`] only synchronizes *entities*. [`RenderAsset`](crate::render_asset::RenderAsset)s like meshes and textures are handled /// differently. /// /// [`PipelinedRenderingPlugin`]: crate::pipelined_rendering::PipelinedRenderingPlugin +/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin +/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin #[derive(Default)] -pub struct WorldSyncPlugin; +pub struct SyncWorldPlugin; -impl Plugin for WorldSyncPlugin { +impl Plugin for SyncWorldPlugin { fn build(&self, app: &mut bevy_app::App) { app.init_resource::(); app.observe( @@ -86,22 +95,30 @@ impl Plugin for WorldSyncPlugin { mut pending: ResMut, query: Query<&RenderEntity>| { if let Ok(e) = query.get(trigger.entity()) { - pending.push(EntityRecord::Removed(e.id())); + pending.push(EntityRecord::Removed(*e)); }; }, ); } } -/// Marker component that indicates that its entity needs to be synchronized to the render world +/// Marker component that indicates that its entity needs to be synchronized to the render world. +/// +/// This component is automatically added as a required component by [`ExtractComponentPlugin`] and [`SyncComponentPlugin`]. +/// For more information see [`SyncWorldPlugin`]. /// /// NOTE: This component should persist throughout the entity's entire lifecycle. /// If this component is removed from its entity, the entity will be despawned. +/// +/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin +/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin #[derive(Component, Clone, Debug, Default, Reflect)] #[reflect[Component]] #[component(storage = "SparseSet")] pub struct SyncToRenderWorld; -/// Component added on the main world entities that are synced to the Render World in order to keep track of the corresponding render world entity +/// Component added on the main world entities that are synced to the Render World in order to keep track of the corresponding render world entity. +/// +/// Can also be used as a newtype wrapper for render world entities. #[derive(Component, Deref, Clone, Debug, Copy)] pub struct RenderEntity(Entity); impl RenderEntity { @@ -111,7 +128,9 @@ impl RenderEntity { } } -/// Component added on the render world entities to keep track of the corresponding main world entity +/// Component added on the render world entities to keep track of the corresponding main world entity. +/// +/// Can also be used as a newtype wrapper for main world entities. #[derive(Component, Deref, Clone, Debug)] pub struct MainEntity(Entity); impl MainEntity { @@ -127,13 +146,17 @@ impl MainEntity { pub struct TemporaryRenderEntity; /// A record enum to what entities with [`SyncToRenderWorld`] have been added or removed. +#[derive(Debug)] pub(crate) enum EntityRecord { /// When an entity is spawned on the main world, notify the render world so that it can spawn a corresponding /// entity. This contains the main world entity. Added(Entity), /// When an entity is despawned on the main world, notify the render world so that the corresponding entity can be /// despawned. This contains the render world entity. - Removed(Entity), + Removed(RenderEntity), + /// When a component is removed from an entity, notify the render world so that the corresponding component can be + /// removed. This contains the main world entity. + ComponentRemoved(Entity), } // Entity Record in MainWorld pending to Sync @@ -148,8 +171,8 @@ pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut Worl for record in pending.drain(..) { match record { EntityRecord::Added(e) => { - if let Ok(mut entity) = world.get_entity_mut(e) { - match entity.entry::() { + if let Ok(mut main_entity) = world.get_entity_mut(e) { + match main_entity.entry::() { bevy_ecs::world::Entry::Occupied(_) => { panic!("Attempting to synchronize an entity that has already been synchronized!"); } @@ -161,11 +184,24 @@ pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut Worl }; } } - EntityRecord::Removed(e) => { - if let Ok(ec) = render_world.get_entity_mut(e) { + EntityRecord::Removed(render_entity) => { + if let Ok(ec) = render_world.get_entity_mut(render_entity.id()) { ec.despawn(); }; } + EntityRecord::ComponentRemoved(main_entity) => { + let Some(mut render_entity) = world.get_mut::(main_entity) else { + continue; + }; + if let Ok(render_world_entity) = render_world.get_entity_mut(render_entity.id()) { + // In order to handle components that extract to derived components, we clear the entity + // and let the extraction system re-add the components. + render_world_entity.despawn(); + + let id = render_world.spawn(MainEntity(main_entity)).id(); + render_entity.0 = id; + } + }, } } }); @@ -207,7 +243,7 @@ mod tests { struct RenderDataComponent; #[test] - fn world_sync() { + fn sync_world() { let mut main_world = World::new(); let mut render_world = World::new(); main_world.init_resource::(); @@ -222,7 +258,7 @@ mod tests { mut pending: ResMut, query: Query<&RenderEntity>| { if let Ok(e) = query.get(trigger.entity()) { - pending.push(EntityRecord::Removed(e.id())); + pending.push(EntityRecord::Removed(*e)); }; }, ); diff --git a/crates/bevy_sprite/src/bundle.rs b/crates/bevy_sprite/src/bundle.rs index df9e99e622dee..8d4cf1365f5de 100644 --- a/crates/bevy_sprite/src/bundle.rs +++ b/crates/bevy_sprite/src/bundle.rs @@ -2,9 +2,9 @@ use crate::Sprite; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; use bevy_render::{ + sync_world::SyncToRenderWorld, texture::Image, view::{InheritedVisibility, ViewVisibility, Visibility}, - world_sync::SyncToRenderWorld, }; use bevy_transform::components::{GlobalTransform, Transform}; diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 63b025d5a6747..d3bef973748b4 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -31,6 +31,7 @@ use bevy_render::{ *, }, renderer::{RenderDevice, RenderQueue}, + sync_world::{RenderEntity, TemporaryRenderEntity}, texture::{ BevyDefault, DefaultImageSampler, FallbackImage, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, @@ -39,7 +40,6 @@ use bevy_render::{ ExtractedView, Msaa, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms, ViewVisibility, VisibleEntities, }, - world_sync::{RenderEntity, TemporaryRenderEntity}, Extract, }; use bevy_transform::components::GlobalTransform; diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 646e28a748e12..db175fd09d436 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -15,7 +15,7 @@ use bevy_ecs::{ system::{Commands, Local, Query, Res, ResMut}, }; use bevy_math::Vec2; -use bevy_render::world_sync::TemporaryRenderEntity; +use bevy_render::sync_world::TemporaryRenderEntity; use bevy_render::{ primitives::Aabb, texture::Image, diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index a53f07b99544b..8f56581ce6f9d 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -19,9 +19,9 @@ use bevy_render::{ render_phase::*, render_resource::{binding_types::uniform_buffer, *}, renderer::{RenderDevice, RenderQueue}, + sync_world::{RenderEntity, TemporaryRenderEntity}, texture::BevyDefault, view::*, - world_sync::{RenderEntity, TemporaryRenderEntity}, Extract, ExtractSchedule, Render, RenderSet, }; use bevy_transform::prelude::GlobalTransform; diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 18b8022860fd3..3d6a57cbe57a4 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -32,9 +32,9 @@ use bevy_render::{ }; use bevy_render::{ render_phase::{PhaseItem, PhaseItemExtraIndex}, + sync_world::{RenderEntity, TemporaryRenderEntity}, texture::GpuImage, view::ViewVisibility, - world_sync::{RenderEntity, TemporaryRenderEntity}, ExtractSchedule, Render, }; use bevy_sprite::TextureAtlasLayout; diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index ac2bb134e3d2c..b8d85906286e5 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -18,9 +18,9 @@ use bevy_render::{ render_phase::*, render_resource::{binding_types::uniform_buffer, *}, renderer::{RenderDevice, RenderQueue}, + sync_world::{RenderEntity, TemporaryRenderEntity}, texture::BevyDefault, view::*, - world_sync::{RenderEntity, TemporaryRenderEntity}, Extract, ExtractSchedule, Render, RenderSet, }; use bevy_transform::prelude::GlobalTransform; diff --git a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs index 9811376a32fa4..f33f50b1717f3 100644 --- a/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_texture_slice_pipeline.rs @@ -16,9 +16,9 @@ use bevy_render::{ render_phase::*, render_resource::{binding_types::uniform_buffer, *}, renderer::{RenderDevice, RenderQueue}, + sync_world::{RenderEntity, TemporaryRenderEntity}, texture::{BevyDefault, GpuImage, Image, TRANSPARENT_IMAGE_HANDLE}, view::*, - world_sync::{RenderEntity, TemporaryRenderEntity}, Extract, ExtractSchedule, Render, RenderSet, }; use bevy_sprite::{ diff --git a/examples/3d/fog_volumes.rs b/examples/3d/fog_volumes.rs index 82e38e21989ae..c69a2f7513c1d 100644 --- a/examples/3d/fog_volumes.rs +++ b/examples/3d/fog_volumes.rs @@ -9,7 +9,6 @@ use bevy::{ math::vec3, pbr::{FogVolume, VolumetricFog, VolumetricLight}, prelude::*, - render::world_sync::SyncToRenderWorld, }; /// Entry point. @@ -44,9 +43,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // up. scattering: 1.0, ..default() - }) - // indicates that this fog volume needs to be Synchronized to the render world - .insert(SyncToRenderWorld); + }); // Spawn a bright directional light that illuminates the fog well. commands.spawn((