Skip to content

Commit 61350cd

Browse files
authored
Remove components if not extracted (#15948)
# Objective - Fixes #15871 (Camera is done in #15946) ## Solution - Do the same as #15904 for other extraction systems - Added missing `SyncComponentPlugin` for DOF, TAA, and SSAO (According to the [documentation](https://dev-docs.bevyengine.org/bevy/render/sync_component/struct.SyncComponentPlugin.html), this plugin "needs to be added for manual extraction implementations." We may need to check this is done.) ## Testing Modified example locally to add toggles if not exist. - [x] DOF - toggling DOF component and perspective in `depth_of_field` example - [x] TAA - toggling `Camera.is_active` and TAA component - [x] clusters - not entirely sure, toggling `Camera.is_active` in `many_lights` example (no crash/glitch even without this PR) - [x] previous_view - toggling `Camera.is_active` in `skybox` (no crash/glitch even without this PR) - [x] lights - toggling `Visibility` of `DirectionalLight` in `lighting` example - [x] SSAO - toggling `Camera.is_active` and SSAO component in `ssao` example - [x] default UI camera view - toggling `Camera.is_active` (nop without #15946 because UI defaults to some camera even if `DefaultCameraView` is not there) - [x] volumetric fog - toggling existence of volumetric light. Looks like optimization, no change in behavior/visuals
1 parent fe7f98f commit 61350cd

File tree

9 files changed

+99
-41
lines changed

9 files changed

+99
-41
lines changed

crates/bevy_core_pipeline/src/dof/mod.rs

+30-18
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use bevy_render::{
4646
TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
4747
},
4848
renderer::{RenderContext, RenderDevice},
49+
sync_component::SyncComponentPlugin,
4950
sync_world::RenderEntity,
5051
texture::{BevyDefault, CachedTexture, TextureCache},
5152
view::{
@@ -211,6 +212,8 @@ impl Plugin for DepthOfFieldPlugin {
211212
app.register_type::<DepthOfFieldMode>();
212213
app.add_plugins(UniformComponentPlugin::<DepthOfFieldUniform>::default());
213214

215+
app.add_plugins(SyncComponentPlugin::<DepthOfField>::default());
216+
214217
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
215218
return;
216219
};
@@ -820,33 +823,42 @@ fn extract_depth_of_field_settings(
820823
}
821824

822825
for (entity, depth_of_field, projection) in query.iter_mut() {
826+
let mut entity_commands = commands
827+
.get_entity(entity)
828+
.expect("Depth of field entity wasn't synced.");
829+
823830
// Depth of field is nonsensical without a perspective projection.
824831
let Projection::Perspective(ref perspective_projection) = *projection else {
832+
// TODO: needs better strategy for cleaning up
833+
entity_commands.remove::<(
834+
DepthOfField,
835+
DepthOfFieldUniform,
836+
// components added in prepare systems (because `DepthOfFieldNode` does not query extracted components)
837+
DepthOfFieldPipelines,
838+
AuxiliaryDepthOfFieldTexture,
839+
ViewDepthOfFieldBindGroupLayouts,
840+
)>();
825841
continue;
826842
};
827843

828844
let focal_length =
829845
calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov);
830846

831847
// Convert `DepthOfField` to `DepthOfFieldUniform`.
832-
commands
833-
.get_entity(entity)
834-
.expect("Depth of field entity wasn't synced.")
835-
.insert((
836-
*depth_of_field,
837-
DepthOfFieldUniform {
838-
focal_distance: depth_of_field.focal_distance,
839-
focal_length,
840-
coc_scale_factor: focal_length * focal_length
841-
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
842-
max_circle_of_confusion_diameter: depth_of_field
843-
.max_circle_of_confusion_diameter,
844-
max_depth: depth_of_field.max_depth,
845-
pad_a: 0,
846-
pad_b: 0,
847-
pad_c: 0,
848-
},
849-
));
848+
entity_commands.insert((
849+
*depth_of_field,
850+
DepthOfFieldUniform {
851+
focal_distance: depth_of_field.focal_distance,
852+
focal_length,
853+
coc_scale_factor: focal_length * focal_length
854+
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
855+
max_circle_of_confusion_diameter: depth_of_field.max_circle_of_confusion_diameter,
856+
max_depth: depth_of_field.max_depth,
857+
pad_a: 0,
858+
pad_b: 0,
859+
pad_c: 0,
860+
},
861+
));
850862
}
851863
}
852864

