Skip to content
Merged
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
31 changes: 13 additions & 18 deletions _release-content/migration-guides/extract_refactor.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
---
title: "`ExtractComponent` refactor"
pull_requests: [22766]
pull_requests: [22766, 23334]
---

The `Out` type from `ExtractComponent` has been split into a separate `SyncComponent` trait.
Previously, `SyncComponentPlugin`/`ExtractComponentPlugin` would despawn the render entity thus removing all the derived components if the component was removed. Now the render entity is no longer despawned and only the `Target` components of `SyncComponent` trait are removed.

Both traits have also gotten an optional marker type that can be used to bypass orphan rules, see the docs for details.
`SyncComponent` is a subtrait of `ExtractComponent` and you must implement it to clean up extracted and derived components.

```rust,ignore
impl SyncComponent for MyComponent {
type Target = (Self, OtherDerivedComponents);
}

impl ExtractComponent for MyComponent {
type QueryData = ();
type QueryFilter = ();
Expand All @@ -21,21 +25,12 @@ impl ExtractComponent for MyComponent {
}
```

After:
You can also specify the sync target (default to `Self`) using `extract_component_sync_target` attribute in derive macros.

```rust,ignore
impl SyncComponent for MyComponent {
type Out = Self;
}

impl ExtractComponent for MyComponent {
type QueryData = ();
type QueryFilter = ();

fn extract_component(
item: QueryItem<'_, '_, Self::QueryData>,
) -> Option<Self::Out> {
Some(*item)
}
}
#[derive(Component, ExtractComponent)]
#[extract_component_sync_target((Self, OtherDerivedComponents))]
struct MyComponent;
```

Both `SyncComponent` and `ExtractComponent` have also gotten an optional marker type that can be used to bypass orphan rules, see the docs for details.
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ pub struct CasUniform {
}

impl SyncComponent for ContrastAdaptiveSharpening {
type Out = (DenoiseCas, CasUniform);
type Target = (DenoiseCas, CasUniform);
}

