Remove components if not extracted (#15948)

# Objective

- Fixes https://github.com/bevyengine/bevy/issues/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
This commit is contained in:
akimakinai 2024-10-20 00:13:39 +09:00 committed by GitHub
parent fe7f98f7f0
commit 61350cd36f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 99 additions and 41 deletions

View file

@ -46,6 +46,7 @@ use bevy_render::{
TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
}, },
renderer::{RenderContext, RenderDevice}, renderer::{RenderContext, RenderDevice},
sync_component::SyncComponentPlugin,
sync_world::RenderEntity, sync_world::RenderEntity,
texture::{BevyDefault, CachedTexture, TextureCache}, texture::{BevyDefault, CachedTexture, TextureCache},
view::{ view::{
@ -211,6 +212,8 @@ impl Plugin for DepthOfFieldPlugin {
app.register_type::<DepthOfFieldMode>(); app.register_type::<DepthOfFieldMode>();
app.add_plugins(UniformComponentPlugin::<DepthOfFieldUniform>::default()); app.add_plugins(UniformComponentPlugin::<DepthOfFieldUniform>::default());
app.add_plugins(SyncComponentPlugin::<DepthOfField>::default());
let Some(render_app) = app.get_sub_app_mut(RenderApp) else { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return; return;
}; };
@ -820,8 +823,21 @@ fn extract_depth_of_field_settings(
} }
for (entity, depth_of_field, projection) in query.iter_mut() { 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. // Depth of field is nonsensical without a perspective projection.
let Projection::Perspective(ref perspective_projection) = *projection else { 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; continue;
}; };
@ -829,24 +845,20 @@ fn extract_depth_of_field_settings(
calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov); calculate_focal_length(depth_of_field.sensor_height, perspective_projection.fov);
// Convert `DepthOfField` to `DepthOfFieldUniform`. // Convert `DepthOfField` to `DepthOfFieldUniform`.
commands entity_commands.insert((
.get_entity(entity) *depth_of_field,
.expect("Depth of field entity wasn't synced.") DepthOfFieldUniform {
.insert(( focal_distance: depth_of_field.focal_distance,
*depth_of_field, focal_length,
DepthOfFieldUniform { coc_scale_factor: focal_length * focal_length
focal_distance: depth_of_field.focal_distance, / (depth_of_field.sensor_height * depth_of_field.aperture_f_stops),
focal_length, max_circle_of_confusion_diameter: depth_of_field.max_circle_of_confusion_diameter,
coc_scale_factor: focal_length * focal_length max_depth: depth_of_field.max_depth,
/ (depth_of_field.sensor_height * depth_of_field.aperture_f_stops), pad_a: 0,
max_circle_of_confusion_diameter: depth_of_field pad_b: 0,
.max_circle_of_confusion_diameter, pad_c: 0,
max_depth: depth_of_field.max_depth, },
pad_a: 0, ));
pad_b: 0,
pad_c: 0,
},
));
} }
} }

View file

@ -32,6 +32,7 @@ use bevy_render::{
TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureDimension, TextureFormat, TextureSampleType, TextureUsages,
}, },
renderer::{RenderContext, RenderDevice}, renderer::{RenderContext, RenderDevice},
sync_component::SyncComponentPlugin,
sync_world::RenderEntity, sync_world::RenderEntity,
texture::{BevyDefault, CachedTexture, TextureCache}, texture::{BevyDefault, CachedTexture, TextureCache},
view::{ExtractedView, Msaa, ViewTarget}, view::{ExtractedView, Msaa, ViewTarget},
@ -52,6 +53,8 @@ impl Plugin for TemporalAntiAliasPlugin {
app.register_type::<TemporalAntiAliasing>(); app.register_type::<TemporalAntiAliasing>();
app.add_plugins(SyncComponentPlugin::<TemporalAntiAliasing>::default());
let Some(render_app) = app.get_sub_app_mut(RenderApp) else { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return; return;
}; };
@ -373,12 +376,20 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
cameras_3d.iter_mut(&mut main_world) cameras_3d.iter_mut(&mut main_world)
{ {
let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_)); let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_));
let mut entity_commands = commands
.get_entity(entity)
.expect("Camera entity wasn't synced.");
if camera.is_active && has_perspective_projection { if camera.is_active && has_perspective_projection {
commands entity_commands.insert(taa_settings.clone());
.get_entity(entity)
.expect("Camera entity wasn't synced.")
.insert(taa_settings.clone());
taa_settings.reset = false; taa_settings.reset = false;
} else {
// TODO: needs better strategy for cleaning up
entity_commands.remove::<(
TemporalAntiAliasing,
// components added in prepare systems (because `TemporalAntiAliasNode` does not query extracted components)
TemporalAntiAliasHistoryTextures,
TemporalAntiAliasPipelineId,
)>();
} }
} }
} }

View file