crates/bevy_core_pipeline/src/taa/mod.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use bevy_render::{
3232
TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
3333
},
3434
renderer::{RenderContext, RenderDevice},
35+
sync_component::SyncComponentPlugin,
3536
sync_world::RenderEntity,
3637
texture::{BevyDefault, CachedTexture, TextureCache},
3738
view::{ExtractedView, Msaa, ViewTarget},
@@ -52,6 +53,8 @@ impl Plugin for TemporalAntiAliasPlugin {
5253

5354
app.register_type::<TemporalAntiAliasing>();
5455

56+
app.add_plugins(SyncComponentPlugin::<TemporalAntiAliasing>::default());
57+
5558
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
5659
return;
5760
};
@@ -373,12 +376,20 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
373376
cameras_3d.iter_mut(&mut main_world)
374377
{
375378
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
379+
let mut entity_commands = commands
380+
.get_entity(entity)
381+
.expect("Camera entity wasn't synced.");
376382
if camera.is_active && has_perspective_projection {
377-
commands
378-
.get_entity(entity)
379-
.expect("Camera entity wasn't synced.")
380-
.insert(taa_settings.clone());
383+
entity_commands.insert(taa_settings.clone());
381384
taa_settings.reset = false;
385+
} else {
386+
// TODO: needs better strategy for cleaning up
387+
entity_commands.remove::<(
388+
TemporalAntiAliasing,
389+
// components added in prepare systems (because `TemporalAntiAliasNode` does not query extracted components)
390+
TemporalAntiAliasHistoryTextures,
391+
TemporalAntiAliasPipelineId,
392+
)>();
382393
}
383394
}
384395
}

crates/bevy_pbr/src/cluster/mod.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,11 @@ pub fn extract_clusters(
530530
mapper: Extract<Query<RenderEntity>>,
531531
) {
532532
for (entity, clusters, camera) in &views {
533+
let mut entity_commands = commands
534+
.get_entity(entity)
535+
.expect("Clusters entity wasn't synced.");
533536
if !camera.is_active {
537+
entity_commands.remove::<(ExtractedClusterableObjects, ExtractedClusterConfig)>();
534538
continue;
535539
}
536540

@@ -554,17 +558,14 @@ pub fn extract_clusters(
554558
}
555559
}
556560

557-
commands
558-
.get_entity(entity)
559-
.expect("Clusters entity wasn't synced.")
560-
.insert((
561-
ExtractedClusterableObjects { data },
562-
ExtractedClusterConfig {
563-
near: clusters.near,
564-
far: clusters.far,
565-
dimensions: clusters.dimensions,
566-
},
567-
));
561+
entity_commands.insert((
562+
ExtractedClusterableObjects { data },
563+
ExtractedClusterConfig {
564+
near: clusters.near,
565+
far: clusters.far,
566+
dimensions: clusters.dimensions,
567+
},
568+
));
568569
}
569570
}
570571

