diff --git a/_release-content/migration-guides/extract-extract-b.md b/_release-content/migration-guides/extract-extract-b.md new file mode 100644 index 0000000000000..3542421912a61 --- /dev/null +++ b/_release-content/migration-guides/extract-extract-b.md @@ -0,0 +1,31 @@ +--- +title: Extract Extract +pull_requests: [24420] +--- + +Extraction used to be specific of Main World to Render World, but will now be generic + +- When using extraction related traits e.g. `SyncComponent`, `ExtractComponent` and `ExtractResource`, +you must specify the `AppLabel` for the target world. + +Before: + +```rust,ignore +impl SyncComponent for TemporalAntiAliasing { ... } + +#[derive(Component, ExtractComponent)] +#[extract_app(RenderApp)] +pub struct Foo { ... } +``` + +After: + +```rust,ignore +impl SyncComponent for TemporalAntiAliasing { ... } + +#[derive(Component, ExtractComponent)] +#[extract_app(RenderApp)] +pub struct Foo { ... } +``` + +NOTE: more to come diff --git a/crates/bevy_anti_alias/src/contrast_adaptive_sharpening/mod.rs b/crates/bevy_anti_alias/src/contrast_adaptive_sharpening/mod.rs index e04883623c0f6..409c3cbd32e6b 100644 --- a/crates/bevy_anti_alias/src/contrast_adaptive_sharpening/mod.rs +++ b/crates/bevy_anti_alias/src/contrast_adaptive_sharpening/mod.rs @@ -75,11 +75,11 @@ pub struct CasUniform { sharpness: f32, } -impl SyncComponent for ContrastAdaptiveSharpening { +impl SyncComponent for ContrastAdaptiveSharpening { type Target = (DenoiseCas, CasUniform); } -impl ExtractComponent for ContrastAdaptiveSharpening { +impl ExtractComponent for ContrastAdaptiveSharpening { type QueryData = &'static Self; type QueryFilter = With; type Out = (DenoiseCas, CasUniform); diff --git a/crates/bevy_anti_alias/src/fxaa/mod.rs b/crates/bevy_anti_alias/src/fxaa/mod.rs index aa44af488358c..b3f7187ea70e1 100644 --- a/crates/bevy_anti_alias/src/fxaa/mod.rs +++ b/crates/bevy_anti_alias/src/fxaa/mod.rs @@ -54,6 +54,7 @@ impl Sensitivity { #[reflect(Component, Default, Clone)] #[extract_component_filter(With)] #[doc(alias = "FastApproximateAntiAliasing")] +#[extract_app(RenderApp)] pub struct Fxaa { /// Enable render passes for FXAA. pub enabled: bool, diff --git a/crates/bevy_anti_alias/src/smaa/mod.rs b/crates/bevy_anti_alias/src/smaa/mod.rs index 9934d8971a08b..5d84c84a29547 100644 --- a/crates/bevy_anti_alias/src/smaa/mod.rs +++ b/crates/bevy_anti_alias/src/smaa/mod.rs @@ -89,6 +89,7 @@ pub struct SmaaPlugin; ViewSmaaPipelines, ))] #[doc(alias = "SubpixelMorphologicalAntiAliasing")] +#[extract_app(RenderApp)] pub struct Smaa { /// A predefined set of SMAA parameters: i.e. a quality level. /// diff --git a/crates/bevy_anti_alias/src/taa/mod.rs b/crates/bevy_anti_alias/src/taa/mod.rs index df035f381c55a..90431a4d416e2 100644 --- a/crates/bevy_anti_alias/src/taa/mod.rs +++ b/crates/bevy_anti_alias/src/taa/mod.rs @@ -129,7 +129,7 @@ impl Default for TemporalAntiAliasing { } } -impl SyncComponent for TemporalAntiAliasing { +impl SyncComponent for TemporalAntiAliasing { type Target = Self; } diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index 2d254666c676b..c7194135535ef 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -26,7 +26,8 @@ type ExtractFn = Box; /// # Example /// /// ``` -/// # use bevy_app::{App, AppLabel, SubApp, Main}; +/// # use bevy_app::{App, SubApp, Main}; +/// # use bevy_derive::AppLabel; /// # use bevy_ecs::prelude::*; /// # use bevy_ecs::schedule::ScheduleLabel; /// diff --git a/crates/bevy_core_pipeline/src/fullscreen_material.rs b/crates/bevy_core_pipeline/src/fullscreen_material.rs index cd2905daa3ed0..e7ea98a6dad02 100644 --- a/crates/bevy_core_pipeline/src/fullscreen_material.rs +++ b/crates/bevy_core_pipeline/src/fullscreen_material.rs @@ -75,7 +75,7 @@ impl Plugin for FullscreenMaterialPlugin { /// A trait to define a material that will render to the entire screen using a fullscreen triangle. pub trait FullscreenMaterial: - Component + ExtractComponent + Clone + Copy + ShaderType + WriteInto + Default + Component + ExtractComponent + Clone + Copy + ShaderType + WriteInto + Default { /// The shader that will run on the entire screen using a fullscreen triangle. fn fragment_shader() -> ShaderRef; diff --git a/crates/bevy_core_pipeline/src/oit/mod.rs b/crates/bevy_core_pipeline/src/oit/mod.rs index 993631783c2e5..6ea2c71e571cb 100644 --- a/crates/bevy_core_pipeline/src/oit/mod.rs +++ b/crates/bevy_core_pipeline/src/oit/mod.rs @@ -39,6 +39,7 @@ pub mod resolve; #[derive(Clone, Copy, ExtractComponent, Reflect, ShaderType, Component)] #[extract_component_sync_target((Self, OrderIndependentTransparencySettingsOffset, OitResolvePipelineId))] #[reflect(Clone, Default)] +#[extract_app(RenderApp)] pub struct OrderIndependentTransparencySettings { /// Controls how many fragments will be exactly sorted. /// If the scene has more fragments than this, they will be merged approximately. diff --git a/crates/bevy_core_pipeline/src/prepass/background_motion_vectors.rs b/crates/bevy_core_pipeline/src/prepass/background_motion_vectors.rs index 06f5566aee39f..3a76391f8bb98 100644 --- a/crates/bevy_core_pipeline/src/prepass/background_motion_vectors.rs +++ b/crates/bevy_core_pipeline/src/prepass/background_motion_vectors.rs @@ -56,11 +56,11 @@ use crate::{ #[reflect(Component, Default, Clone)] pub struct NoBackgroundMotionVectors; -impl SyncComponent for NoBackgroundMotionVectors { +impl SyncComponent for NoBackgroundMotionVectors { type Target = Self; } -impl ExtractComponent for NoBackgroundMotionVectors { +impl ExtractComponent for NoBackgroundMotionVectors { type QueryData = Read; type QueryFilter = (); type Out = Self; diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index 2b74db367892d..f29f375fbcfa1 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -59,7 +59,7 @@ impl Plugin for SkyboxPlugin { } } -impl SyncComponent for Skybox { +impl SyncComponent for Skybox { type Target = (Self, SkyboxUniforms, SkyboxPipelineId, SkyboxBindGroup); } diff --git a/crates/bevy_core_pipeline/src/tonemapping/mod.rs b/crates/bevy_core_pipeline/src/tonemapping/mod.rs index d1fa63b830162..9a38ca9165e4b 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/mod.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/mod.rs @@ -33,6 +33,7 @@ use crate::FullscreenShader; /// 3D LUT (look up table) textures used for tonemapping #[derive(Resource, Clone, ExtractResource)] +#[extract_app(RenderApp)] pub struct TonemappingLuts { pub blender_filmic: Handle, pub agx: Handle, @@ -116,6 +117,7 @@ pub struct TonemappingPipeline { )] #[extract_component_filter(With)] #[reflect(Component, Debug, Hash, Default, PartialEq)] +#[extract_app(RenderApp)] pub enum Tonemapping { /// Bypass tonemapping. None, @@ -380,6 +382,7 @@ pub fn prepare_view_tonemapping_pipelines( )] #[extract_component_filter(With)] #[reflect(Component, Debug, Hash, Default, PartialEq)] +#[extract_app(RenderApp)] pub enum DebandDither { #[default] Disabled, diff --git a/crates/bevy_dev_tools/src/render_debug.rs b/crates/bevy_dev_tools/src/render_debug.rs index 0f8d7c801d085..4bdf9b359bf5b 100644 --- a/crates/bevy_dev_tools/src/render_debug.rs +++ b/crates/bevy_dev_tools/src/render_debug.rs @@ -272,6 +272,7 @@ pub enum RenderDebugOverlayEvent { /// Overwrites the default [`GlobalRenderDebugOverlay`] resource. #[derive(Component, Clone, ExtractComponent, Reflect, PartialEq)] #[reflect(Component, Default)] +#[extract_app(RenderApp)] pub struct RenderDebugOverlay { /// Enables or disables drawing the overlay. pub enabled: bool, @@ -295,6 +296,7 @@ impl Default for RenderDebugOverlay { /// Can be overwritten by using a [`RenderDebugOverlay`] component. #[derive(Resource, Clone, ExtractResource, ExtractComponent, Reflect, PartialEq)] #[reflect(Resource, Default)] +#[extract_app(RenderApp)] pub struct GlobalRenderDebugOverlay { /// Enables or disables drawing the overlay. pub enabled: bool, diff --git a/crates/bevy_gizmos_render/src/lib.rs b/crates/bevy_gizmos_render/src/lib.rs index 0da17ebc25bd0..98d135d49d8bd 100755 --- a/crates/bevy_gizmos_render/src/lib.rs +++ b/crates/bevy_gizmos_render/src/lib.rs @@ -621,6 +621,7 @@ fn line_joint_gizmo_vertex_buffer_layouts() -> Vec { /// can be added and therefore three potential entities. #[derive(Clone, Reflect, Resource, ExtractResource)] #[reflect(Clone, Resource)] +#[extract_app(RenderApp)] pub struct LineGizmoEntities { /// An entity that regular line phase items are associated with. pub line_gizmo_renderer: MainEntity, diff --git a/crates/bevy_pbr/src/atmosphere/environment.rs b/crates/bevy_pbr/src/atmosphere/environment.rs index e19f39e15323b..0481bab3f975c 100644 --- a/crates/bevy_pbr/src/atmosphere/environment.rs +++ b/crates/bevy_pbr/src/atmosphere/environment.rs @@ -24,12 +24,14 @@ use bevy_render::{ renderer::{RenderContext, RenderDevice, ViewQuery}, texture::{CachedTexture, GpuImage}, view::{ViewUniform, ViewUniformOffset, ViewUniforms}, + RenderApp, }; use bevy_utils::default; use tracing::warn; // Render world representation of an environment map light for the atmosphere #[derive(Component, ExtractComponent, Clone, FromTemplate)] +#[extract_app(RenderApp)] pub struct AtmosphereEnvironmentMap { pub environment_map: Handle, pub size: UVec2, diff --git a/crates/bevy_pbr/src/atmosphere/mod.rs b/crates/bevy_pbr/src/atmosphere/mod.rs index 183c3e69132e5..5df61114f54ca 100644 --- a/crates/bevy_pbr/src/atmosphere/mod.rs +++ b/crates/bevy_pbr/src/atmosphere/mod.rs @@ -391,7 +391,7 @@ impl From for GpuAtmosphereSettings { } } -impl SyncComponent for AtmosphereSettings { +impl SyncComponent for AtmosphereSettings { type Target = GpuAtmosphereSettings; } diff --git a/crates/bevy_pbr/src/cluster/gpu.rs b/crates/bevy_pbr/src/cluster/gpu.rs index e1417312d9387..2cb5abc61dac5 100644 --- a/crates/bevy_pbr/src/cluster/gpu.rs +++ b/crates/bevy_pbr/src/cluster/gpu.rs @@ -1727,7 +1727,7 @@ pub(crate) fn prepare_clusters_for_gpu_clustering( .retain(|view_main_entity, _| all_view_main_entities.contains(view_main_entity)); } -impl ExtractResource for GlobalClusterSettings { +impl ExtractResource for GlobalClusterSettings { type Source = GlobalClusterSettings; fn extract_resource(source: &Self::Source) -> Self { diff --git a/crates/bevy_pbr/src/contact_shadows.rs b/crates/bevy_pbr/src/contact_shadows.rs index 711c59368dc8f..c55016c1f3597 100644 --- a/crates/bevy_pbr/src/contact_shadows.rs +++ b/crates/bevy_pbr/src/contact_shadows.rs @@ -79,11 +79,11 @@ impl From for ContactShadowsUniform { } } -impl SyncComponent for ContactShadows { +impl SyncComponent for ContactShadows { type Target = (Self, ViewContactShadowsUniformOffset); } -impl ExtractComponent for ContactShadows { +impl ExtractComponent for ContactShadows { type QueryData = &'static ContactShadows; type QueryFilter = (); type Out = Self; diff --git a/crates/bevy_pbr/src/decal/clustered.rs b/crates/bevy_pbr/src/decal/clustered.rs index 31f9f2e436f40..2e0a8719acbff 100644 --- a/crates/bevy_pbr/src/decal/clustered.rs +++ b/crates/bevy_pbr/src/decal/clustered.rs @@ -184,7 +184,7 @@ impl Plugin for ClusteredDecalPlugin { } } -impl SyncComponent for ClusteredDecal { +impl SyncComponent for ClusteredDecal { type Target = Self; } diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index b01258bb8937e..eb054b09658ef 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -40,6 +40,7 @@ pub const DEFAULT_PBR_DEFERRED_LIGHTING_PASS_ID: u8 = 1; /// /// Will be automatically added to entities with the [`DeferredPrepass`] component that don't already have a [`PbrDeferredLightingDepthId`]. #[derive(Component, Clone, Copy, ExtractComponent, ShaderType)] +#[extract_app(RenderApp)] pub struct PbrDeferredLightingDepthId { depth_id: u32, diff --git a/crates/bevy_pbr/src/fog.rs b/crates/bevy_pbr/src/fog.rs index 3c470b792e88e..a5c19d94fbbd4 100644 --- a/crates/bevy_pbr/src/fog.rs +++ b/crates/bevy_pbr/src/fog.rs @@ -3,7 +3,7 @@ use bevy_color::{Color, ColorToComponents, LinearRgba}; use bevy_ecs::prelude::*; use bevy_math::{ops, Vec3}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render::extract_component::ExtractComponent; +use bevy_render::{extract_component::ExtractComponent, RenderApp}; use crate::ViewFogUniformOffset; @@ -52,6 +52,7 @@ use crate::ViewFogUniformOffset; #[extract_component_filter(With)] #[extract_component_sync_target((Self, ViewFogUniformOffset))] #[reflect(Component, Default, Debug, Clone)] +#[extract_app(RenderApp)] pub struct DistanceFog { /// The color of the fog effect. /// diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index c3ee7793dd3bb..d795420c7847b 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -507,7 +507,7 @@ pub fn area_light_luts_placeholder() -> Image { } } -impl SyncComponent for DirectionalLight { +impl SyncComponent for DirectionalLight { type Target = ( Self, ExtractedDirectionalLight, @@ -516,7 +516,7 @@ impl SyncComponent for DirectionalLight { DirectionalLightViewEntities, ); } -impl SyncComponent for PointLight { +impl SyncComponent for PointLight { type Target = ( Self, ExtractedPointLight, @@ -525,7 +525,7 @@ impl SyncComponent for PointLight { PointAndSpotLightViewEntities, ); } -impl SyncComponent for SpotLight { +impl SyncComponent for SpotLight { type Target = ( Self, ExtractedPointLight, @@ -534,12 +534,12 @@ impl SyncComponent for SpotLight { PointAndSpotLightViewEntities, ); } -impl SyncComponent for RectLight { +impl SyncComponent for RectLight { type Target = (Self, ExtractedRectLight); } -impl SyncComponent for AmbientLight { +impl SyncComponent for AmbientLight { type Target = Self; } -impl SyncComponent for ShadowFilteringMethod { +impl SyncComponent for ShadowFilteringMethod { type Target = Self; } diff --git a/crates/bevy_pbr/src/light_probe/generate.rs b/crates/bevy_pbr/src/light_probe/generate.rs index fe4dd70ffdd59..545d5437a0ffb 100644 --- a/crates/bevy_pbr/src/light_probe/generate.rs +++ b/crates/bevy_pbr/src/light_probe/generate.rs @@ -1111,6 +1111,6 @@ pub fn generate_environment_map_light( } } -impl SyncComponent for GeneratedEnvironmentMapLight { +impl SyncComponent for GeneratedEnvironmentMapLight { type Target = RenderEnvironmentMap; } diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 967ccda522ace..d3703ab0d5ed5 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -1423,6 +1423,7 @@ pub fn queue_material_meshes( /// Default render method used for opaque materials. #[derive(Default, Resource, Clone, Debug, ExtractResource, Reflect)] #[reflect(Resource, Default, Debug, Clone)] +#[extract_app(RenderApp)] pub struct DefaultOpaqueRendererMethod(OpaqueRendererMethod); impl DefaultOpaqueRendererMethod { diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index a1767d6c5bec0..2e06d8a918c7d 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -113,6 +113,7 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { #[require(DepthPrepass, NormalPrepass)] #[extract_component_sync_target((Self, ScreenSpaceAmbientOcclusionResources, SsaoPipelineId, SsaoBindGroups))] #[doc(alias = "Ssao")] +#[extract_app(RenderApp)] pub struct ScreenSpaceAmbientOcclusion { /// Quality of the SSAO effect. pub quality_level: ScreenSpaceAmbientOcclusionQualityLevel, diff --git a/crates/bevy_pbr/src/ssr/mod.rs b/crates/bevy_pbr/src/ssr/mod.rs index 1de696857d29d..0b7053d64e551 100644 --- a/crates/bevy_pbr/src/ssr/mod.rs +++ b/crates/bevy_pbr/src/ssr/mod.rs @@ -445,7 +445,7 @@ pub fn prepare_ssr_settings( } } -impl SyncComponent for ScreenSpaceReflections { +impl SyncComponent for ScreenSpaceReflections { type Target = ( ScreenSpaceReflectionsUniform, ViewScreenSpaceReflectionsUniformOffset, @@ -453,7 +453,7 @@ impl SyncComponent for ScreenSpaceReflections { ); } -impl ExtractComponent for ScreenSpaceReflections { +impl ExtractComponent for ScreenSpaceReflections { type QueryData = Read; type QueryFilter = (); type Out = ScreenSpaceReflectionsUniform; diff --git a/crates/bevy_pbr/src/transmission/mod.rs b/crates/bevy_pbr/src/transmission/mod.rs index a7e3f056119e7..1463ae5da0cb6 100644 --- a/crates/bevy_pbr/src/transmission/mod.rs +++ b/crates/bevy_pbr/src/transmission/mod.rs @@ -64,6 +64,7 @@ impl Plugin for ScreenSpaceTransmissionPlugin { /// Configures transmission behavior, offering a trade-off between performance and visual fidelity. #[derive(Component, Reflect, Clone, ExtractComponent)] #[reflect(Component, Default, Clone)] +#[extract_app(RenderApp)] pub struct ScreenSpaceTransmission { /// How many individual steps should be performed in the `Transmissive3d` pass. /// diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index 661032dd2aab3..fc1f084c278ab 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -107,6 +107,6 @@ impl Plugin for VolumetricFogPlugin { } } -impl SyncComponent for FogVolume { +impl SyncComponent for FogVolume { type Target = Self; } diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index 48df9fa16bd39..002b5aa97d5af 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -882,6 +882,7 @@ pub enum WireframeTopology { #[derive(Resource, Debug, Clone, ExtractResource, Reflect)] #[reflect(Resource, Debug, Default)] +#[extract_app(RenderApp)] pub struct WireframeConfig { /// Whether to show wireframes for all meshes. /// Can be overridden for individual meshes by adding a [`Wireframe`] or [`NoWireframe`] component. diff --git a/crates/bevy_post_process/src/auto_exposure/settings.rs b/crates/bevy_post_process/src/auto_exposure/settings.rs index 95da8bec99304..6169031f6d1b0 100644 --- a/crates/bevy_post_process/src/auto_exposure/settings.rs +++ b/crates/bevy_post_process/src/auto_exposure/settings.rs @@ -6,7 +6,7 @@ use bevy_camera::Hdr; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; use bevy_image::Image; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render::extract_component::ExtractComponent; +use bevy_render::{extract_component::ExtractComponent, RenderApp}; use bevy_utils::default; /// Component that enables auto exposure for an HDR-enabled 2d or 3d camera. @@ -27,6 +27,7 @@ use bevy_utils::default; #[derive(Component, Clone, Reflect, ExtractComponent)] #[reflect(Component, Default, Clone)] #[require(Hdr)] +#[extract_app(RenderApp)] pub struct AutoExposure { /// The range of exposure values for the histogram. /// diff --git a/crates/bevy_post_process/src/bloom/settings.rs b/crates/bevy_post_process/src/bloom/settings.rs index fc6ada8d82e7e..08aad2401eea7 100644 --- a/crates/bevy_post_process/src/bloom/settings.rs +++ b/crates/bevy_post_process/src/bloom/settings.rs @@ -7,7 +7,7 @@ use bevy_ecs::{ }; use bevy_math::{AspectRatio, URect, UVec4, Vec2, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render::{extract_component::ExtractComponent, sync_component::SyncComponent}; +use bevy_render::{extract_component::ExtractComponent, sync_component::SyncComponent, RenderApp}; /// Applies a bloom effect to an HDR-enabled 2d or 3d camera. /// @@ -220,11 +220,11 @@ pub enum BloomCompositeMode { Additive, } -impl SyncComponent for Bloom { +impl SyncComponent for Bloom { type Target = (Self, BloomUniforms); } -impl ExtractComponent for Bloom { +impl ExtractComponent for Bloom { type QueryData = (&'static Self, &'static Camera); type QueryFilter = With; type Out = (Self, BloomUniforms); diff --git a/crates/bevy_post_process/src/dof/mod.rs b/crates/bevy_post_process/src/dof/mod.rs index aabd4d56d53e4..cdbc907e2c979 100644 --- a/crates/bevy_post_process/src/dof/mod.rs +++ b/crates/bevy_post_process/src/dof/mod.rs @@ -650,7 +650,7 @@ impl SpecializedRenderPipeline for DepthOfFieldPipeline { } } -impl SyncComponent for DepthOfField { +impl SyncComponent for DepthOfField { type Target = ( DepthOfField, DepthOfFieldUniform, @@ -679,7 +679,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::<::Target>(); + entity_commands.remove::<>::Target>(); continue; }; diff --git a/crates/bevy_post_process/src/effect_stack/chromatic_aberration.rs b/crates/bevy_post_process/src/effect_stack/chromatic_aberration.rs index c77115d20d3aa..8cbf06e254c1b 100644 --- a/crates/bevy_post_process/src/effect_stack/chromatic_aberration.rs +++ b/crates/bevy_post_process/src/effect_stack/chromatic_aberration.rs @@ -10,7 +10,8 @@ use bevy_ecs::{ use bevy_image::Image; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ - extract_component::ExtractComponent, render_resource::ShaderType, sync_component::SyncComponent, + extract_component::ExtractComponent, render_resource::ShaderType, + sync_component::SyncComponent, RenderApp, }; /// The raw RGBA data for the default chromatic aberration gradient. @@ -78,11 +79,11 @@ impl Default for ChromaticAberration { } } -impl SyncComponent for ChromaticAberration { +impl SyncComponent for ChromaticAberration { type Target = Self; } -impl ExtractComponent for ChromaticAberration { +impl ExtractComponent for ChromaticAberration { type QueryData = Read; type QueryFilter = With; type Out = Self; diff --git a/crates/bevy_post_process/src/effect_stack/lens_distortion.rs b/crates/bevy_post_process/src/effect_stack/lens_distortion.rs index e676da36b5c3e..93aadb5dd252b 100644 --- a/crates/bevy_post_process/src/effect_stack/lens_distortion.rs +++ b/crates/bevy_post_process/src/effect_stack/lens_distortion.rs @@ -6,7 +6,8 @@ use bevy_ecs::{ use bevy_math::{ops::abs, Vec2}; use bevy_reflect::Reflect; use bevy_render::{ - extract_component::ExtractComponent, render_resource::ShaderType, sync_component::SyncComponent, + extract_component::ExtractComponent, render_resource::ShaderType, + sync_component::SyncComponent, RenderApp, }; /// Simulates the warping of the image caused by real-world camera lenses. @@ -78,11 +79,11 @@ impl Default for LensDistortion { } } -impl SyncComponent for LensDistortion { +impl SyncComponent for LensDistortion { type Target = Self; } -impl ExtractComponent for LensDistortion { +impl ExtractComponent for LensDistortion { type QueryData = Read; type QueryFilter = With; type Out = Self; diff --git a/crates/bevy_post_process/src/effect_stack/vignette.rs b/crates/bevy_post_process/src/effect_stack/vignette.rs index 1ebc913a30741..a83cd21ab1de0 100644 --- a/crates/bevy_post_process/src/effect_stack/vignette.rs +++ b/crates/bevy_post_process/src/effect_stack/vignette.rs @@ -9,7 +9,8 @@ use bevy_ecs::{ use bevy_math::{Vec2, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{ - extract_component::ExtractComponent, render_resource::ShaderType, sync_component::SyncComponent, + extract_component::ExtractComponent, render_resource::ShaderType, + sync_component::SyncComponent, RenderApp, }; /// Adds a gradual shading effect to the edges of the screen, drawing focus @@ -87,11 +88,11 @@ impl Default for Vignette { } } -impl SyncComponent for Vignette { +impl SyncComponent for Vignette { type Target = Self; } -impl ExtractComponent for Vignette { +impl ExtractComponent for Vignette { type QueryData = Read; type QueryFilter = With; type Out = Self; diff --git a/crates/bevy_post_process/src/motion_blur/mod.rs b/crates/bevy_post_process/src/motion_blur/mod.rs index a5c385df37ff1..ebe6856208668 100644 --- a/crates/bevy_post_process/src/motion_blur/mod.rs +++ b/crates/bevy_post_process/src/motion_blur/mod.rs @@ -115,11 +115,11 @@ impl Default for MotionBlur { } } -impl SyncComponent for MotionBlur { +impl SyncComponent for MotionBlur { type Target = MotionBlurUniform; } -impl ExtractComponent for MotionBlur { +impl ExtractComponent for MotionBlur { type QueryData = &'static Self; type QueryFilter = With; type Out = MotionBlurUniform; diff --git a/crates/bevy_render/macros/src/extract_component.rs b/crates/bevy_render/macros/src/extract_component.rs index 0a672b0d7ef54..9577f8c954d5b 100644 --- a/crates/bevy_render/macros/src/extract_component.rs +++ b/crates/bevy_render/macros/src/extract_component.rs @@ -20,6 +20,21 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let app_label = match ast.attrs.iter().find(|a| a.path().is_ident("extract_app")) { + Some(attr) => match attr.parse_args::() { + Ok(label) => label, + Err(e) => return e.to_compile_error().into(), + }, + None => { + return syn::Error::new_spanned( + &ast.ident, + "ExtractComponent requires #[extract_app(MyAppLabel)] to specify the target sub-app", + ) + .to_compile_error() + .into(); + } + }; + let filter = if let Some(attr) = ast .attrs .iter() @@ -59,12 +74,13 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream { }; TokenStream::from(quote! { - impl #impl_generics #bevy_render_path::sync_component::SyncComponent for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_render_path::sync_component::SyncComponent<#app_label> for #struct_name #type_generics #where_clause { type Target = #sync_target; } - impl #impl_generics #bevy_render_path::extract_component::ExtractComponent for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_render_path::extract_component::ExtractComponent<#app_label> for #struct_name #type_generics #where_clause { type QueryData = &'static Self; + type QueryFilter = #filter; type Out = Self; diff --git a/crates/bevy_render/macros/src/extract_resource.rs b/crates/bevy_render/macros/src/extract_resource.rs index 0c65fa3fc5d12..2f05734ad7f3d 100644 --- a/crates/bevy_render/macros/src/extract_resource.rs +++ b/crates/bevy_render/macros/src/extract_resource.rs @@ -15,8 +15,23 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + let app_label = match ast.attrs.iter().find(|a| a.path().is_ident("extract_app")) { + Some(attr) => match attr.parse_args::() { + Ok(label) => label, + Err(e) => return e.to_compile_error().into(), + }, + None => { + return syn::Error::new_spanned( + &ast.ident, + "ExtractResource requires #[extract_app(MyAppLabel)] to specify the target sub-app", + ) + .to_compile_error() + .into(); + } + }; + TokenStream::from(quote! { - impl #impl_generics #bevy_render_path::extract_resource::ExtractResource for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_render_path::extract_resource::ExtractResource<#app_label> for #struct_name #type_generics #where_clause { type Source = Self; fn extract_resource(source: &Self::Source) -> Self { diff --git a/crates/bevy_render/macros/src/lib.rs b/crates/bevy_render/macros/src/lib.rs index 15115b2157987..aa858cc5b1280 100644 --- a/crates/bevy_render/macros/src/lib.rs +++ b/crates/bevy_render/macros/src/lib.rs @@ -19,7 +19,7 @@ pub(crate) fn bevy_ecs_path() -> syn::Path { BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs")) } -#[proc_macro_derive(ExtractResource)] +#[proc_macro_derive(ExtractResource, attributes(extract_app))] pub fn derive_extract_resource(input: TokenStream) -> TokenStream { extract_resource::derive_extract_resource(input) } @@ -55,7 +55,7 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream { /// ``` #[proc_macro_derive( ExtractComponent, - attributes(extract_component_filter, extract_component_sync_target) + attributes(extract_component_filter, extract_component_sync_target, extract_app) )] pub fn derive_extract_component(input: TokenStream) -> TokenStream { extract_component::derive_extract_component(input) diff --git a/crates/bevy_render/src/camera.rs b/crates/bevy_render/src/camera.rs index 29d15a00b0b97..572ecfaecffe1 100644 --- a/crates/bevy_render/src/camera.rs +++ b/crates/bevy_render/src/camera.rs @@ -122,7 +122,7 @@ fn warn_on_no_render_graph(world: DeferredWorld, HookContext { entity, caller, . } } -impl ExtractResource for ClearColor { +impl ExtractResource for ClearColor { type Source = Self; fn extract_resource(source: &Self::Source) -> Self { @@ -130,11 +130,11 @@ impl ExtractResource for ClearColor { } } -impl SyncComponent for CameraMainTextureUsages { +impl SyncComponent for CameraMainTextureUsages { type Target = Self; } -impl ExtractComponent for CameraMainTextureUsages { +impl ExtractComponent for CameraMainTextureUsages { type QueryData = &'static Self; type QueryFilter = (); type Out = Self; @@ -144,11 +144,11 @@ impl ExtractComponent for CameraMainTextureUsages { } } -impl SyncComponent for Camera2d { +impl SyncComponent for Camera2d { type Target = Self; } -impl ExtractComponent for Camera2d { +impl ExtractComponent for Camera2d { type QueryData = &'static Self; type QueryFilter = With; type Out = Self; @@ -158,11 +158,11 @@ impl ExtractComponent for Camera2d { } } -impl SyncComponent for Camera3d { +impl SyncComponent for Camera3d { type Target = Self; } -impl ExtractComponent for Camera3d { +impl ExtractComponent for Camera3d { type QueryData = &'static Self; type QueryFilter = With; type Out = Self; diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 4d32b3f369a13..1603f91219b38 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -3,7 +3,7 @@ use crate::{ sync_world::RenderEntity, Extract, ExtractSchedule, RenderApp, }; -use bevy_app::{App, Plugin}; +use bevy_app::{App, AppLabel, Plugin}; use bevy_camera::visibility::ViewVisibility; use bevy_ecs::{ bundle::NoBundleEffect, @@ -27,7 +27,7 @@ pub use bevy_render_macros::ExtractComponent; /// 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. @@ -80,7 +80,9 @@ impl ExtractComponentPlugin { } } -impl, F: '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()); @@ -95,7 +97,7 @@ impl, F: 'static + Send + Sync> Plugin for ExtractCompone } /// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`]. -fn extract_components, F>( +fn extract_components, F>( mut commands: Commands, mut previous_len: Local, query: Extract>, @@ -113,7 +115,7 @@ fn extract_components, F>( } /// 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, F>( +fn extract_visible_components, F>( mut commands: Commands, mut previous_len: Local, query: Extract>, diff --git a/crates/bevy_render/src/extract_plugin.rs b/crates/bevy_render/src/extract_plugin.rs index 752a2cdafd4a0..a5c478f81f5a9 100644 --- a/crates/bevy_render/src/extract_plugin.rs +++ b/crates/bevy_render/src/extract_plugin.rs @@ -145,16 +145,17 @@ mod test { struct RenderComponentExtra; #[derive(Component, Clone, Debug, ExtractComponent)] + #[extract_app(RenderApp)] struct RenderComponentSeparate; #[derive(Component, Clone, Debug)] struct RenderComponentNoExtract; - impl SyncComponent for RenderComponent { + impl SyncComponent for RenderComponent { type Target = (RenderComponent, RenderComponentExtra); } - impl ExtractComponent for RenderComponent { + impl ExtractComponent for RenderComponent { type QueryData = &'static Self; type QueryFilter = (); type Out = (RenderComponent, RenderComponentExtra); diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs index 619a25cb60321..ca7556587c494 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_render/src/extract_resource.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use bevy_app::{App, Plugin}; +use bevy_app::{App, AppLabel, Plugin}; use bevy_ecs::{component::Mutable, prelude::*}; pub use bevy_render_macros::ExtractResource; use bevy_utils::once; @@ -15,7 +15,7 @@ use crate::{Extract, ExtractSchedule, RenderApp}; /// 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 { +pub trait ExtractResource: Resource { type Source: Resource; /// Defines how the resource is transferred into the "render world". @@ -30,15 +30,15 @@ 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 struct ExtractResourcePlugin, F = ()>(PhantomData<(R, F)>); +pub struct ExtractResourcePlugin, F = ()>(PhantomData<(R, F)>); -impl, F> Default for ExtractResourcePlugin { +impl, F> Default for ExtractResourcePlugin { fn default() -> Self { Self(PhantomData) } } -impl, F: 'static + Send + Sync> Plugin +impl, F: 'static + Send + Sync> Plugin for ExtractResourcePlugin { fn build(&self, app: &mut App) { @@ -54,7 +54,7 @@ impl, F: 'static + Send + Sync> Plug } /// This system extracts the resource of the corresponding [`Resource`] type -pub fn extract_resource, F>( +pub fn extract_resource, F>( mut commands: Commands, main_resource: Extract>>, target_resource: Option>, diff --git a/crates/bevy_render/src/globals.rs b/crates/bevy_render/src/globals.rs index 436dcdcfe831a..0d565bf32a033 100644 --- a/crates/bevy_render/src/globals.rs +++ b/crates/bevy_render/src/globals.rs @@ -41,6 +41,7 @@ fn extract_time(mut commands: Commands, time: Extract>) { /// Currently only contains values related to time. #[derive(Default, Clone, Resource, ExtractResource, Reflect, ShaderType)] #[reflect(Resource, Default, Clone)] +#[extract_app(RenderApp)] pub struct GlobalsUniform { /// The time since startup in seconds. /// Wraps to 0 after 1 hour. diff --git a/crates/bevy_render/src/gpu_readback.rs b/crates/bevy_render/src/gpu_readback.rs index 1b1cddb24d6f0..b3d10d3cfd796 100644 --- a/crates/bevy_render/src/gpu_readback.rs +++ b/crates/bevy_render/src/gpu_readback.rs @@ -81,6 +81,7 @@ impl Plugin for GpuReadbackPlugin { /// Data is read asynchronously and will be triggered on the entity via the [`ReadbackComplete`] event /// when complete. If this component is not removed, the readback will be attempted every frame #[derive(Component, ExtractComponent, Clone, Debug, FromTemplate)] +#[extract_app(RenderApp)] pub enum Readback { #[default] Texture(Handle), diff --git a/crates/bevy_render/src/occlusion_culling/mod.rs b/crates/bevy_render/src/occlusion_culling/mod.rs index 531a93930e78b..5803750bccab4 100644 --- a/crates/bevy_render/src/occlusion_culling/mod.rs +++ b/crates/bevy_render/src/occlusion_culling/mod.rs @@ -8,7 +8,7 @@ use bevy_ecs::{component::Component, entity::Entity, prelude::ReflectComponent}; use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_shader::load_shader_library; -use crate::{extract_component::ExtractComponent, render_resource::TextureView}; +use crate::{extract_component::ExtractComponent, render_resource::TextureView, RenderApp}; /// Enables GPU occlusion culling. /// @@ -69,6 +69,7 @@ impl Plugin for OcclusionCullingPlugin { /// https://medium.com/@mil_kru/two-pass-occlusion-culling-4100edcad501 #[derive(Component, ExtractComponent, Clone, Copy, Default, Reflect)] #[reflect(Component, Default, Clone)] +#[extract_app(RenderApp)] pub struct OcclusionCulling; /// A render-world component that contains resources necessary to perform diff --git a/crates/bevy_render/src/sync_component.rs b/crates/bevy_render/src/sync_component.rs index e2c85d470fbfb..5ec00b2ffc896 100644 --- a/crates/bevy_render/src/sync_component.rs +++ b/crates/bevy_render/src/sync_component.rs @@ -1,12 +1,15 @@ use core::marker::PhantomData; -use bevy_app::{App, Plugin}; +use bevy_app::{App, AppLabel, Plugin}; use bevy_ecs::{ bundle::{Bundle, NoBundleEffect}, component::Component, }; -use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}; +use crate::{ + sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}, + RenderApp, +}; /// Plugin that registers a component for automatic sync to the render world. See [`SyncWorldPlugin`] for more information. /// @@ -25,7 +28,7 @@ use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToRenderWorld}; /// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin pub struct SyncComponentPlugin(PhantomData<(C, F)>); -impl, F> Default for SyncComponentPlugin { +impl, F> Default for SyncComponentPlugin { fn default() -> Self { Self(PhantomData) } @@ -37,7 +40,7 @@ impl, F> Default for SyncComponentPlugin { /// 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`]. -pub trait SyncComponent: Component { +pub trait SyncComponent: Component { /// Describes what components should be removed from the render world if the /// implementing component is removed. type Target: Bundle; @@ -45,7 +48,9 @@ pub trait SyncComponent: Component { // type Target: Bundle = Self; } -impl, F: Send + Sync + 'static> Plugin for SyncComponentPlugin { +impl, F: Send + Sync + 'static> Plugin + for SyncComponentPlugin +{ fn build(&self, app: &mut App) { app.register_required_components::(); diff --git a/crates/bevy_render/src/texture/manual_texture_view.rs b/crates/bevy_render/src/texture/manual_texture_view.rs index 2202071a51168..17d7d0bcdb6fd 100644 --- a/crates/bevy_render/src/texture/manual_texture_view.rs +++ b/crates/bevy_render/src/texture/manual_texture_view.rs @@ -5,7 +5,7 @@ use bevy_platform::collections::HashMap; use bevy_render_macros::ExtractResource; use wgpu::TextureFormat; -use crate::render_resource::TextureView; +use crate::{render_resource::TextureView, RenderApp}; /// A manually managed [`TextureView`] for use as a [`bevy_camera::RenderTarget`]. #[derive(Debug, Clone)] @@ -50,6 +50,7 @@ impl ManualTextureView { /// ``` /// Bevy will then use the `ManualTextureViews` resource to find your texture view and render to it. #[derive(Default, Clone, Resource, ExtractResource)] +#[extract_app(RenderApp)] pub struct ManualTextureViews(HashMap); impl core::ops::Deref for ManualTextureViews { diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index ddabb60e7d408..c986754dec584 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -237,6 +237,7 @@ impl Plugin for ViewPlugin { Debug, )] #[reflect(Component, Default, PartialEq, Hash, Debug)] +#[extract_app(RenderApp)] pub enum Msaa { Off = 1, Sample2 = 2, diff --git a/crates/bevy_solari/src/scene/extract.rs b/crates/bevy_solari/src/scene/extract.rs index deb59e804317a..bf0b629a76bd4 100644 --- a/crates/bevy_solari/src/scene/extract.rs +++ b/crates/bevy_solari/src/scene/extract.rs @@ -7,7 +7,9 @@ use bevy_ecs::{ }; use bevy_pbr::{MeshMaterial3d, PreviousGlobalTransform, StandardMaterial}; use bevy_platform::collections::HashMap; -use bevy_render::{extract_resource::ExtractResource, sync_world::RenderEntity, Extract}; +use bevy_render::{ + extract_resource::ExtractResource, sync_world::RenderEntity, Extract, RenderApp, +}; use bevy_transform::components::GlobalTransform; pub fn extract_raytracing_scene( @@ -40,7 +42,7 @@ pub fn extract_raytracing_scene( #[derive(Resource, Deref, Default)] pub struct StandardMaterialAssets(HashMap, StandardMaterial>); -impl ExtractResource for StandardMaterialAssets { +impl ExtractResource for StandardMaterialAssets { type Source = Assets; fn extract_resource(source: &Self::Source) -> Self { diff --git a/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs b/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs index ec844c40e4ae5..b6b39ed9b9afb 100644 --- a/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs +++ b/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs @@ -421,6 +421,7 @@ pub struct NoWireframe2d; #[derive(Resource, Debug, Clone, Default, ExtractResource, Reflect)] #[reflect(Resource, Debug, Default)] +#[extract_app(RenderApp)] pub struct Wireframe2dConfig { /// Whether to show wireframes for all meshes. /// Can be overridden for individual meshes by adding a [`Wireframe2d`] or [`NoWireframe2d`] component. diff --git a/crates/bevy_ui_render/src/ui_material.rs b/crates/bevy_ui_render/src/ui_material.rs index e716a600c8c02..7fa6d8b8b7a9c 100644 --- a/crates/bevy_ui_render/src/ui_material.rs +++ b/crates/bevy_ui_render/src/ui_material.rs @@ -6,6 +6,7 @@ use bevy_reflect::{prelude::ReflectDefault, Reflect}; use bevy_render::{ extract_component::ExtractComponent, render_resource::{AsBindGroup, RenderPipelineDescriptor, TextureFormat}, + RenderApp, }; use bevy_shader::ShaderRef; use derive_more::derive::From; @@ -177,6 +178,7 @@ where )] #[reflect(Component, Default)] #[require(Node)] +#[extract_app(RenderApp)] pub struct MaterialNode(pub Handle); impl Default for MaterialNode { diff --git a/examples/2d/mesh2d_manual.rs b/examples/2d/mesh2d_manual.rs index 571a08abdf88e..cdc9d9f8fd610 100644 --- a/examples/2d/mesh2d_manual.rs +++ b/examples/2d/mesh2d_manual.rs @@ -125,7 +125,7 @@ fn star( #[derive(Component, Default)] pub struct ColoredMesh2d; -impl SyncComponent for ColoredMesh2d { +impl SyncComponent for ColoredMesh2d { type Target = Self; } diff --git a/examples/app/render_recovery.rs b/examples/app/render_recovery.rs index 25ad2b6dfeac1..ed63e4a469617 100644 --- a/examples/app/render_recovery.rs +++ b/examples/app/render_recovery.rs @@ -94,6 +94,7 @@ fn update_camera(mut camera: Query<&mut Transform, With>, time: Res