impl ExtractComponent for ContrastAdaptiveSharpening {
type QueryData = &'static Self;
type QueryFilter = With<Camera>;
type Out = (DenoiseCas, CasUniform);

fn extract_component(item: QueryItem<Self::QueryData>) -> Option<Self::Out> {
if !item.enabled || item.sharpening_strength == 0.0 {
Expand Down
20 changes: 7 additions & 13 deletions crates/bevy_anti_alias/src/smaa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
component::Component,
entity::Entity,
lifecycle::Remove,
observer::On,
query::With,
reflect::ReflectComponent,
resource::Resource,
Expand Down Expand Up @@ -85,6 +83,13 @@ pub struct SmaaPlugin;
/// for a [`bevy_camera::Camera`].
#[derive(Clone, Copy, Default, Component, Reflect, ExtractComponent)]
#[reflect(Component, Default, Clone)]
#[extract_component_sync_target((
Self,
SmaaTextures,
SmaaPipelines,
SmaaBindGroups,
ViewSmaaPipelines,
))]
#[doc(alias = "SubpixelMorphologicalAntiAliasing")]
pub struct Smaa {
/// A predefined set of SMAA parameters: i.e. a quality level.
Expand Down Expand Up @@ -331,17 +336,6 @@ impl Plugin for SmaaPlugin {
return;
};

// TODO: remove this manual cleanup when ExtractComponent gets support
// for cleanup of derived components
render_app.add_observer(|event: On<Remove, Smaa>, mut commands: Commands| {
commands.entity(event.entity).remove::<(
SmaaTextures,
SmaaPipelines,
SmaaBindGroups,
ViewSmaaPipelines,
)>();
});

render_app
.insert_resource(smaa_luts)
.init_resource::<SmaaSpecializedRenderPipelines>()
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_anti_alias/src/taa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ impl Default for TemporalAntiAliasing {
}

impl SyncComponent for TemporalAntiAliasing {
type Out = Self;
type Target = Self;
}

fn temporal_anti_alias(
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_core_pipeline/src/oit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use resolve::OitResolvePlugin;

use crate::{
core_3d::main_transparent_pass_3d,
oit::resolve::node::oit_resolve,
oit::resolve::{node::oit_resolve, OitResolvePipelineId},
schedule::{Core3d, Core3dSystems},
};

Expand All @@ -37,6 +37,7 @@ pub mod resolve;
// This should probably be done by adding an enum to this component.
// We use the same struct to pass on the settings to the drawing shader.
#[derive(Clone, Copy, ExtractComponent, Reflect, ShaderType, Component)]
#[extract_component_sync_target((Self, OrderIndependentTransparencySettingsOffset, OitResolvePipelineId))]
#[reflect(Clone, Default)]
pub struct OrderIndependentTransparencySettings {
/// Controls how many fragments will be exactly sorted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ use crate::{
pub struct NoBackgroundMotionVectors;

impl SyncComponent for NoBackgroundMotionVectors {
type Out = Self;
type Target = Self;
}

impl ExtractComponent for NoBackgroundMotionVectors {
type QueryData = Read<NoBackgroundMotionVectors>;
type QueryFilter = ();
type Out = Self;

fn extract_component(_item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
Some(NoBackgroundMotionVectors)
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/atmosphere/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,13 @@ impl From<AtmosphereSettings> for GpuAtmosphereSettings {
}

impl SyncComponent for GpuAtmosphereSettings {
type Out = Self;
type Target = Self;
}

impl ExtractComponent for GpuAtmosphereSettings {
type QueryData = Read<AtmosphereSettings>;
type QueryFilter = (With<Camera3d>, With<Atmosphere>);
type Out = Self;

fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
Some(item.clone().into())
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/contact_shadows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,13 @@ impl From<ContactShadows> for ContactShadowsUniform {
}

impl SyncComponent for ContactShadows {
type Out = Self;
type Target = Self;
}

impl ExtractComponent for ContactShadows {
type QueryData = &'static ContactShadows;
type QueryFilter = ();
type Out = Self;

fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
Some(*settings)
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_pbr/src/decal/clustered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl Plugin for ClusteredDecalPlugin {
}

impl SyncComponent<ClusteredDecalPlugin> for ClusteredDecal {
type Out = Self;
type Target = Self;
}

// This is needed because of the orphan rule not allowing implementing
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,17 +381,17 @@ pub fn stbn_placeholder() -> Image {
}

impl SyncComponent<PbrPlugin> for DirectionalLight {
type Out = Self;
type Target = Self;
}
impl SyncComponent<PbrPlugin> for PointLight {
type Out = Self;
type Target = Self;
}
impl SyncComponent<PbrPlugin> for SpotLight {
type Out = Self;
type Target = Self;
}
impl SyncComponent<PbrPlugin> for AmbientLight {
type Out = Self;
type Target = Self;
}
impl SyncComponent<PbrPlugin> for ShadowFilteringMethod {
type Out = Self;
type Target = Self;
}
2 changes: 1 addition & 1 deletion crates/bevy_pbr/src/light_probe/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,5 +1107,5 @@ pub fn generate_environment_map_light(
}

impl SyncComponent<EnvironmentMapGenerationPlugin> for GeneratedEnvironmentMapLight {
type Out = RenderEnvironmentMap;
type Target = RenderEnvironmentMap;
}
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/ssr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,12 +516,17 @@ pub fn prepare_ssr_settings(
}

impl SyncComponent for ScreenSpaceReflections {
type Out = ScreenSpaceReflectionsUniform;
type Target = (
ScreenSpaceReflectionsUniform,
ViewScreenSpaceReflectionsUniformOffset,
ScreenSpaceReflectionsPipelineId,
);
}

impl ExtractComponent for ScreenSpaceReflections {
type QueryData = Read<ScreenSpaceReflections>;
type QueryFilter = ();
type Out = ScreenSpaceReflectionsUniform;

fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
if !DEPTH_TEXTURE_SAMPLING_SUPPORTED {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_pbr/src/volumetric_fog/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,5 @@ impl Plugin for VolumetricFogPlugin {
}

impl SyncComponent<VolumetricFogPlugin> for FogVolume {
type Out = Self;
type Target = Self;
}
3 changes: 2 additions & 1 deletion crates/bevy_post_process/src/bloom/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,13 @@ pub enum BloomCompositeMode {
}

impl SyncComponent for Bloom {
type Out = (Self, BloomUniforms);
type Target = (Self, BloomUniforms);
}

impl ExtractComponent for Bloom {
type QueryData = (&'static Self, &'static Camera);
type QueryFilter = With<Hdr>;
type Out = (Self, BloomUniforms);

fn extract_component((bloom, camera): QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
match (
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_post_process/src/dof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ impl SpecializedRenderPipeline for DepthOfFieldPipeline {
}

impl SyncComponent for DepthOfField {
type Out = (
type Target = (
DepthOfField,
DepthOfFieldUniform,
DepthOfFieldPipelines,
Expand Down Expand Up @@ -683,7 +683,7 @@ fn extract_depth_of_field_settings(

// Depth of field is nonsensical without a perspective projection.
let Projection::Perspective(ref perspective_projection) = *projection else {
entity_commands.remove::<<DepthOfField as SyncComponent>::Out>();
entity_commands.remove::<<DepthOfField as SyncComponent>::Target>();

continue;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,13 @@ impl Default for ChromaticAberration {
}

impl SyncComponent for ChromaticAberration {
type Out = Self;
type Target = Self;
}

impl ExtractComponent for ChromaticAberration {
type QueryData = Read<ChromaticAberration>;
type QueryFilter = With<Camera>;
type Out = Self;

fn extract_component(
chromatic_aberration: QueryItem<'_, '_, Self::QueryData>,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_post_process/src/effect_stack/vignette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,13 @@ impl Default for Vignette {
}

impl SyncComponent for Vignette {
type Out = Self;
type Target = Self;
}

impl ExtractComponent for Vignette {
type QueryData = Read<Vignette>;
type QueryFilter = With<Camera>;
type Out = Self;

fn extract_component(vignette: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
// Skip the postprocessing phase entirely if the intensity is zero.
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_post_process/src/motion_blur/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,13 @@ impl Default for MotionBlur {
}

impl SyncComponent for MotionBlur {
type Out = MotionBlurUniform;
type Target = MotionBlurUniform;
}

impl ExtractComponent for MotionBlur {
type QueryData = &'static Self;
type QueryFilter = With<Camera>;
type Out = MotionBlurUniform;

fn extract_component(item: QueryItem<Self::QueryData>) -> Option<Self::Out> {
Some(MotionBlurUniform {
Expand Down
23 changes: 21 additions & 2 deletions crates/bevy_render/macros/src/extract_component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,34 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream {
}
};

let sync_target = if let Some(attr) = ast
.attrs
.iter()
.find(|a| a.path().is_ident("extract_component_sync_target"))
{
let sync_target = match attr.parse_args::<syn::Type>() {
Ok(sync_target) => sync_target,
Err(e) => return e.to_compile_error().into(),
};

quote! {
#sync_target
}
} else {
quote! {
Self
}
};

TokenStream::from(quote! {
impl #impl_generics #bevy_render_path::sync_component::SyncComponent for #struct_name #type_generics #where_clause {
type Out = Self;
type Target = #sync_target;
}

impl #impl_generics #bevy_render_path::extract_component::ExtractComponent for #struct_name #type_generics #where_clause {
type QueryData = &'static Self;

type QueryFilter = #filter;
type Out = Self;

fn extract_component(item: #bevy_ecs_path::query::QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
Some(item.clone())
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_render/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream {
/// See `ExtractComponentPlugin` to actually perform the extraction.
///
/// If you only want to extract a component conditionally, you may use the `extract_component_filter` attribute.
/// To specify `SyncComponent::Target`, you can use the `extract_component_sync_target` attribute.
///
/// # Example
///
Expand All @@ -41,6 +42,7 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream {
///
/// #[derive(Component, Clone, ExtractComponent)]
/// #[extract_component_filter(With<Camera>)]
/// #[extract_component_sync_target((Self, OtherNeedsCleanup))]
/// pub struct Foo {
/// pub should_foo: bool,
/// }
Expand All @@ -51,7 +53,10 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream {
/// pub should_bar: bool,
/// }
/// ```
#[proc_macro_derive(ExtractComponent, attributes(extract_component_filter))]
#[proc_macro_derive(
ExtractComponent,
attributes(extract_component_filter, extract_component_sync_target)
)]
pub fn derive_extract_component(input: TokenStream) -> TokenStream {
extract_component::derive_extract_component(input)
}
Expand Down
Loading