crates/bevy_pbr/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ impl Plugin for PbrPlugin {
455455
render_app
456456
.world_mut()
457457
.add_observer(remove_light_view_entities);
458+
render_app.world_mut().add_observer(extracted_light_removed);
458459

459460
let shadow_pass_node = ShadowPassNode::new(render_app.world_mut());
460461
let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();

crates/bevy_pbr/src/prepass/mod.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -588,14 +588,15 @@ pub fn extract_camera_previous_view_data(
588588
cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
589589
) {
590590
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() {
591+
let mut entity = commands
592+
.get_entity(entity)
593+
.expect("Camera entity wasn't synced.");
591594
if camera.is_active {
592-
let mut entity = commands
593-
.get_entity(entity)
594-
.expect("Camera entity wasn't synced.");
595-
596595
if let Some(previous_view_data) = maybe_previous_view_data {
597596
entity.insert(previous_view_data.clone());
598597
}
598+
} else {
599+
entity.remove::<PreviousViewData>();
599600
}
600601
}
601602
}

crates/bevy_pbr/src/render/light.rs

+14
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ pub fn extract_lights(
379379
) in &directional_lights
380380
{
381381
if !view_visibility.get() {
382+
commands
383+
.get_entity(entity)
384+
.expect("Light entity wasn't synced.")
385+
.remove::<(ExtractedDirectionalLight, RenderCascadesVisibleEntities)>();
382386
continue;
383387
}
384388

@@ -473,6 +477,16 @@ pub(crate) fn add_light_view_entities(
473477
}
474478
}
475479

480+
/// Removes [`LightViewEntities`] when light is removed. See [`add_light_view_entities`].
481+
pub(crate) fn extracted_light_removed(
482+
trigger: Trigger<OnRemove, (ExtractedDirectionalLight, ExtractedPointLight)>,
483+
mut commands: Commands,
484+
) {
485+
if let Some(mut v) = commands.get_entity(trigger.entity()) {
486+
v.remove::<LightViewEntities>();
487+
}
488+
}
489+
476490
pub(crate) fn remove_light_view_entities(
477491
trigger: Trigger<OnRemove, LightViewEntities>,
478492
query: Query<&LightViewEntities>,

crates/bevy_pbr/src/ssao/mod.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use bevy_render::{
3030
*,
3131
},
3232
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue},
33+
sync_component::SyncComponentPlugin,
3334
sync_world::RenderEntity,
3435
texture::{CachedTexture, TextureCache},
3536
view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms},
@@ -72,6 +73,8 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin {
7273
);
7374

7475
app.register_type::<ScreenSpaceAmbientOcclusion>();
76+
77+
app.add_plugins(SyncComponentPlugin::<ScreenSpaceAmbientOcclusion>::default());
7578
}
7679

7780
fn finish(&self, app: &mut App) {
@@ -531,11 +534,13 @@ fn extract_ssao_settings(
531534
);
532535
return;
533536
}
537+
let mut entity_commands = commands
538+
.get_entity(entity)
539+
.expect("SSAO entity wasn't synced.");
534540
if camera.is_active {
535-
commands
536-
.get_entity(entity)
537-
.expect("SSAO entity wasn't synced.")
538-
.insert(ssao_settings.clone());
541+
entity_commands.insert(ssao_settings.clone());
542+
} else {
543+
entity_commands.remove::<ScreenSpaceAmbientOcclusion>();
539544
}
540545
}
541546
}

crates/bevy_pbr/src/volumetric_fog/render.rs

+9
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,15 @@ pub fn extract_volumetric_fog(
276276
volumetric_lights: Extract<Query<(RenderEntity, &VolumetricLight)>>,
277277
) {
278278
if volumetric_lights.is_empty() {
279+
// TODO: needs better way to handle clean up in render world
280+
for (entity, ..) in view_targets.iter() {
281+
commands
282+
.entity(entity)
283+
.remove::<(VolumetricFog, ViewVolumetricFogPipelines, ViewVolumetricFog)>();
284+
}
285+
for (entity, ..) in fog_volumes.iter() {
286+
commands.entity(entity).remove::<FogVolume>();
287+
}
279288
return;
280289
}
281290

crates/bevy_ui/src/render/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@ pub fn extract_default_ui_camera_view(
537537
for (entity, camera, ui_anti_alias, shadow_samples) in &query {
538538
// ignore inactive cameras
539539
if !camera.is_active {
540+
commands
541+
.get_entity(entity)
542+
.expect("Camera entity wasn't synced.")
543+
.remove::<(DefaultCameraView, UiAntiAlias, UiBoxShadowSamples)>();
540544
continue;
541545
}
542546

0 commit comments

Comments
 (0)