From 69200664aad9bea8967035759afca7eae4d1a0b3 Mon Sep 17 00:00:00 2001 From: Daniel Skates Date: Sun, 8 Feb 2026 16:30:37 +0800 Subject: [PATCH 1/2] Add marker for ExtractResource --- crates/bevy_render/src/extract_component.rs | 8 +++++--- crates/bevy_render/src/extract_resource.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 57a307ba7df88..3276f6fe932de 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -41,7 +41,7 @@ impl DynamicUniformIndex { /// The marker type is only used as a way to bypass the orphan rules. To /// implement the trait for a foreign type you can use a local type as the /// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`]. -pub trait ExtractComponent: SyncComponent { +pub trait ExtractComponent: SyncComponent { /// ECS [`ReadOnlyQueryData`] to fetch the components to extract. type QueryData: ReadOnlyQueryData; /// Filters the entities with additional constraints. @@ -174,9 +174,11 @@ impl ExtractComponentPlugin { } } -impl, Marker: 'static> Plugin for ExtractComponentPlugin { +impl + SyncComponent, Marker: 'static + Send + Sync> Plugin + for ExtractComponentPlugin +{ fn build(&self, app: &mut App) { - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(SyncComponentPlugin::::default()); if let Some(render_app) = app.get_sub_app_mut(RenderApp) { if self.only_extract_visible { diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index 91dae94d909f8..a999d14d67256 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -11,7 +11,7 @@ use crate::{Extract, ExtractSchedule, RenderApp}; /// /// Therefore the resource is transferred from the "main world" into the "render world" /// in the [`ExtractSchedule`] step. -pub trait ExtractResource: Resource { +pub trait ExtractResource: Resource { type Source: Resource; /// Defines how the resource is transferred into the "render world". @@ -22,18 +22,18 @@ pub trait ExtractResource: Resource { /// /// Therefore it sets up the[`ExtractSchedule`] step /// for the specified [`Resource`]. -pub struct ExtractResourcePlugin(PhantomData); +pub struct ExtractResourcePlugin, M = ()>(PhantomData<(R, M)>); -impl Default for ExtractResourcePlugin { +impl, M> Default for ExtractResourcePlugin { fn default() -> Self { Self(PhantomData) } } -impl Plugin for ExtractResourcePlugin { +impl, M: 'static + Send + Sync> Plugin for ExtractResourcePlugin { fn build(&self, app: &mut App) { if let Some(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.add_systems(ExtractSchedule, extract_resource::); + render_app.add_systems(ExtractSchedule, extract_resource::); } else { once!(bevy_log::error!( "Render app did not exist when trying to add `extract_resource` for <{}>.", @@ -44,7 +44,7 @@ impl Plugin for ExtractResourcePlugin { } /// This system extracts the resource of the corresponding [`Resource`] type -pub fn extract_resource( +pub fn extract_resource, M>( mut commands: Commands, main_resource: Extract>>, target_resource: Option>, From 7291304dfb39db48ba5295bcab07e0135c20df4a Mon Sep 17 00:00:00 2001 From: Daniel Skates Date: Mon, 9 Feb 2026 17:33:11 +0800 Subject: [PATCH 2/2] Use F --- crates/bevy_render/src/extract_component.rs | 22 +++++++++++---------- crates/bevy_render/src/extract_resource.rs | 20 +++++++++++++------ crates/bevy_render/src/sync_component.rs | 16 ++++++++------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 3276f6fe932de..0aa6aafc99696 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -38,10 +38,10 @@ impl DynamicUniformIndex { /// /// The Out type is defined in [`SyncComponent`]. /// -/// The marker type is only used as a way to bypass the orphan rules. To +/// The marker type `F` is only used as a way to bypass the orphan rules. To /// implement the trait for a foreign type you can use a local type as the /// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`]. -pub trait ExtractComponent: SyncComponent { +pub trait ExtractComponent: SyncComponent { /// ECS [`ReadOnlyQueryData`] to fetch the components to extract. type QueryData: ReadOnlyQueryData; /// Filters the entities with additional constraints. @@ -151,6 +151,10 @@ fn prepare_uniform_components( /// /// It also registers [`SyncComponentPlugin`] to ensure the extracted components /// are deleted if the main world components are removed. +/// +/// The marker type `F` is only used as a way to bypass the orphan rules. To +/// implement the trait for a foreign type you can use a local type as the +/// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`]. pub struct ExtractComponentPlugin { only_extract_visible: bool, marker: PhantomData (C, F)>, @@ -174,24 +178,22 @@ impl ExtractComponentPlugin { } } -impl + SyncComponent, Marker: 'static + Send + Sync> Plugin - for ExtractComponentPlugin -{ +impl, F: 'static + Send + Sync> Plugin for ExtractComponentPlugin { fn build(&self, app: &mut App) { - app.add_plugins(SyncComponentPlugin::::default()); + 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::); + render_app.add_systems(ExtractSchedule, extract_visible_components::); } else { - render_app.add_systems(ExtractSchedule, extract_components::); + render_app.add_systems(ExtractSchedule, extract_components::); } } } } /// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`]. -fn extract_components, Marker>( +fn extract_components, F>( mut commands: Commands, mut previous_len: Local, query: Extract>, @@ -209,7 +211,7 @@ fn extract_components, Marker>( } /// 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, Marker>( +fn extract_visible_components, F>( mut commands: Commands, mut previous_len: Local, query: Extract>, diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index a999d14d67256..5e374b0b56126 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -11,7 +11,11 @@ use crate::{Extract, ExtractSchedule, RenderApp}; /// /// Therefore the resource is transferred from the "main world" into the "render world" /// in the [`ExtractSchedule`] step. -pub trait ExtractResource: Resource { +/// +/// The marker type `F` is only used as a way to bypass the orphan rules. To +/// implement the trait for a foreign type you can use a local type as the +/// marker, e.g. the type of the plugin that calls [`ExtractResourcePlugin`]. +pub trait ExtractResource: Resource { type Source: Resource; /// Defines how the resource is transferred into the "render world". @@ -22,18 +26,22 @@ pub trait ExtractResource: Resource { /// /// Therefore it sets up the[`ExtractSchedule`] step /// for the specified [`Resource`]. -pub struct ExtractResourcePlugin, M = ()>(PhantomData<(R, M)>); +/// +/// The marker type `F` is only used as a way to bypass the orphan rules. To +/// implement the trait for a foreign type you can use a local type as the +/// marker, e.g. the type of the plugin that calls [`ExtractResourcePlugin`]. +pub struct ExtractResourcePlugin, F = ()>(PhantomData<(R, F)>); -impl, M> Default for ExtractResourcePlugin { +impl, F> Default for ExtractResourcePlugin { fn default() -> Self { Self(PhantomData) } } -impl, M: 'static + Send + Sync> Plugin for ExtractResourcePlugin { +impl, F: 'static + Send + Sync> Plugin for ExtractResourcePlugin { fn build(&self, app: &mut App) { if let Some(render_app) = app.get_sub_app_mut(RenderApp) { - render_app.add_systems(ExtractSchedule, extract_resource::); + render_app.add_systems(ExtractSchedule, extract_resource::); } else { once!(bevy_log::error!( "Render app did not exist when trying to add `extract_resource` for <{}>.", @@ -44,7 +52,7 @@ impl, M: 'static + Send + Sync> Plugin for ExtractResource } /// This system extracts the resource of the corresponding [`Resource`] type -pub fn extract_resource, M>( +pub fn extract_resource, F>( mut commands: Commands, main_resource: Extract>>, target_resource: Option>, diff --git a/crates/bevy_render/src/sync_component.rs b/crates/bevy_render/src/sync_component.rs index 0b5ddf68e87cd..fd89f5d74166e 100644 --- a/crates/bevy_render/src/sync_component.rs +++ b/crates/bevy_render/src/sync_component.rs @@ -12,6 +12,10 @@ use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}; /// /// This plugin is automatically added by [`ExtractComponentPlugin`], and only needs to be added for manual extraction implementations. /// +/// The marker type `F` is only used as a way to bypass the orphan rules. To +/// implement the trait for a foreign type you can use a local type as the +/// marker, e.g. the type of the plugin that calls [`SyncComponentPlugin`]. +/// /// # Implementation details /// /// It adds [`SyncToRenderWorld`] as a required component to make the [`SyncWorldPlugin`] aware of the component, and @@ -19,9 +23,9 @@ use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}; /// /// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin /// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin -pub struct SyncComponentPlugin(PhantomData<(C, Marker)>); +pub struct SyncComponentPlugin(PhantomData<(C, F)>); -impl, Marker> Default for SyncComponentPlugin { +impl, F> Default for SyncComponentPlugin { fn default() -> Self { Self(PhantomData) } @@ -33,12 +37,12 @@ impl, Marker> Default for SyncComponentPlugin: Component { +pub trait SyncComponent: Component { /// Describes what components should be removed from the render world if the /// implementing component is removed. /// @@ -51,9 +55,7 @@ pub trait SyncComponent: Component { // type Out: Component = Self; } -impl, Marker: Send + Sync + 'static> Plugin - for SyncComponentPlugin -{ +impl, F: Send + Sync + 'static> Plugin for SyncComponentPlugin { fn build(&self, app: &mut App) { app.register_required_components::();