diff --git a/crates/bevy_core_pipeline/src/bloom/mod.rs b/crates/bevy_core_pipeline/src/bloom/mod.rs index ff642c1f2f9af..a82d71603e25e 100644 --- a/crates/bevy_core_pipeline/src/bloom/mod.rs +++ b/crates/bevy_core_pipeline/src/bloom/mod.rs @@ -8,6 +8,7 @@ pub use settings::{ Bloom, BloomCompositeMode, BloomPrefilter, BloomPrefilterSettings, BloomSettings, }; +use crate::bloom::settings::UseBloom; use crate::{ core_2d::graph::{Core2d, Node2d}, core_3d::graph::{Core3d, Node3d}, @@ -16,6 +17,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_math::{ops, UVec2}; +use bevy_render::render_component::RenderComponentPlugin; use bevy_render::{ camera::ExtractedCamera, diagnostic::RecordDiagnostics, @@ -53,6 +55,7 @@ impl Plugin for BloomPlugin { app.add_plugins(( ExtractComponentPlugin::::default(), UniformComponentPlugin::::default(), + RenderComponentPlugin::::default(), )); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { @@ -107,6 +110,7 @@ impl ViewNode for BloomNode { &'static UpsamplingPipelineIds, &'static BloomDownsamplingPipelineIds, ); + type ViewFilter = With; // Atypically for a post-processing effect, we do not need to // use a secondary texture normally provided by view_target.post_process_write(), @@ -327,7 +331,7 @@ fn prepare_bloom_textures( mut commands: Commands, mut texture_cache: ResMut, render_device: Res, - views: Query<(Entity, &ExtractedCamera, &Bloom)>, + views: Query<(Entity, &ExtractedCamera, &Bloom), With>, ) { for (entity, camera, bloom) in &views { if let Some(UVec2 { diff --git a/crates/bevy_core_pipeline/src/bloom/settings.rs b/crates/bevy_core_pipeline/src/bloom/settings.rs index effa135677f3b..ae74e473e5832 100644 --- a/crates/bevy_core_pipeline/src/bloom/settings.rs +++ b/crates/bevy_core_pipeline/src/bloom/settings.rs @@ -2,6 +2,7 @@ use super::downsampling_pipeline::BloomUniforms; use bevy_ecs::{prelude::Component, query::QueryItem, reflect::ReflectComponent}; use bevy_math::{AspectRatio, URect, UVec4, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::render_component::RenderComponent; use bevy_render::{extract_component::ExtractComponent, prelude::Camera}; /// Applies a bloom effect to an HDR-enabled 2d or 3d camera. @@ -179,6 +180,9 @@ impl Default for Bloom { } } +#[derive(Component, RenderComponent)] +pub struct UseBloom; + /// Applies a threshold filter to the input image to extract the brightest /// regions before blurring them and compositing back onto the original image. /// These settings are useful when emulating the 1990s-2000s game look. @@ -216,7 +220,7 @@ impl ExtractComponent for Bloom { type QueryData = (&'static Self, &'static Camera); type QueryFilter = (); - type Out = (Self, BloomUniforms); + type Out = (Self, BloomUniforms, UseBloom); fn extract_component((bloom, camera): QueryItem<'_, Self::QueryData>) -> Option { match ( @@ -249,7 +253,7 @@ impl ExtractComponent for Bloom { uv_offset: bloom.uv_offset, }; - Some((bloom.clone(), uniform)) + Some((bloom.clone(), uniform, UseBloom)) } _ => None, } diff --git a/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs b/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs index 71c9cc2bb2a61..cf1ffa0d66023 100644 --- a/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs +++ b/crates/bevy_core_pipeline/src/contrast_adaptive_sharpening/mod.rs @@ -7,6 +7,7 @@ use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, Handle}; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::render_component::{RenderComponent, RenderComponentPlugin}; use bevy_render::{ extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin}, prelude::Camera, @@ -67,6 +68,9 @@ impl Default for ContrastAdaptiveSharpening { } } +#[derive(Component, RenderComponent)] +pub struct UseContrastAdaptiveSharpening; + #[derive(Component, Default, Reflect, Clone)] #[reflect(Component, Default)] pub struct DenoiseCas(bool); @@ -82,7 +86,7 @@ pub struct CasUniform { impl ExtractComponent for ContrastAdaptiveSharpening { type QueryData = &'static Self; type QueryFilter = With; - type Out = (DenoiseCas, CasUniform); + type Out = (DenoiseCas, CasUniform, UseContrastAdaptiveSharpening); fn extract_component(item: QueryItem) -> Option { if !item.enabled || item.sharpening_strength == 0.0 { @@ -94,6 +98,7 @@ impl ExtractComponent for ContrastAdaptiveSharpening { // above 1.0 causes extreme artifacts and fireflies sharpness: item.sharpening_strength.clamp(0.0, 1.0), }, + UseContrastAdaptiveSharpening, )) } } @@ -117,6 +122,7 @@ impl Plugin for CasPlugin { app.add_plugins(( ExtractComponentPlugin::::default(), UniformComponentPlugin::::default(), + RenderComponentPlugin::::default(), )); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { diff --git a/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs index 91093d0da5c94..6a574643ad891 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_opaque_pass_2d_node.rs @@ -25,6 +25,7 @@ impl ViewNode for MainOpaquePass2dNode { &'static ViewTarget, &'static ViewDepthTexture, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs index e365be954775b..8981e14214f65 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs @@ -22,6 +22,7 @@ impl ViewNode for MainTransparentPass2dNode { &'static ViewTarget, &'static ViewDepthTexture, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_core_pipeline/src/core_2d/mod.rs b/crates/bevy_core_pipeline/src/core_2d/mod.rs index c9883ce441cb6..7a129135c10b9 100644 --- a/crates/bevy_core_pipeline/src/core_2d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_2d/mod.rs @@ -38,10 +38,12 @@ pub use camera_2d::*; pub use main_opaque_pass_2d_node::*; pub use main_transparent_pass_2d_node::*; +use self::graph::{Core2d, Node2d}; use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode}; use bevy_app::{App, Plugin}; use bevy_ecs::{entity::EntityHashSet, prelude::*}; use bevy_math::FloatOrd; +use bevy_render::camera::CameraActive; use bevy_render::sync_world::MainEntity; use bevy_render::{ camera::{Camera, ExtractedCamera}, @@ -63,8 +65,6 @@ use bevy_render::{ Extract, ExtractSchedule, Render, RenderApp, RenderSet, }; -use self::graph::{Core2d, Node2d}; - pub const CORE_2D_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth32Float; pub struct Core2dPlugin; @@ -403,7 +403,7 @@ pub fn prepare_core_2d_depth_textures( render_device: Res, transparent_2d_phases: Res>, opaque_2d_phases: Res>, - views_2d: Query<(Entity, &ExtractedCamera, &Msaa), (With,)>, + views_2d: Query<(Entity, &ExtractedCamera, &Msaa), (With, With)>, ) { let mut textures = HashMap::default(); for (view, camera, msaa) in &views_2d { diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index b51f36354340a..5d8f43fbdf140 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -32,6 +32,7 @@ impl ViewNode for MainOpaquePass3dNode { Option<&'static SkyboxBindGroup>, &'static ViewUniformOffset, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs index 225ce81da6c3a..abfb6bc5b754f 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs @@ -27,6 +27,7 @@ impl ViewNode for MainTransmissivePass3dNode { Option<&'static ViewTransmissionTexture>, &'static ViewDepthTexture, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index 4f0d3d0722f0e..e6525da58b909 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -24,6 +24,8 @@ impl ViewNode for MainTransparentPass3dNode { &'static ViewTarget, &'static ViewDepthTexture, ); + type ViewFilter = (); + fn run( &self, graph: &mut RenderGraphContext, diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index ae88905bacd49..4af0f1fcf7b44 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -74,6 +74,7 @@ pub use main_transparent_pass_3d_node::*; use bevy_app::{App, Plugin, PostUpdate}; use bevy_ecs::{entity::EntityHashSet, prelude::*}; use bevy_math::FloatOrd; +use bevy_render::camera::CameraActive; use bevy_render::sync_world::MainEntity; use bevy_render::{ camera::{Camera, ExtractedCamera}, @@ -715,7 +716,7 @@ pub fn prepare_core_3d_transmission_textures( alpha_mask_3d_phases: Res>, transmissive_3d_phases: Res>, transparent_3d_phases: Res>, - views_3d: Query<(Entity, &ExtractedCamera, &Camera3d, &ExtractedView)>, + views_3d: Query<(Entity, &ExtractedCamera, &Camera3d, &ExtractedView), With>, ) { let mut textures = HashMap::default(); for (entity, camera, camera_3d, view) in &views_3d { diff --git a/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs b/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs index 4f5462d1f52ba..63aa7978385e9 100644 --- a/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs +++ b/crates/bevy_core_pipeline/src/deferred/copy_lighting_id.rs @@ -15,14 +15,14 @@ use bevy_render::{ Render, RenderApp, RenderSet, }; +use super::DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT; use bevy_ecs::query::QueryItem; +use bevy_render::camera::CameraActive; use bevy_render::{ render_graph::{NodeRunError, RenderGraphContext, ViewNode}, renderer::RenderContext, }; -use super::DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT; - pub const COPY_DEFERRED_LIGHTING_ID_SHADER_HANDLE: Handle = Handle::weak_from_u128(5230948520734987); pub struct CopyDeferredLightingIdPlugin; @@ -65,6 +65,7 @@ impl ViewNode for CopyDeferredLightingIdNode { &'static ViewPrepassTextures, &'static DeferredLightingIdDepthTexture, ); + type ViewFilter = (); fn run( &self, @@ -178,7 +179,7 @@ fn prepare_deferred_lighting_id_textures( mut commands: Commands, mut texture_cache: ResMut, render_device: Res, - views: Query<(Entity, &ExtractedCamera), With>, + views: Query<(Entity, &ExtractedCamera), (With, With)>, ) { for (entity, camera) in &views { if let Some(UVec2 { diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs index 5aa89a8e94a0d..1e9774b2339ec 100644 --- a/crates/bevy_core_pipeline/src/deferred/node.rs +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -30,6 +30,7 @@ impl ViewNode for DeferredGBufferPrepassNode { &'static ViewDepthTexture, &'static ViewPrepassTextures, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_core_pipeline/src/dof/mod.rs b/crates/bevy_core_pipeline/src/dof/mod.rs index ccf54dd4d5055..0e2997d672968 100644 --- a/crates/bevy_core_pipeline/src/dof/mod.rs +++ b/crates/bevy_core_pipeline/src/dof/mod.rs @@ -14,6 +14,13 @@ //! //! [Depth of field]: https://en.wikipedia.org/wiki/Depth_of_field +use crate::{ + core_3d::{ + graph::{Core3d, Node3d}, + Camera3d, DEPTH_TEXTURE_SAMPLING_SUPPORTED, + }, + fullscreen_vertex_shader::fullscreen_shader_vertex_state, +}; use bevy_app::{App, Plugin}; use bevy_asset::{load_internal_asset, Handle}; use bevy_derive::{Deref, DerefMut}; @@ -28,6 +35,8 @@ use bevy_ecs::{ }; use bevy_math::ops; use bevy_reflect::{prelude::ReflectDefault, Reflect}; +use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; +use bevy_render::render_component::{RenderComponent, RenderComponentPlugin}; use bevy_render::{ camera::{PhysicalCameraParameters, Projection}, extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, @@ -46,7 +55,6 @@ use bevy_render::{ TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages, }, renderer::{RenderContext, RenderDevice}, - sync_component::SyncComponentPlugin, sync_world::RenderEntity, texture::{BevyDefault, CachedTexture, TextureCache}, view::{ @@ -58,14 +66,6 @@ use bevy_render::{ use bevy_utils::{info_once, prelude::default, warn_once}; use smallvec::SmallVec; -use crate::{ - core_3d::{ - graph::{Core3d, Node3d}, - Camera3d, DEPTH_TEXTURE_SAMPLING_SUPPORTED, - }, - fullscreen_vertex_shader::fullscreen_shader_vertex_state, -}; - const DOF_SHADER_HANDLE: Handle = Handle::weak_from_u128(2031861180739216043); /// A plugin that adds support for the depth of field effect to Bevy. @@ -75,7 +75,7 @@ pub struct DepthOfFieldPlugin; /// simulating the focus of a camera lens. /// /// [depth of field]: https://en.wikipedia.org/wiki/Depth_of_field -#[derive(Component, Clone, Copy, Reflect)] +#[derive(Component, ExtractComponent, Clone, Copy, Reflect)] #[reflect(Component, Default)] pub struct DepthOfField { /// The appearance of the effect. @@ -118,6 +118,9 @@ pub struct DepthOfField { pub max_depth: f32, } +#[derive(Component, RenderComponent)] +pub struct UseDepthOfField; + #[deprecated(since = "0.15.0", note = "Renamed to `DepthOfField`")] pub type DepthOfFieldSettings = DepthOfField; @@ -210,9 +213,12 @@ impl Plugin for DepthOfFieldPlugin { app.register_type::(); app.register_type::(); - app.add_plugins(UniformComponentPlugin::::default()); + app.add_plugins(( + UniformComponentPlugin::::default(), + RenderComponentPlugin::::default(), + )); - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(ExtractComponentPlugin::::default()); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; @@ -339,6 +345,7 @@ impl ViewNode for DepthOfFieldNode { Read>, Option>, ); + type ViewFilter = With; fn run<'w>( &self, @@ -596,7 +603,7 @@ pub fn prepare_depth_of_field_view_bind_group_layouts( /// need to set the appropriate flag to tell Bevy to make samplable depth /// buffers. pub fn configure_depth_of_field_view_targets( - mut view_targets: Query<&mut Camera3d, With>, + mut view_targets: Query<&mut Camera3d, With>, ) { for mut camera_3d in view_targets.iter_mut() { let mut depth_texture_usages = TextureUsages::from(camera_3d.depth_texture_usages); @@ -823,21 +830,8 @@ fn extract_depth_of_field_settings( } for (entity, depth_of_field, projection) in query.iter_mut() { - let mut entity_commands = commands - .get_entity(entity) - .expect("Depth of field entity wasn't synced."); - // Depth of field is nonsensical without a perspective projection. let Projection::Perspective(ref perspective_projection) = *projection else { - // TODO: needs better strategy for cleaning up - entity_commands.remove::<( - DepthOfField, - DepthOfFieldUniform, - // components added in prepare systems (because `DepthOfFieldNode` does not query extracted components) - DepthOfFieldPipelines, - AuxiliaryDepthOfFieldTexture, - ViewDepthOfFieldBindGroupLayouts, - )>(); continue; }; @@ -845,8 +839,7 @@ fn extract_depth_of_field_settings( calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov); // Convert `DepthOfField` to `DepthOfFieldUniform`. - entity_commands.insert(( - *depth_of_field, + commands.entity(entity).insert(( DepthOfFieldUniform { focal_distance: depth_of_field.focal_distance, focal_length, @@ -858,6 +851,7 @@ fn extract_depth_of_field_settings( pad_b: 0, pad_c: 0, }, + UseDepthOfField, )); } } diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs index a58f21d9a7746..248ca30debc0d 100644 --- a/crates/bevy_core_pipeline/src/fxaa/node.rs +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -23,6 +23,7 @@ impl ViewNode for FxaaNode { &'static CameraFxaaPipeline, &'static Fxaa, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_core_pipeline/src/motion_blur/node.rs b/crates/bevy_core_pipeline/src/motion_blur/node.rs index 2497bd633deda..32bad8fde2136 100644 --- a/crates/bevy_core_pipeline/src/motion_blur/node.rs +++ b/crates/bevy_core_pipeline/src/motion_blur/node.rs @@ -29,6 +29,8 @@ impl ViewNode for MotionBlurNode { &'static MotionBlur, &'static Msaa, ); + type ViewFilter = (); + fn run( &self, _graph: &mut RenderGraphContext, diff --git a/crates/bevy_core_pipeline/src/msaa_writeback.rs b/crates/bevy_core_pipeline/src/msaa_writeback.rs index f9c543aeff03c..94154af86df89 100644 --- a/crates/bevy_core_pipeline/src/msaa_writeback.rs +++ b/crates/bevy_core_pipeline/src/msaa_writeback.rs @@ -6,6 +6,7 @@ use crate::{ use bevy_app::{App, Plugin}; use bevy_color::LinearRgba; use bevy_ecs::{prelude::*, query::QueryItem}; +use bevy_render::camera::CameraActive; use bevy_render::{ camera::ExtractedCamera, render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner}, @@ -56,6 +57,7 @@ impl ViewNode for MsaaWritebackNode { &'static MsaaWritebackBlitPipeline, &'static Msaa, ); + type ViewFilter = (); fn run<'w>( &self, @@ -124,7 +126,7 @@ fn prepare_msaa_writeback_pipelines( pipeline_cache: Res, mut pipelines: ResMut>, blit_pipeline: Res, - view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa)>, + view_targets: Query<(Entity, &ViewTarget, &ExtractedCamera, &Msaa), With>, ) { for (entity, view_target, camera, msaa) in view_targets.iter() { // only do writeback if writeback is enabled for the camera and this isn't the first camera in the target, diff --git a/crates/bevy_core_pipeline/src/oit/resolve/node.rs b/crates/bevy_core_pipeline/src/oit/resolve/node.rs index 14d42235f12a9..b84cbf06f0f15 100644 --- a/crates/bevy_core_pipeline/src/oit/resolve/node.rs +++ b/crates/bevy_core_pipeline/src/oit/resolve/node.rs @@ -24,6 +24,7 @@ impl ViewNode for OitResolveNode { &'static OitResolvePipelineId, &'static ViewDepthTexture, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_core_pipeline/src/post_process/mod.rs b/crates/bevy_core_pipeline/src/post_process/mod.rs index 79c41f990b33c..01f286f10a00f 100644 --- a/crates/bevy_core_pipeline/src/post_process/mod.rs +++ b/crates/bevy_core_pipeline/src/post_process/mod.rs @@ -15,6 +15,7 @@ use bevy_ecs::{ world::{FromWorld, World}, }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::render_component::{RenderComponent, RenderComponentPlugin}; use bevy_render::{ camera::Camera, extract_component::{ExtractComponent, ExtractComponentPlugin}, @@ -122,6 +123,9 @@ pub struct ChromaticAberration { pub max_samples: u32, } +#[derive(Component, RenderComponent)] +pub struct UseChromaticAberration; + /// GPU pipeline data for the built-in postprocessing stack. /// /// This is stored in the render world. @@ -216,7 +220,10 @@ impl Plugin for PostProcessingPlugin { ); app.register_type::(); - app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(( + ExtractComponentPlugin::::default(), + RenderComponentPlugin::::default(), + )); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; @@ -355,6 +362,7 @@ impl ViewNode for PostProcessingNode { Read, Read, ); + type ViewFilter = With; fn run<'w>( &self, @@ -490,14 +498,14 @@ impl ExtractComponent for ChromaticAberration { type QueryFilter = With; - type Out = ChromaticAberration; + type Out = (ChromaticAberration, UseChromaticAberration); fn extract_component( chromatic_aberration: QueryItem<'_, Self::QueryData>, ) -> Option { // Skip the postprocessing phase entirely if the intensity is zero. if chromatic_aberration.intensity > 0.0 { - Some(chromatic_aberration.clone()) + Some((chromatic_aberration.clone(), UseChromaticAberration)) } else { None } diff --git a/crates/bevy_core_pipeline/src/prepass/mod.rs b/crates/bevy_core_pipeline/src/prepass/mod.rs index f8151a0e163c4..8483a5f498876 100644 --- a/crates/bevy_core_pipeline/src/prepass/mod.rs +++ b/crates/bevy_core_pipeline/src/prepass/mod.rs @@ -34,6 +34,7 @@ use bevy_asset::UntypedAssetId; use bevy_ecs::prelude::*; use bevy_math::Mat4; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::render_component::RenderComponent; use bevy_render::sync_world::MainEntity; use bevy_render::{ render_phase::{ @@ -72,7 +73,7 @@ pub struct MotionVectorPrepass; #[reflect(Component, Default)] pub struct DeferredPrepass; -#[derive(Component, ShaderType, Clone)] +#[derive(Component, RenderComponent, ShaderType, Clone)] pub struct PreviousViewData { pub view_from_world: Mat4, pub clip_from_world: Mat4, diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 17f5dfb2cfe84..9d46ba43cb2a1 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -37,6 +37,7 @@ impl ViewNode for PrepassNode { Option<&'static SkyboxPrepassBindGroup>, Option<&'static PreviousViewUniformOffset>, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_core_pipeline/src/smaa/mod.rs b/crates/bevy_core_pipeline/src/smaa/mod.rs index a41a77c806844..1e44036fca38d 100644 --- a/crates/bevy_core_pipeline/src/smaa/mod.rs +++ b/crates/bevy_core_pipeline/src/smaa/mod.rs @@ -46,6 +46,7 @@ use bevy_ecs::{ }; use bevy_math::{vec4, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::camera::CameraActive; use bevy_render::{ camera::ExtractedCamera, extract_component::{ExtractComponent, ExtractComponentPlugin}, @@ -695,7 +696,10 @@ fn prepare_smaa_textures( mut commands: Commands, render_device: Res, mut texture_cache: ResMut, - view_targets: Query<(Entity, &ExtractedCamera), (With, With)>, + view_targets: Query< + (Entity, &ExtractedCamera), + (With, With, With), + >, ) { for (entity, camera) in &view_targets { let Some(texture_size) = camera.physical_target_size else { @@ -834,6 +838,7 @@ impl ViewNode for SmaaNode { Read, Read, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_core_pipeline/src/taa/mod.rs b/crates/bevy_core_pipeline/src/taa/mod.rs index f8333fc53827e..d243e3354a8d0 100644 --- a/crates/bevy_core_pipeline/src/taa/mod.rs +++ b/crates/bevy_core_pipeline/src/taa/mod.rs @@ -18,6 +18,8 @@ use bevy_ecs::{ }; use bevy_math::vec2; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::extract_component::ExtractComponent; +use bevy_render::render_component::{RenderComponent, RenderComponentPlugin}; use bevy_render::{ camera::{ExtractedCamera, MipBias, TemporalJitter}, prelude::{Camera, Projection}, @@ -51,7 +53,8 @@ impl Plugin for TemporalAntiAliasPlugin { fn build(&self, app: &mut App) { load_internal_asset!(app, TAA_SHADER_HANDLE, "taa.wgsl", Shader::from_wgsl); - app.register_type::(); + app.add_plugins(RenderComponentPlugin::::default()) + .register_type::(); app.add_plugins(SyncComponentPlugin::::default()); @@ -143,7 +146,7 @@ pub struct TemporalAntiAliasBundle { /// 2. Render particles after TAA /// /// If no [`MipBias`] component is attached to the camera, TAA will add a `MipBias(-1.0)` component. -#[derive(Component, Reflect, Clone)] +#[derive(Component, ExtractComponent, Reflect, Clone)] #[reflect(Component, Default)] #[require(TemporalJitter, DepthPrepass, MotionVectorPrepass)] #[doc(alias = "Taa")] @@ -158,6 +161,9 @@ pub struct TemporalAntiAliasing { pub reset: bool, } +#[derive(Component, RenderComponent)] +pub struct UseTemporalAntiAliasing; + #[deprecated(since = "0.15.0", note = "Renamed to `TemporalAntiAliasing`")] pub type TemporalAntiAliasSettings = TemporalAntiAliasing; @@ -180,6 +186,7 @@ impl ViewNode for TemporalAntiAliasNode { &'static TemporalAntiAliasPipelineId, &'static Msaa, ); + type ViewFilter = With; fn run( &self, @@ -376,27 +383,20 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut(); } } } fn prepare_taa_jitter_and_mip_bias( frame_count: Res, - mut query: Query<(Entity, &mut TemporalJitter, Option<&MipBias>), With>, + mut query: Query< + (Entity, &mut TemporalJitter, Option<&MipBias>), + With, + >, mut commands: Commands, ) { // Halton sequence (2, 3) - 0.5, skipping i = 0 @@ -433,7 +433,7 @@ fn prepare_taa_history_textures( mut texture_cache: ResMut, render_device: Res, frame_count: Res, - views: Query<(Entity, &ExtractedCamera, &ExtractedView), With>, + views: Query<(Entity, &ExtractedCamera, &ExtractedView), With>, ) { for (entity, camera, view) in &views { if let Some(physical_target_size) = camera.physical_target_size { diff --git a/crates/bevy_core_pipeline/src/tonemapping/node.rs b/crates/bevy_core_pipeline/src/tonemapping/node.rs index 0f8f6edc49eb2..5743e8efc3458 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/node.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/node.rs @@ -30,6 +30,7 @@ impl ViewNode for TonemappingNode { &'static ViewTonemappingPipeline, &'static Tonemapping, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_core_pipeline/src/upscaling/node.rs b/crates/bevy_core_pipeline/src/upscaling/node.rs index ece71c194710c..aec4e43583e27 100644 --- a/crates/bevy_core_pipeline/src/upscaling/node.rs +++ b/crates/bevy_core_pipeline/src/upscaling/node.rs @@ -22,6 +22,7 @@ impl ViewNode for UpscalingNode { &'static ViewUpscalingPipeline, Option<&'static ExtractedCamera>, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_pbr/src/cluster/mod.rs b/crates/bevy_pbr/src/cluster/mod.rs index 73944f17a0014..de83678384029 100644 --- a/crates/bevy_pbr/src/cluster/mod.rs +++ b/crates/bevy_pbr/src/cluster/mod.rs @@ -13,6 +13,7 @@ use bevy_ecs::{ }; use bevy_math::{AspectRatio, UVec2, UVec3, UVec4, Vec3Swizzles as _, Vec4}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::render_component::RenderComponent; use bevy_render::{ camera::Camera, render_resource::{ @@ -194,6 +195,9 @@ pub struct ExtractedClusterableObjects { data: Vec, } +#[derive(Component, RenderComponent)] +pub struct UseClustering; + #[derive(ShaderType)] struct GpuClusterOffsetsAndCountsUniform { data: Box<[UVec4; ViewClusterBindings::MAX_UNIFORM_ITEMS]>, @@ -565,6 +569,7 @@ pub fn extract_clusters( far: clusters.far, dimensions: clusters.dimensions, }, + UseClustering, )); } } @@ -575,7 +580,7 @@ pub fn prepare_clusters( render_queue: Res, mesh_pipeline: Res, global_clusterable_object_meta: Res, - views: Query<(Entity, &ExtractedClusterableObjects)>, + views: Query<(Entity, &ExtractedClusterableObjects), With>, ) { let render_device = render_device.into_inner(); let supports_storage_buffers = matches!( diff --git a/crates/bevy_pbr/src/deferred/mod.rs b/crates/bevy_pbr/src/deferred/mod.rs index bcb23a7048bfd..4e7373f4c5621 100644 --- a/crates/bevy_pbr/src/deferred/mod.rs +++ b/crates/bevy_pbr/src/deferred/mod.rs @@ -156,6 +156,7 @@ impl ViewNode for DeferredOpaquePass3dPbrLightingNode { &'static DeferredLightingIdDepthTexture, &'static DeferredLightingPipeline, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 559fa99dcc2bf..efc626bc5f2a6 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -116,6 +116,7 @@ use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle}; use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d}; use bevy_ecs::prelude::*; +use bevy_render::render_component::RenderComponentPlugin; use bevy_render::{ alpha::AlphaMode, camera::{ @@ -351,6 +352,10 @@ impl Plugin for PbrPlugin { SyncComponentPlugin::::default(), SyncComponentPlugin::::default(), )) + .add_plugins(( + RenderComponentPlugin::::default(), + RenderComponentPlugin::::default(), + )) .configure_sets( PostUpdate, ( diff --git a/crates/bevy_pbr/src/meshlet/cull_clusters.wgsl b/crates/bevy_pbr/src/meshlet/cull_clusters.wgsl index fe5df60f12082..d4b4bda663270 100644 --- a/crates/bevy_pbr/src/meshlet/cull_clusters.wgsl +++ b/crates/bevy_pbr/src/meshlet/cull_clusters.wgsl @@ -14,7 +14,7 @@ meshlet_raster_clusters, meshlet_raster_cluster_rightmost_slot, } -#import bevy_render::maths::affine3_to_square +#import bevy_der::maths::affine3_to_square /// Culls individual clusters (1 per thread) in two passes (two pass occlusion culling), and outputs a bitmask of which clusters survived. /// 1. The first pass tests instance visibility, frustum culling, LOD selection, and finally occlusion culling using last frame's depth pyramid. diff --git a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs index b1b91e8cc5c6a..887ef08918bcf 100644 --- a/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs +++ b/crates/bevy_pbr/src/meshlet/material_pipeline_prepare.rs @@ -57,7 +57,7 @@ pub fn prepare_material_meshlet_meshes_main_opaque_pass( Has>, Has>, ), - With, + (With, With), >, ) where M::Data: PartialEq + Eq + Hash + Clone, diff --git a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs index 9c2d432d8856a..9b996d56e81c7 100644 --- a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs +++ b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs @@ -46,6 +46,7 @@ impl ViewNode for MeshletMainOpaquePass3dNode { &'static MeshletViewBindGroups, &'static MeshletViewResources, ); + type ViewFilter = (); fn run( &self, @@ -152,6 +153,7 @@ impl ViewNode for MeshletPrepassNode { &'static MeshletViewBindGroups, &'static MeshletViewResources, ); + type ViewFilter = (); fn run( &self, @@ -275,6 +277,7 @@ impl ViewNode for MeshletDeferredGBufferPrepassNode { &'static MeshletViewBindGroups, &'static MeshletViewResources, ); + type ViewFilter = (); fn run( &self, diff --git a/crates/bevy_pbr/src/meshlet/resource_manager.rs b/crates/bevy_pbr/src/meshlet/resource_manager.rs index fd95f45b3f26e..f8c27f92e4260 100644 --- a/crates/bevy_pbr/src/meshlet/resource_manager.rs +++ b/crates/bevy_pbr/src/meshlet/resource_manager.rs @@ -5,6 +5,7 @@ use bevy_core_pipeline::{ core_3d::Camera3d, prepass::{PreviousViewData, PreviousViewUniforms}, }; +use bevy_ecs::prelude::With; use bevy_ecs::{ component::Component, entity::{Entity, EntityHashMap}, @@ -12,6 +13,7 @@ use bevy_ecs::{ system::{Commands, Query, Res, ResMut, Resource}, }; use bevy_math::{UVec2, Vec4Swizzles}; +use bevy_render::camera::ExtractedCamera; use bevy_render::{ render_resource::*, renderer::{RenderDevice, RenderQueue}, @@ -302,12 +304,15 @@ fn upload_storage_buffer( pub fn prepare_meshlet_per_frame_resources( mut resource_manager: ResMut, mut instance_manager: ResMut, - views: Query<( - Entity, - &ExtractedView, - Option<&RenderLayers>, - AnyOf<(&Camera3d, &ShadowView)>, - )>, + views: Query< + ( + Entity, + &ExtractedView, + Option<&RenderLayers>, + AnyOf<(&Camera3d, &ShadowView)>, + ), + With, + >, mut texture_cache: ResMut, render_queue: Res, render_device: Res, diff --git a/crates/bevy_pbr/src/prepass/mod.rs b/crates/bevy_pbr/src/prepass/mod.rs index 63ff1d87b1246..72fcc550d8c52 100644 --- a/crates/bevy_pbr/src/prepass/mod.rs +++ b/crates/bevy_pbr/src/prepass/mod.rs @@ -153,6 +153,7 @@ where .add_plugins(( BinnedRenderPhasePlugin::::default(), BinnedRenderPhasePlugin::::default(), + RenderComponentPlugin::::default(), )); } @@ -588,15 +589,10 @@ pub fn extract_camera_previous_view_data( cameras_3d: Extract), With>>, ) { for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() { - let mut entity = commands - .get_entity(entity) - .expect("Camera entity wasn't synced."); if camera.is_active { if let Some(previous_view_data) = maybe_previous_view_data { - entity.insert(previous_view_data.clone()); + commands.entity(entity).insert(previous_view_data.clone()); } - } else { - entity.remove::(); } } } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index a502f1d7352fa..b0cea09378d95 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -9,6 +9,7 @@ use bevy_ecs::{ system::lifetimeless::Read, }; use bevy_math::{ops, Mat4, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles}; +use bevy_render::render_component::RenderComponent; use bevy_render::sync_world::{MainEntity, RenderEntity, TemporaryRenderEntity}; use bevy_render::{ diagnostic::RecordDiagnostics, @@ -65,6 +66,9 @@ pub struct ExtractedDirectionalLight { pub render_layers: RenderLayers, } +#[derive(Component, RenderComponent)] +pub struct VisibleLight; + // NOTE: These must match the bit flags in bevy_pbr/src/render/mesh_view_types.wgsl! bitflags::bitflags! { #[repr(transparent)] @@ -379,10 +383,6 @@ pub fn extract_lights( ) in &directional_lights { if !view_visibility.get() { - commands - .get_entity(entity) - .expect("Light entity wasn't synced.") - .remove::<(ExtractedDirectionalLight, RenderCascadesVisibleEntities)>(); continue; } @@ -440,6 +440,7 @@ pub fn extract_lights( RenderCascadesVisibleEntities { entities: cascade_visible_entities, }, + VisibleLight, )); } } @@ -667,7 +668,7 @@ pub fn prepare_lights( &ExtractedClusterConfig, Option<&RenderLayers>, ), - With, + (With, With), >, ambient_light: Res, point_light_shadow_map: Res, @@ -682,7 +683,7 @@ pub fn prepare_lights( &ExtractedPointLight, AnyOf<(&CubemapFrusta, &Frustum)>, )>, - directional_lights: Query<(Entity, &ExtractedDirectionalLight)>, + directional_lights: Query<(Entity, &ExtractedDirectionalLight), With>, mut light_view_entities: Query<&mut LightViewEntities>, mut live_shadow_mapping_lights: Local, ) { @@ -1449,10 +1450,7 @@ pub fn queue_shadows( view_lights: Query<(Entity, &ViewLightEntities)>, view_light_entities: Query<&LightEntity>, point_light_entities: Query<&RenderCubemapVisibleEntities, With>, - directional_light_entities: Query< - &RenderCascadesVisibleEntities, - With, - >, + directional_light_entities: Query<&RenderCascadesVisibleEntities, With>, spot_light_entities: Query<&RenderVisibleMeshEntities, With>, ) where M::Data: PartialEq + Eq + Hash + Clone, diff --git a/crates/bevy_pbr/src/ssao/mod.rs b/crates/bevy_pbr/src/ssao/mod.rs index 42ac9978f6192..d4cbc7378a61b 100644 --- a/crates/bevy_pbr/src/ssao/mod.rs +++ b/crates/bevy_pbr/src/ssao/mod.rs @@ -17,6 +17,8 @@ use bevy_ecs::{ world::{FromWorld, World}, }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::extract_component::ExtractComponentPlugin; +use bevy_render::render_component::{RenderComponent, RenderComponentPlugin}; use bevy_render::{ camera::{ExtractedCamera, TemporalJitter}, extract_component::ExtractComponent, @@ -30,7 +32,6 @@ use bevy_render::{ *, }, renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue}, - sync_component::SyncComponentPlugin, sync_world::RenderEntity, texture::{CachedTexture, TextureCache}, view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms}, @@ -74,7 +75,10 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin { app.register_type::(); - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(( + ExtractComponentPlugin::::default(), + RenderComponentPlugin::::default(), + )); } fn finish(&self, app: &mut App) { @@ -185,6 +189,9 @@ impl Default for ScreenSpaceAmbientOcclusion { } } +#[derive(Component, RenderComponent)] +pub struct UseScreenSpaceAmbientOcclusion; + #[deprecated(since = "0.15.0", note = "Renamed to `ScreenSpaceAmbientOcclusion`")] pub type ScreenSpaceAmbientOcclusionSettings = ScreenSpaceAmbientOcclusion; @@ -228,6 +235,7 @@ impl ViewNode for SsaoNode { &'static SsaoBindGroups, &'static ViewUniformOffset, ); + type ViewFilter = (); fn run( &self, @@ -521,12 +529,17 @@ fn extract_ssao_settings( mut commands: Commands, cameras: Extract< Query< - (RenderEntity, &Camera, &ScreenSpaceAmbientOcclusion, &Msaa), - (With, With, With), + (RenderEntity, &Camera, &Msaa), + ( + With, + With, + With, + With, + ), >, >, ) { - for (entity, camera, ssao_settings, msaa) in &cameras { + for (entity, camera, msaa) in &cameras { if *msaa != Msaa::Off { error!( "SSAO is being used which requires Msaa::Off, but Msaa is currently set to Msaa::{:?}", @@ -534,13 +547,10 @@ fn extract_ssao_settings( ); return; } - let mut entity_commands = commands - .get_entity(entity) - .expect("SSAO entity wasn't synced."); if camera.is_active { - entity_commands.insert(ssao_settings.clone()); - } else { - entity_commands.remove::(); + commands + .entity(entity) + .insert(UseScreenSpaceAmbientOcclusion); } } } @@ -558,7 +568,10 @@ fn prepare_ssao_textures( mut commands: Commands, mut texture_cache: ResMut, render_device: Res, - views: Query<(Entity, &ExtractedCamera, &ScreenSpaceAmbientOcclusion)>, + views: Query< + (Entity, &ExtractedCamera, &ScreenSpaceAmbientOcclusion), + With, + >, ) { for (entity, camera, ssao_settings) in &views { let Some(physical_viewport_size) = camera.physical_viewport_size else { diff --git a/crates/bevy_pbr/src/ssr/mod.rs b/crates/bevy_pbr/src/ssr/mod.rs index 5852315f58d84..e40cd05e2b77a 100644 --- a/crates/bevy_pbr/src/ssr/mod.rs +++ b/crates/bevy_pbr/src/ssr/mod.rs @@ -274,6 +274,7 @@ impl ViewNode for ScreenSpaceReflectionsNode { Read, Read, ); + type ViewFilter = (); fn run<'w>( &self, diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index 7a94a1e135797..e4dbcd7511bcf 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -38,6 +38,7 @@ use bevy_core_pipeline::core_3d::{ graph::{Core3d, Node3d}, prepare_core_3d_depth_textures, }; +use bevy_ecs::query::QueryItem; use bevy_ecs::{ bundle::Bundle, component::Component, reflect::ReflectComponent, schedule::IntoSystemConfigs as _, @@ -47,11 +48,12 @@ use bevy_math::{ Vec2, Vec3, }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; +use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; +use bevy_render::render_component::{RenderComponent, RenderComponentPlugin}; use bevy_render::{ mesh::{Mesh, Meshable}, render_graph::{RenderGraphApp, ViewNodeRunner}, render_resource::{Shader, SpecializedRenderPipelines}, - sync_component::SyncComponentPlugin, texture::Image, view::{InheritedVisibility, ViewVisibility, Visibility}, ExtractSchedule, Render, RenderApp, RenderSet, @@ -73,14 +75,14 @@ pub struct VolumetricFogPlugin; /// (`shadows_enabled: true`) to make volumetric fog interact with it. /// /// This allows the light to generate light shafts/god rays. -#[derive(Clone, Copy, Component, Default, Debug, Reflect)] +#[derive(Clone, Copy, Component, ExtractComponent, Default, Debug, Reflect)] #[reflect(Component, Default, Debug)] pub struct VolumetricLight; /// When placed on a [`bevy_core_pipeline::core_3d::Camera3d`], enables /// volumetric fog and volumetric lighting, also known as light shafts or god /// rays. -#[derive(Clone, Copy, Component, Debug, Reflect)] +#[derive(Clone, Copy, Component, ExtractComponent, Debug, Reflect)] #[reflect(Component, Default, Debug)] pub struct VolumetricFog { /// Color of the ambient light. @@ -118,6 +120,10 @@ pub struct VolumetricFog { pub step_count: u32, } +/// A marker component that enables volumetric fog. +#[derive(Component, RenderComponent)] +pub struct UseVolumetricFog; + #[deprecated(since = "0.15.0", note = "Renamed to `VolumetricFog`")] pub type VolumetricFogSettings = VolumetricFog; @@ -216,6 +222,18 @@ pub struct FogVolume { pub light_intensity: f32, } +impl ExtractComponent for FogVolume { + type QueryData = (&'static Self, &'static GlobalTransform); + type QueryFilter = (); + type Out = (Self, GlobalTransform); + + fn extract_component( + (fog_volume, global_transform): QueryItem<'_, Self::QueryData>, + ) -> Option { + Some((fog_volume.clone(), *global_transform)) + } +} + impl Plugin for VolumetricFogPlugin { fn build(&self, app: &mut App) { load_internal_asset!( @@ -232,7 +250,12 @@ impl Plugin for VolumetricFogPlugin { app.register_type::() .register_type::(); - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(( + ExtractComponentPlugin::::default(), + ExtractComponentPlugin::::default(), + ExtractComponentPlugin::::default(), + RenderComponentPlugin::::default(), + )); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; diff --git a/crates/bevy_pbr/src/volumetric_fog/render.rs b/crates/bevy_pbr/src/volumetric_fog/render.rs index d1dd500f44396..ed8c6bb79509f 100644 --- a/crates/bevy_pbr/src/volumetric_fog/render.rs +++ b/crates/bevy_pbr/src/volumetric_fog/render.rs @@ -45,6 +45,7 @@ use bevy_transform::components::GlobalTransform; use bevy_utils::prelude::default; use bitflags::bitflags; +use crate::volumetric_fog::UseVolumetricFog; use crate::{ FogVolume, MeshPipelineViewLayoutKey, MeshPipelineViewLayouts, MeshViewBindGroup, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset, @@ -271,43 +272,15 @@ impl FromWorld for VolumetricFogPipeline { /// from the main world to the render world. pub fn extract_volumetric_fog( mut commands: Commands, - view_targets: Extract>, - fog_volumes: Extract>, - volumetric_lights: Extract>, + view_targets: Extract>>, + volumetric_lights: Extract>>, ) { if volumetric_lights.is_empty() { - // TODO: needs better way to handle clean up in render world - for (entity, ..) in view_targets.iter() { - commands - .entity(entity) - .remove::<(VolumetricFog, ViewVolumetricFogPipelines, ViewVolumetricFog)>(); - } - for (entity, ..) in fog_volumes.iter() { - commands.entity(entity).remove::(); - } return; } - for (entity, volumetric_fog) in view_targets.iter() { - commands - .get_entity(entity) - .expect("Volumetric fog entity wasn't synced.") - .insert(*volumetric_fog); - } - - for (entity, fog_volume, fog_transform) in fog_volumes.iter() { - commands - .get_entity(entity) - .expect("Fog volume entity wasn't synced.") - .insert((*fog_volume).clone()) - .insert(*fog_transform); - } - - for (entity, volumetric_light) in volumetric_lights.iter() { - commands - .get_entity(entity) - .expect("Volumetric light entity wasn't synced.") - .insert(*volumetric_light); + for entity in view_targets.iter() { + commands.entity(entity).insert(UseVolumetricFog); } } @@ -326,6 +299,7 @@ impl ViewNode for VolumetricFogNode { Read, Read, ); + type ViewFilter = With; fn run<'w>( &self, diff --git a/crates/bevy_render/macros/src/lib.rs b/crates/bevy_render/macros/src/lib.rs index d7cb909477326..4af21a9c869c7 100644 --- a/crates/bevy_render/macros/src/lib.rs +++ b/crates/bevy_render/macros/src/lib.rs @@ -5,6 +5,7 @@ mod as_bind_group; mod extract_component; mod extract_resource; +mod render_component; use bevy_macro_utils::{derive_label, BevyManifest}; use proc_macro::TokenStream; @@ -65,6 +66,11 @@ pub fn derive_as_bind_group(input: TokenStream) -> TokenStream { as_bind_group::derive_as_bind_group(input).unwrap_or_else(|err| err.to_compile_error().into()) } +#[proc_macro_derive(RenderComponent)] +pub fn derive_render_component(input: TokenStream) -> TokenStream { + render_component::derive_render_component(input) +} + /// Derive macro generating an impl of the trait `RenderLabel`. /// /// This does not work for unions. diff --git a/crates/bevy_render/macros/src/render_component.rs b/crates/bevy_render/macros/src/render_component.rs new file mode 100644 index 0000000000000..6b4db2e15dc9d --- /dev/null +++ b/crates/bevy_render/macros/src/render_component.rs @@ -0,0 +1,18 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, DeriveInput, Path}; + +pub fn derive_render_component(input: TokenStream) -> TokenStream { + let mut ast = parse_macro_input!(input as DeriveInput); + let bevy_render_path: Path = crate::bevy_render_path(); + + ast.generics.make_where_clause(); + + let struct_name = &ast.ident; + let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); + + TokenStream::from(quote! { + impl #impl_generics #bevy_render_path::render_component::RenderComponent for #struct_name #type_generics #where_clause { + } + }) +} diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index f00ebb5e9d5f3..33b029254ee7d 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -32,7 +32,7 @@ use bevy_ecs::{ }; use bevy_math::{ops, vec2, Dir3, Mat4, Ray3d, Rect, URect, UVec2, UVec4, Vec2, Vec3}; use bevy_reflect::prelude::*; -use bevy_render_macros::ExtractComponent; +use bevy_render_macros::{ExtractComponent, RenderComponent}; use bevy_transform::components::{GlobalTransform, Transform}; use bevy_utils::{tracing::warn, warn_once, HashMap, HashSet}; use bevy_window::{ @@ -1015,6 +1015,9 @@ pub struct ExtractedCamera { pub hdr: bool, } +#[derive(Component, RenderComponent)] +pub struct CameraActive; + pub fn extract_cameras( mut commands: Commands, query: Extract< @@ -1054,15 +1057,6 @@ pub fn extract_cameras( ) in query.iter() { if !camera.is_active { - commands.entity(render_entity).remove::<( - ExtractedCamera, - ExtractedView, - RenderVisibleEntities, - TemporalJitter, - RenderLayers, - Projection, - GpuCulling, - )>(); continue; } @@ -1108,6 +1102,7 @@ pub fn extract_cameras( }; let mut commands = commands.entity(render_entity); commands.insert(( + CameraActive, ExtractedCamera { target: camera.target.normalize(primary_window), viewport: camera.viewport.clone(), @@ -1179,7 +1174,7 @@ pub struct SortedCamera { pub fn sort_cameras( mut sorted_cameras: ResMut, - mut cameras: Query<(Entity, &mut ExtractedCamera)>, + mut cameras: Query<(Entity, &mut ExtractedCamera), With>, ) { sorted_cameras.0.clear(); for (entity, camera) in cameras.iter() { diff --git a/crates/bevy_render/src/camera/camera_driver_node.rs b/crates/bevy_render/src/camera/camera_driver_node.rs index f01d781478783..d22f8503bd459 100644 --- a/crates/bevy_render/src/camera/camera_driver_node.rs +++ b/crates/bevy_render/src/camera/camera_driver_node.rs @@ -1,21 +1,23 @@ +use crate::camera::CameraActive; use crate::{ camera::{ClearColor, ExtractedCamera, NormalizedRenderTarget, SortedCameras}, render_graph::{Node, NodeRunError, RenderGraphContext}, renderer::RenderContext, view::ExtractedWindows, }; +use bevy_ecs::query::With; use bevy_ecs::{prelude::QueryState, world::World}; use bevy_utils::HashSet; use wgpu::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor, StoreOp}; pub struct CameraDriverNode { - cameras: QueryState<&'static ExtractedCamera>, + cameras: QueryState<&'static ExtractedCamera, With>, } impl CameraDriverNode { pub fn new(world: &mut World) -> Self { Self { - cameras: world.query(), + cameras: world.query_filtered(), } } } diff --git a/crates/bevy_render/src/camera/mod.rs b/crates/bevy_render/src/camera/mod.rs index 83c882cc3ed8e..9770f359c743d 100644 --- a/crates/bevy_render/src/camera/mod.rs +++ b/crates/bevy_render/src/camera/mod.rs @@ -11,6 +11,7 @@ pub use clear_color::*; pub use manual_texture_view::*; pub use projection::*; +use crate::render_component::RenderComponentPlugin; use crate::{ extract_component::ExtractComponentPlugin, extract_resource::ExtractResourcePlugin, render_graph::RenderGraph, ExtractSchedule, Render, RenderApp, RenderSet, @@ -39,6 +40,7 @@ impl Plugin for CameraPlugin { ExtractResourcePlugin::::default(), ExtractResourcePlugin::::default(), ExtractComponentPlugin::::default(), + RenderComponentPlugin::::default(), )); if let Some(render_app) = app.get_sub_app_mut(RenderApp) { diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_render/src/extract_component.rs index 64e744775ffaf..4bf7967cba122 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_render/src/extract_component.rs @@ -42,10 +42,8 @@ pub trait ExtractComponent: Component { /// The output from extraction. /// - /// Returning `None` based on the queried item will remove the component from the entity in - /// the render world. This can be used, for example, to conditionally extract camera settings - /// in order to disable a rendering feature on the basis of those settings, without removing - /// the component from the entity in the main world. + /// Caution: Returning `None` based on the queried item may leave a previously inserted + /// component in the render world. /// /// The output may be different from the queried component. /// This can be useful for example if only a subset of the fields are useful @@ -206,8 +204,6 @@ fn extract_components( for (entity, query_item) in &query { if let Some(component) = C::extract_component(query_item) { values.push((entity, component)); - } else { - commands.entity(entity).remove::(); } } *previous_len = values.len(); @@ -225,8 +221,6 @@ fn extract_visible_components( if view_visibility.get() { if let Some(component) = C::extract_component(query_item) { values.push((entity, component)); - } else { - commands.entity(entity).remove::(); } } } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index a682f2b954413..8441ad7113534 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -31,6 +31,7 @@ pub mod mesh; pub mod pipelined_rendering; pub mod primitives; pub mod render_asset; +pub mod render_component; pub mod render_graph; pub mod render_phase; pub mod render_resource; diff --git a/crates/bevy_render/src/render_component.rs b/crates/bevy_render/src/render_component.rs new file mode 100644 index 0000000000000..dc0a3d8df5b4c --- /dev/null +++ b/crates/bevy_render/src/render_component.rs @@ -0,0 +1,44 @@ +use crate::RenderSet::Cleanup; +use crate::{Render, RenderApp}; +use bevy_app::{App, Plugin}; +use bevy_ecs::entity::Entity; +use bevy_ecs::prelude::Component; +use bevy_ecs::query::With; +use bevy_ecs::schedule::IntoSystemConfigs; +use bevy_ecs::system::{Commands, Query}; +use core::marker::PhantomData; + +pub use bevy_render_macros::RenderComponent; + +/// A plugin that registers a component used to indicate that an entity should be rendered using +/// a particular render pipeline. These components are automatically removed from entities every +/// frame and must be re-added if the entity should continue to be rendered using the given +/// pipeline. +pub struct RenderComponentPlugin(PhantomData); + +impl Plugin for RenderComponentPlugin { + fn build(&self, app: &mut App) { + if let Some(render_app) = app.get_sub_app_mut(RenderApp) { + render_app.add_systems(Render, cleanup_render_component::.in_set(Cleanup)); + } + } +} + +impl Default for RenderComponentPlugin { + fn default() -> Self { + Self(PhantomData) + } +} + +/// Marker trait for components that are used to indicate that an entity should be rendered using a +/// particular render pipeline. +pub trait RenderComponent: Component {} + +fn cleanup_render_component( + mut commands: Commands, + components: Query>, +) { + for entity in components.iter() { + commands.entity(entity).remove::(); + } +} diff --git a/crates/bevy_render/src/render_graph/node.rs b/crates/bevy_render/src/render_graph/node.rs index 1e0243e31cdff..db09e7402c4b5 100644 --- a/crates/bevy_render/src/render_graph/node.rs +++ b/crates/bevy_render/src/render_graph/node.rs @@ -1,3 +1,5 @@ +use super::{InternedRenderSubGraph, RenderSubGraph}; +use crate::camera::CameraActive; use crate::{ render_graph::{ Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError, @@ -7,21 +9,19 @@ use crate::{ renderer::RenderContext, }; pub use bevy_ecs::label::DynEq; +use bevy_ecs::query::{QueryFilter, With}; use bevy_ecs::{ define_label, intern::Interned, query::{QueryItem, QueryState, ReadOnlyQueryData}, world::{FromWorld, World}, }; +pub use bevy_render_macros::RenderLabel; use bevy_utils::all_tuples_with_size; use core::fmt::Debug; use derive_more::derive::{Display, Error, From}; use downcast_rs::{impl_downcast, Downcast}; -pub use bevy_render_macros::RenderLabel; - -use super::{InternedRenderSubGraph, RenderSubGraph}; - define_label!( /// A strongly-typed class of labels used to identify a [`Node`] in a render graph. RenderLabel, @@ -344,6 +344,7 @@ pub trait ViewNode { /// The query that will be used on the view entity. /// It is guaranteed to run on the view entity, so there's no need for a filter type ViewQuery: ReadOnlyQueryData; + type ViewFilter: QueryFilter; /// Updates internal node state using the current render [`World`] prior to the run method. fn update(&mut self, _world: &mut World) {} @@ -365,7 +366,7 @@ pub trait ViewNode { /// /// This [`Node`] exists to help reduce boilerplate when making a render node that runs on a view. pub struct ViewNodeRunner { - view_query: QueryState, + view_query: QueryState)>, node: N, } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 2a6d966705e72..27c24138aacce 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -5,6 +5,7 @@ use bevy_asset::{load_internal_asset, Handle}; pub use visibility::*; pub use window::*; +use crate::camera::CameraActive; use crate::{ camera::{ CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, ExtractedCamera, @@ -815,7 +816,7 @@ pub fn prepare_view_attachments( windows: Res, images: Res>, manual_texture_views: Res, - cameras: Query<&ExtractedCamera>, + cameras: Query<&ExtractedCamera, With>, mut view_target_attachments: ResMut, ) { for camera in cameras.iter() { diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 0e19e475f6d17..a23c11c94fd06 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -69,6 +69,8 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_ecs::prelude::*; use bevy_input::InputSystem; +use bevy_render::render_component::RenderComponentPlugin; +use bevy_render::sync_component::SyncComponentPlugin; use bevy_render::{ camera::CameraUpdateSystem, view::{check_visibility, VisibilitySystems}, @@ -131,47 +133,52 @@ struct AmbiguousWithUpdateText2DLayout; impl Plugin for UiPlugin { fn build(&self, app: &mut App) { - app.init_resource::() - .init_resource::() - .init_resource::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .register_type::() - .configure_sets( - PostUpdate, - ( - CameraUpdateSystem, - UiSystem::Prepare - .before(UiSystem::Stack) - .after(bevy_animation::Animation), - UiSystem::Layout, - UiSystem::PostLayout, - ) - .chain(), + app.add_plugins(( + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + RenderComponentPlugin::::default(), + )) + .init_resource::() + .init_resource::() + .init_resource::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .register_type::() + .configure_sets( + PostUpdate, + ( + CameraUpdateSystem, + UiSystem::Prepare + .before(UiSystem::Stack) + .after(bevy_animation::Animation), + UiSystem::Layout, + UiSystem::PostLayout, ) - .add_systems( - PreUpdate, - ui_focus_system.in_set(UiSystem::Focus).after(InputSystem), - ); + .chain(), + ) + .add_systems( + PreUpdate, + ui_focus_system.in_set(UiSystem::Focus).after(InputSystem), + ); let ui_layout_system_config = ui_layout_system .in_set(UiSystem::Layout) diff --git a/crates/bevy_ui/src/render/box_shadow.rs b/crates/bevy_ui/src/render/box_shadow.rs index 7727567a095b1..3d2305e4bff22 100644 --- a/crates/bevy_ui/src/render/box_shadow.rs +++ b/crates/bevy_ui/src/render/box_shadow.rs @@ -249,6 +249,8 @@ pub fn extract_shadows( >, mapping: Extract>, ) { + extracted_box_shadows.box_shadows.clear(); + for (entity, uinode, transform, view_visibility, box_shadow, clip, camera) in &box_shadow_query { let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get()) @@ -380,7 +382,7 @@ pub fn prepare_shadows( render_device: Res, render_queue: Res, mut ui_meta: ResMut, - mut extracted_shadows: ResMut, + extracted_shadows: ResMut, view_uniforms: Res, texture_slicer_pipeline: Res, mut phases: ResMut>, @@ -511,7 +513,6 @@ pub fn prepare_shadows( *previous_len = batches.len(); commands.insert_or_spawn_batch(batches); } - extracted_shadows.box_shadows.clear(); } pub type DrawBoxShadows = (SetItemPipeline, SetBoxShadowViewBindGroup<0>, DrawBoxShadow); diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 4e1ac3069a95c..9cb433d2c618d 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -6,7 +6,7 @@ pub mod ui_texture_slice_pipeline; use crate::{ BackgroundColor, BorderColor, CalculatedClip, ComputedNode, DefaultUiCamera, Outline, - ResolvedBorderRadius, TargetCamera, UiAntiAlias, UiBoxShadowSamples, UiImage, UiScale, + ResolvedBorderRadius, TargetCamera, UiAntiAlias, UiImage, UiScale, }; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetEvent, AssetId, Assets, Handle}; @@ -41,6 +41,7 @@ use bevy_render::{ use bevy_sprite::TextureAtlasLayout; use bevy_sprite::{BorderRect, ImageScaleMode, SpriteAssetEvents, TextureAtlas}; +use bevy_render::render_component::RenderComponent; use bevy_text::{ComputedTextBlock, PositionedGlyph, TextColor, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; use bevy_utils::HashMap; @@ -510,7 +511,7 @@ const UI_CAMERA_FAR: f32 = 1000.0; // TODO: Evaluate if we still need this. const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1; -#[derive(Component)] +#[derive(Component, RenderComponent)] pub struct DefaultCameraView(pub Entity); /// Extracts all UI elements associated with a camera into the render world. @@ -518,29 +519,15 @@ pub fn extract_default_ui_camera_view( mut commands: Commands, mut transparent_render_phases: ResMut>, ui_scale: Extract>, - query: Extract< - Query< - ( - RenderEntity, - &Camera, - Option<&UiAntiAlias>, - Option<&UiBoxShadowSamples>, - ), - Or<(With, With)>, - >, - >, + query: Extract, With)>>>, mut live_entities: Local, ) { live_entities.clear(); let scale = ui_scale.0.recip(); - for (entity, camera, ui_anti_alias, shadow_samples) in &query { + for (entity, camera) in &query { // ignore inactive cameras if !camera.is_active { - commands - .get_entity(entity) - .expect("Camera entity wasn't synced.") - .remove::<(DefaultCameraView, UiAntiAlias, UiBoxShadowSamples)>(); continue; } @@ -591,12 +578,7 @@ pub fn extract_default_ui_camera_view( .get_entity(entity) .expect("Camera entity wasn't synced."); entity_commands.insert(DefaultCameraView(default_camera_view)); - if let Some(ui_anti_alias) = ui_anti_alias { - entity_commands.insert(*ui_anti_alias); - } - if let Some(shadow_samples) = shadow_samples { - entity_commands.insert(*shadow_samples); - } + transparent_render_phases.insert_or_clear(entity); live_entities.insert(entity); diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 6c035c3838efc..7bdb1f9a4f12c 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -7,6 +7,7 @@ use bevy_ecs::{ system::{lifetimeless::*, SystemParamItem}, }; use bevy_math::FloatOrd; +use bevy_render::camera::CameraActive; use bevy_render::sync_world::MainEntity; use bevy_render::{ camera::ExtractedCamera, @@ -19,7 +20,10 @@ use bevy_render::{ use bevy_utils::tracing::error; pub struct UiPassNode { - ui_view_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera), With>, + ui_view_query: QueryState< + (&'static ViewTarget, &'static ExtractedCamera), + (With, With), + >, default_camera_view_query: QueryState<&'static DefaultCameraView>, } diff --git a/examples/shader/custom_post_processing.rs b/examples/shader/custom_post_processing.rs index 0e06c684db965..fb3caf1c6ba6e 100644 --- a/examples/shader/custom_post_processing.rs +++ b/examples/shader/custom_post_processing.rs @@ -132,6 +132,7 @@ impl ViewNode for PostProcessNode { // we need to get the index of the one that is associated with the current view. &'static DynamicUniformIndex, ); + type ViewFilter = (); // Runs the node logic // This is where you encode draw commands.