Use multidraw for shadows when GPU culling is in use. (#16692)

This patch makes shadows use multidraw when the camera they'll be drawn
to has the `GpuCulling` component. This results in a significant
reduction in drawcalls; Bistro Exterior drops to 3 drawcalls for each
shadow cascade.

Note that PR #16670 will remove the `GpuCulling` component, making
shadows automatically use multidraw. Beware of that when testing this
patch; before #16670 lands, you'll need to manually add `GpuCulling` to
your camera in order to see any performance benefits.
This commit is contained in:
Patrick Walton 2024-12-10 09:47:39 -08:00 committed by GitHub
parent bb090e6176
commit 7236070573
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -15,6 +15,7 @@ use bevy_render::{
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
camera::SortedCameras, camera::SortedCameras,
mesh::allocator::MeshAllocator, mesh::allocator::MeshAllocator,
view::GpuCulling,
}; };
use bevy_render::{ use bevy_render::{
diagnostic::RecordDiagnostics, diagnostic::RecordDiagnostics,
@ -686,6 +687,7 @@ pub fn prepare_lights(
&ExtractedView, &ExtractedView,
&ExtractedClusterConfig, &ExtractedClusterConfig,
Option<&RenderLayers>, Option<&RenderLayers>,
Has<GpuCulling>,
), ),
With<Camera3d>, With<Camera3d>,
>, >,
@ -1094,7 +1096,7 @@ pub fn prepare_lights(
let mut live_views = EntityHashSet::with_capacity_and_hasher(views_count, EntityHash); let mut live_views = EntityHashSet::with_capacity_and_hasher(views_count, EntityHash);
// set up light data for each view // set up light data for each view
for (entity, extracted_view, clusters, maybe_layers) in sorted_cameras for (entity, extracted_view, clusters, maybe_layers, has_gpu_culling) in sorted_cameras
.0 .0
.iter() .iter()
.filter_map(|sorted_camera| views.get(sorted_camera.entity).ok()) .filter_map(|sorted_camera| views.get(sorted_camera.entity).ok())
@ -1102,6 +1104,12 @@ pub fn prepare_lights(
live_views.insert(entity); live_views.insert(entity);
let mut view_lights = Vec::new(); let mut view_lights = Vec::new();
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if has_gpu_culling {
GpuPreprocessingMode::Culling
} else {
GpuPreprocessingMode::PreprocessingOnly
});
let is_orthographic = extracted_view.clip_from_view.w_axis.w == 1.0; let is_orthographic = extracted_view.clip_from_view.w_axis.w == 1.0;
let cluster_factors_zw = calculate_cluster_factors( let cluster_factors_zw = calculate_cluster_factors(
clusters.near, clusters.near,
@ -1229,15 +1237,15 @@ pub fn prepare_lights(
}, },
)); ));
if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
commands.entity(view_light_entity).insert(GpuCulling);
}
view_lights.push(view_light_entity); view_lights.push(view_light_entity);
if first { if first {
// Subsequent views with the same light entity will reuse the same shadow map // Subsequent views with the same light entity will reuse the same shadow map
// TODO: Implement GPU culling for shadow passes. shadow_render_phases.insert_or_clear(view_light_entity, gpu_preprocessing_mode);
shadow_render_phases.insert_or_clear(
view_light_entity,
gpu_preprocessing_support.min(GpuPreprocessingMode::PreprocessingOnly),
);
live_shadow_mapping_lights.insert(view_light_entity); live_shadow_mapping_lights.insert(view_light_entity);
} }
} }
@ -1321,14 +1329,15 @@ pub fn prepare_lights(
LightEntity::Spot { light_entity }, LightEntity::Spot { light_entity },
)); ));
if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
commands.entity(view_light_entity).insert(GpuCulling);
}
view_lights.push(view_light_entity); view_lights.push(view_light_entity);
if first { if first {
// Subsequent views with the same light entity will reuse the same shadow map // Subsequent views with the same light entity will reuse the same shadow map
shadow_render_phases.insert_or_clear( shadow_render_phases.insert_or_clear(view_light_entity, gpu_preprocessing_mode);
view_light_entity,
gpu_preprocessing_support.min(GpuPreprocessingMode::PreprocessingOnly),
);
live_shadow_mapping_lights.insert(view_light_entity); live_shadow_mapping_lights.insert(view_light_entity);
} }
} }
@ -1454,15 +1463,17 @@ pub fn prepare_lights(
cascade_index, cascade_index,
}, },
)); ));
if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
commands.entity(view_light_entity).insert(GpuCulling);
}
view_lights.push(view_light_entity); view_lights.push(view_light_entity);
// Subsequent views with the same light entity will **NOT** reuse the same shadow map // Subsequent views with the same light entity will **NOT** reuse the same shadow map
// (Because the cascades are unique to each view) // (Because the cascades are unique to each view)
// TODO: Implement GPU culling for shadow passes. // TODO: Implement GPU culling for shadow passes.
shadow_render_phases.insert_or_clear( shadow_render_phases.insert_or_clear(view_light_entity, gpu_preprocessing_mode);
view_light_entity,
gpu_preprocessing_support.min(GpuPreprocessingMode::PreprocessingOnly),
);
live_shadow_mapping_lights.insert(view_light_entity); live_shadow_mapping_lights.insert(view_light_entity);
} }
} }