Skip to content

Use of SyncComponentPlugin on ExtractComponentPlugin causes RenderEntity to be incorrectly despawned #18722

@tychedelia

Description

@tychedelia

Bevy version

Since 0.15.

What you did

use bevy::prelude::*;
use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin};
use bevy_render::sync_world::RenderEntity;
use bevy_render::{Extract, RenderApp};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins((
            TestExtractPlugin,
            ExtractComponentPlugin::<ComponentA>::default(),
            ExtractComponentPlugin::<ComponentB>::default(),
        ))
        .add_systems(Startup, setup)
        .add_systems(Update, update)
        .run();
}

#[derive(Component, ExtractComponent, Clone)]
struct ComponentA;
#[derive(Component, ExtractComponent, Clone)]
struct ComponentB;

/// set up a simple 3D scene
fn setup(mut commands: Commands) {
    commands.spawn((ComponentA, ComponentB));
}

fn update(mut commands: Commands, mut query: Query<(Entity), With<ComponentB>>) {
    for (entity) in query.iter() {
        commands.entity(entity).remove::<ComponentA>();
        commands.entity(entity).insert(ComponentA);
    }
}

struct TestExtractPlugin;

impl Plugin for TestExtractPlugin {
    fn build(&self, app: &mut App) {
        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
            return;
        };

        render_app
            .add_systems(ExtractSchedule, extract);
    }
}

fn extract(query: Extract<Query<RenderEntity, With<ComponentB>>>) {
    for (entity) in query.iter() {
        println!("Render entity id: {:?}", entity);
    }
}

What went wrong

Adding ExtractComponentPlugin adds SyncComponentPlugin, which will cause the RenderEntity to be changed in entity_sync_system if the component is removed.

Unfortunately, this logic doesn't make sense, as it's possible for multiple components a main world entity to want to be extracted. Further, being removed doesn't mean that the main world entity necessarily needs to be de-synced from the render world, as it could be being extracted for other reasons. This leads to very confusing and hard to debug behavior and is generally just incorrect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsA-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behaviorS-Needs-DesignThis issue requires design work to think about how it would best be accomplished

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions