Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronize removed components with the render world #15582

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
14 changes: 14 additions & 0 deletions crates/bevy_render/src/sync_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ use bevy_ecs::component::Component;

use crate::world_sync::{EntityRecord, PendingSyncEntity, SyncToRenderWorld};

/// Plugin that registers a component for automatic sync to the render world. See [`WorldSyncPlugin`] 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 [`WorldSyncPlugin`] aware of the component, and
/// handles cleanup of the component in the render world when it is removed from an entity.
///
/// NOTE: When the component is removed from the main world entity, all components are removed from the entity in the render world.
kristoff3r marked this conversation as resolved.
Show resolved Hide resolved
/// This is in order to handle components with custom extraction logic.
///
/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
/// [`WorldSyncPlugin`]: crate::world_sync::WorldSyncPlugin
pub struct SyncComponentPlugin<C: Component>(PhantomData<C>);

impl<C: Component> Default for SyncComponentPlugin<C> {
Expand Down
16 changes: 14 additions & 2 deletions crates/bevy_render/src/world_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ use bevy_reflect::Reflect;

/// A plugin that synchronizes entities with [`SyncToRenderWorld`] between the main world and the render world.
///
/// [`SyncToRenderWorld`] is automatically added as a required components by
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
/// [`ExtractComponentPlugin`] and [`SyncComponentPlugin`], so it doesn't need be
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved
/// added manually when spawning or as a required component when either of these 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.
Expand Down Expand Up @@ -70,6 +76,8 @@ use bevy_reflect::Reflect;
/// differently.
///
/// [`PipelinedRenderingPlugin`]: crate::pipelined_rendering::PipelinedRenderingPlugin
/// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin
/// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin
#[derive(Default)]
pub struct WorldSyncPlugin;

Expand Down Expand Up @@ -102,6 +110,8 @@ impl Plugin for WorldSyncPlugin {
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
///
/// Can also be used as a newtype wrapper for render world entities.
#[derive(Component, Deref, Clone, Debug, Copy)]
pub struct RenderEntity(Entity);
impl RenderEntity {
Expand All @@ -112,6 +122,8 @@ impl RenderEntity {
}

/// 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 {
Expand Down Expand Up @@ -171,12 +183,12 @@ pub(crate) fn entity_sync_system(main_world: &mut World, render_world: &mut Worl
};
}
EntityRecord::ComponentRemoved(main_entity) => {
// It's difficult to remove only the relevant component because component ids aren't stable across worlds,
// so we just clear the entire render world entity.
let Some(mut render_entity) = world.get_mut::<RenderEntity>(main_entity) else {
continue;
};
if let Some(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();
Expand Down