@ -530,7 +530,11 @@ pub fn extract_clusters(
mapper: Extract<Query<RenderEntity>>, mapper: Extract<Query<RenderEntity>>,
) { ) {
for (entity, clusters, camera) in &views { for (entity, clusters, camera) in &views {
let mut entity_commands = commands
.get_entity(entity)
.expect("Clusters entity wasn't synced.");
if !camera.is_active { if !camera.is_active {
entity_commands.remove::<(ExtractedClusterableObjects, ExtractedClusterConfig)>();
continue; continue;
} }
@ -554,17 +558,14 @@ pub fn extract_clusters(
} }
} }
commands entity_commands.insert((
.get_entity(entity) ExtractedClusterableObjects { data },
.expect("Clusters entity wasn't synced.") ExtractedClusterConfig {
.insert(( near: clusters.near,
ExtractedClusterableObjects { data }, far: clusters.far,
ExtractedClusterConfig { dimensions: clusters.dimensions,
near: clusters.near, },
far: clusters.far, ));
dimensions: clusters.dimensions,
},
));
} }
} }

View file

@ -455,6 +455,7 @@ impl Plugin for PbrPlugin {
render_app render_app
.world_mut() .world_mut()
.add_observer(remove_light_view_entities); .add_observer(remove_light_view_entities);
render_app.world_mut().add_observer(extracted_light_removed);
let shadow_pass_node = ShadowPassNode::new(render_app.world_mut()); let shadow_pass_node = ShadowPassNode::new(render_app.world_mut());
let mut graph = render_app.world_mut().resource_mut::<RenderGraph>(); let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();

View file

@ -588,14 +588,15 @@ pub fn extract_camera_previous_view_data(
cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>, cameras_3d: Extract<Query<(RenderEntity, &Camera, Option<&PreviousViewData>), With<Camera3d>>>,
) { ) {
for (entity, camera, maybe_previous_view_data) in cameras_3d.iter() { 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 camera.is_active {
let mut entity = commands
.get_entity(entity)
.expect("Camera entity wasn't synced.");
if let Some(previous_view_data) = maybe_previous_view_data { if let Some(previous_view_data) = maybe_previous_view_data {
entity.insert(previous_view_data.clone()); entity.insert(previous_view_data.clone());
} }
} else {
entity.remove::<PreviousViewData>();
} }
} }
} }

View file

@ -379,6 +379,10 @@ pub fn extract_lights(
) in &directional_lights ) in &directional_lights
{ {
if !view_visibility.get() { if !view_visibility.get() {
commands
.get_entity(entity)
.expect("Light entity wasn't synced.")
.remove::<(ExtractedDirectionalLight, RenderCascadesVisibleEntities)>();
continue; continue;
} }
@ -473,6 +477,16 @@ pub(crate) fn add_light_view_entities(
} }
} }
/// Removes [`LightViewEntities`] when light is removed. See [`add_light_view_entities`].
pub(crate) fn extracted_light_removed(
trigger: Trigger<OnRemove, (ExtractedDirectionalLight, ExtractedPointLight)>,
mut commands: Commands,
) {
if let Some(mut v) = commands.get_entity(trigger.entity()) {
v.remove::<LightViewEntities>();
}
}
pub(crate) fn remove_light_view_entities( pub(crate) fn remove_light_view_entities(
trigger: Trigger<OnRemove, LightViewEntities>, trigger: Trigger<OnRemove, LightViewEntities>,
query: Query<&LightViewEntities>, query: Query<&LightViewEntities>,

View file

@ -30,6 +30,7 @@ use bevy_render::{
*, *,
}, },
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue}, renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue},
sync_component::SyncComponentPlugin,
sync_world::RenderEntity, sync_world::RenderEntity,
texture::{CachedTexture, TextureCache}, texture::{CachedTexture, TextureCache},
view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms}, view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms},
@ -72,6 +73,8 @@ impl Plugin for ScreenSpaceAmbientOcclusionPlugin {
); );
app.register_type::<ScreenSpaceAmbientOcclusion>(); app.register_type::<ScreenSpaceAmbientOcclusion>();
app.add_plugins(SyncComponentPlugin::<ScreenSpaceAmbientOcclusion>::default());
} }
fn finish(&self, app: &mut App) { fn finish(&self, app: &mut App) {
@ -531,11 +534,13 @@ fn extract_ssao_settings(
); );
return; return;
} }
let mut entity_commands = commands
.get_entity(entity)
.expect("SSAO entity wasn't synced.");
if camera.is_active { if camera.is_active {
commands entity_commands.insert(ssao_settings.clone());
.get_entity(entity) } else {
.expect("SSAO entity wasn't synced.") entity_commands.remove::<ScreenSpaceAmbientOcclusion>();
.insert(ssao_settings.clone());
} }
} }
} }

View file

@ -276,6 +276,15 @@ pub fn extract_volumetric_fog(
volumetric_lights: Extract<Query<(RenderEntity, &VolumetricLight)>>, volumetric_lights: Extract<Query<(RenderEntity, &VolumetricLight)>>,
) { ) {
if volumetric_lights.is_empty() { 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::<FogVolume>();
}
return; return;
} }

View file

@ -537,6 +537,10 @@ pub fn extract_default_ui_camera_view(
for (entity, camera, ui_anti_alias, shadow_samples) in &query { for (entity, camera, ui_anti_alias, shadow_samples) in &query {
// ignore inactive cameras // ignore inactive cameras
if !camera.is_active { if !camera.is_active {
commands
.get_entity(entity)
.expect("Camera entity wasn't synced.")
.remove::<(DefaultCameraView, UiAntiAlias, UiBoxShadowSamples)>();
continue; continue;
} }