From 723607057356585f0a7cf561addc97cc7da83cf4 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 10 Dec 2024 09:47:39 -0800 Subject: [PATCH] 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. --- crates/bevy_pbr/src/render/light.rs | 39 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index fe14688a24187..e13db2a8bbe18 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -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, ), With, >, @@ -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); } }