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