Skip to content

Commit 00722b8

Browse files
authored
Make indirect drawing opt-out instead of opt-in, enabling multidraw by default. (#16757)
This patch replaces the undocumented `NoGpuCulling` component with a new component, `NoIndirectDrawing`, effectively turning indirect drawing on by default. Indirect mode is needed for the recently-landed multidraw feature (#16427). Since multidraw is such a win for performance, when that feature is supported the small performance tax that indirect mode incurs is virtually always worth paying. To ensure that custom drawing code such as that in the `custom_shader_instancing` example continues to function, this commit additionally makes GPU culling take the `NoFrustumCulling` component into account. This PR is an alternative to #16670 that doesn't break the `custom_shader_instancing` example. **PR #16755 should land first in order to avoid breaking deferred rendering, as multidraw currently breaks it**. ## Migration Guide * Indirect drawing (GPU culling) is now enabled by default, so the `GpuCulling` component is no longer available. To disable indirect mode, which may be useful with custom render nodes, add the new `NoIndirectDrawing` component to your camera.
1 parent 116c2b0 commit 00722b8

File tree

11 files changed

+109
-69
lines changed

11 files changed

+109
-69
lines changed

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ use bevy_render::{
6969
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
7070
mesh::allocator::SlabId,
7171
render_phase::PhaseItemBinKey,
72-
view::GpuCulling,
72+
view::NoIndirectDrawing,
7373
};
7474
pub use camera_3d::*;
7575
pub use main_opaque_pass_3d_node::*;
@@ -569,20 +569,20 @@ pub fn extract_core_3d_camera_phases(
569569
mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
570570
mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
571571
mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
572-
cameras_3d: Extract<Query<(RenderEntity, &Camera, Has<GpuCulling>), With<Camera3d>>>,
572+
cameras_3d: Extract<Query<(RenderEntity, &Camera, Has<NoIndirectDrawing>), With<Camera3d>>>,
573573
mut live_entities: Local<EntityHashSet>,
574574
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
575575
) {
576576
live_entities.clear();
577577

578-
for (entity, camera, has_gpu_culling) in &cameras_3d {
578+
for (entity, camera, no_indirect_drawing) in &cameras_3d {
579579
if !camera.is_active {
580580
continue;
581581
}
582582

583583
// If GPU culling is in use, use it (and indirect mode); otherwise, just
584584
// preprocess the meshes.
585-
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if has_gpu_culling {
585+
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
586586
GpuPreprocessingMode::Culling
587587
} else {
588588
GpuPreprocessingMode::PreprocessingOnly
@@ -616,7 +616,7 @@ pub fn extract_camera_prepass_phase(
616616
(
617617
RenderEntity,
618618
&Camera,
619-
Has<GpuCulling>,
619+
Has<NoIndirectDrawing>,
620620
Has<DepthPrepass>,
621621
Has<NormalPrepass>,
622622
Has<MotionVectorPrepass>,
@@ -633,7 +633,7 @@ pub fn extract_camera_prepass_phase(
633633
for (
634634
entity,
635635
camera,
636-
gpu_culling,
636+
no_indirect_drawing,
637637
depth_prepass,
638638
normal_prepass,
639639
motion_vector_prepass,
@@ -646,7 +646,7 @@ pub fn extract_camera_prepass_phase(
646646

647647
// If GPU culling is in use, use it (and indirect mode); otherwise, just
648648
// preprocess the meshes.
649-
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if gpu_culling {
649+
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
650650
GpuPreprocessingMode::Culling
651651
} else {
652652
GpuPreprocessingMode::PreprocessingOnly

crates/bevy_pbr/src/render/gpu_preprocess.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use bevy_render::{
3333
SpecializedComputePipeline, SpecializedComputePipelines,
3434
},
3535
renderer::{RenderContext, RenderDevice, RenderQueue},
36-
view::{GpuCulling, ViewUniform, ViewUniformOffset, ViewUniforms},
36+
view::{NoIndirectDrawing, ViewUniform, ViewUniformOffset, ViewUniforms},
3737
Render, RenderApp, RenderSet,
3838
};
3939
use bevy_utils::tracing::warn;
@@ -70,7 +70,7 @@ pub struct GpuPreprocessNode {
7070
Entity,
7171
Read<PreprocessBindGroup>,
7272
Read<ViewUniformOffset>,
73-
Has<GpuCulling>,
73+
Has<NoIndirectDrawing>,
7474
),
7575
Without<SkipGpuPreprocess>,
7676
>,
@@ -202,7 +202,7 @@ impl Node for GpuPreprocessNode {
202202
});
203203

204204
// Run the compute passes.
205-
for (view, bind_group, view_uniform_offset, gpu_culling) in
205+
for (view, bind_group, view_uniform_offset, no_indirect_drawing) in
206206
self.view_query.iter_manual(world)
207207
{
208208
// Grab the index buffer for this view.
@@ -213,7 +213,7 @@ impl Node for GpuPreprocessNode {
213213

214214
// Select the right pipeline, depending on whether GPU culling is in
215215
// use.
216-
let maybe_pipeline_id = if gpu_culling {
216+
let maybe_pipeline_id = if !no_indirect_drawing {
217217
preprocess_pipelines.gpu_culling.pipeline_id
218218
} else {
219219
preprocess_pipelines.direct.pipeline_id
@@ -235,7 +235,7 @@ impl Node for GpuPreprocessNode {
235235
compute_pass.set_pipeline(preprocess_pipeline);
236236

237237
let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![];
238-
if gpu_culling {
238+
if !no_indirect_drawing {
239239
dynamic_offsets.push(view_uniform_offset.offset);
240240
}
241241
compute_pass.set_bind_group(0, &bind_group.0, &dynamic_offsets);
@@ -422,7 +422,7 @@ pub fn prepare_preprocess_bind_groups(
422422
)
423423
.ok();
424424

425-
let bind_group = if index_buffer_vec.gpu_culling {
425+
let bind_group = if !index_buffer_vec.no_indirect_drawing {
426426
let (
427427
Some(indirect_parameters_buffer),
428428
Some(mesh_culling_data_buffer),

crates/bevy_pbr/src/render/light.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use bevy_render::{
1515
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
1616
camera::SortedCameras,
1717
mesh::allocator::MeshAllocator,
18-
view::GpuCulling,
18+
view::NoIndirectDrawing,
1919
};
2020
use bevy_render::{
2121
diagnostic::RecordDiagnostics,
@@ -687,7 +687,7 @@ pub fn prepare_lights(
687687
&ExtractedView,
688688
&ExtractedClusterConfig,
689689
Option<&RenderLayers>,
690-
Has<GpuCulling>,
690+
Has<NoIndirectDrawing>,
691691
),
692692
With<Camera3d>,
693693
>,
@@ -1096,15 +1096,15 @@ pub fn prepare_lights(
10961096
let mut live_views = EntityHashSet::with_capacity_and_hasher(views_count, EntityHash);
10971097

10981098
// set up light data for each view
1099-
for (entity, extracted_view, clusters, maybe_layers, has_gpu_culling) in sorted_cameras
1099+
for (entity, extracted_view, clusters, maybe_layers, no_indirect_drawing) in sorted_cameras
11001100
.0
11011101
.iter()
11021102
.filter_map(|sorted_camera| views.get(sorted_camera.entity).ok())
11031103
{
11041104
live_views.insert(entity);
11051105
let mut view_lights = Vec::new();
11061106

1107-
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if has_gpu_culling {
1107+
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing {
11081108
GpuPreprocessingMode::Culling
11091109
} else {
11101110
GpuPreprocessingMode::PreprocessingOnly
@@ -1237,8 +1237,8 @@ pub fn prepare_lights(
12371237
},
12381238
));
12391239

1240-
if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
1241-
commands.entity(view_light_entity).insert(GpuCulling);
1240+
if !matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
1241+
commands.entity(view_light_entity).insert(NoIndirectDrawing);
12421242
}
12431243

12441244
view_lights.push(view_light_entity);
@@ -1329,8 +1329,8 @@ pub fn prepare_lights(
13291329
LightEntity::Spot { light_entity },
13301330
));
13311331

1332-
if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
1333-
commands.entity(view_light_entity).insert(GpuCulling);
1332+
if !matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
1333+
commands.entity(view_light_entity).insert(NoIndirectDrawing);
13341334
}
13351335

13361336
view_lights.push(view_light_entity);
@@ -1464,8 +1464,8 @@ pub fn prepare_lights(
14641464
},
14651465
));
14661466

1467-
if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
1468-
commands.entity(view_light_entity).insert(GpuCulling);
1467+
if !matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) {
1468+
commands.entity(view_light_entity).insert(NoIndirectDrawing);
14691469
}
14701470

14711471
view_lights.push(view_light_entity);

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ use bevy_render::{
3737
renderer::{RenderDevice, RenderQueue},
3838
texture::DefaultImageSampler,
3939
view::{
40-
prepare_view_targets, GpuCulling, RenderVisibilityRanges, ViewTarget, ViewUniformOffset,
41-
ViewVisibility, VisibilityRange,
40+
prepare_view_targets, NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges,
41+
ViewTarget, ViewUniformOffset, ViewVisibility, VisibilityRange,
4242
},
4343
Extract,
4444
};
@@ -421,6 +421,11 @@ bitflags::bitflags! {
421421
///
422422
/// This will be `u16::MAX` if this mesh has no LOD.
423423
const LOD_INDEX_MASK = (1 << 16) - 1;
424+
/// Disables frustum culling for this mesh.
425+
///
426+
/// This corresponds to the
427+
/// [`bevy_render::view::visibility::NoFrustumCulling`] component.
428+
const NO_FRUSTUM_CULLING = 1 << 28;
424429
const SHADOW_RECEIVER = 1 << 29;
425430
const TRANSMITTED_SHADOW_RECEIVER = 1 << 30;
426431
// Indicates the sign of the determinant of the 3x3 model matrix. If the sign is positive,
@@ -435,6 +440,7 @@ impl MeshFlags {
435440
fn from_components(
436441
transform: &GlobalTransform,
437442
lod_index: Option<NonMaxU16>,
443+
no_frustum_culling: bool,
438444
not_shadow_receiver: bool,
439445
transmitted_receiver: bool,
440446
) -> MeshFlags {
@@ -443,6 +449,9 @@ impl MeshFlags {
443449
} else {
444450
MeshFlags::SHADOW_RECEIVER
445451
};
452+
if no_frustum_culling {
453+
mesh_flags |= MeshFlags::NO_FRUSTUM_CULLING;
454+
}
446455
if transmitted_receiver {
447456
mesh_flags |= MeshFlags::TRANSMITTED_SHADOW_RECEIVER;
448457
}
@@ -1046,6 +1055,7 @@ pub fn extract_meshes_for_cpu_building(
10461055
&GlobalTransform,
10471056
Option<&PreviousGlobalTransform>,
10481057
&Mesh3d,
1058+
Has<NoFrustumCulling>,
10491059
Has<NotShadowReceiver>,
10501060
Has<TransmittedShadowReceiver>,
10511061
Has<NotShadowCaster>,
@@ -1063,6 +1073,7 @@ pub fn extract_meshes_for_cpu_building(
10631073
transform,
10641074
previous_transform,
10651075
mesh,
1076+
no_frustum_culling,
10661077
not_shadow_receiver,
10671078
transmitted_receiver,
10681079
not_shadow_caster,
@@ -1084,6 +1095,7 @@ pub fn extract_meshes_for_cpu_building(
10841095
let mesh_flags = MeshFlags::from_components(
10851096
transform,
10861097
lod_index,
1098+
no_frustum_culling,
10871099
not_shadow_receiver,
10881100
transmitted_receiver,
10891101
);
@@ -1155,6 +1167,7 @@ pub fn extract_meshes_for_gpu_building(
11551167
Option<&Lightmap>,
11561168
Option<&Aabb>,
11571169
&Mesh3d,
1170+
Has<NoFrustumCulling>,
11581171
Has<NotShadowReceiver>,
11591172
Has<TransmittedShadowReceiver>,
11601173
Has<NotShadowCaster>,
@@ -1168,6 +1181,7 @@ pub fn extract_meshes_for_gpu_building(
11681181
Changed<Lightmap>,
11691182
Changed<Aabb>,
11701183
Changed<Mesh3d>,
1184+
Changed<NoFrustumCulling>,
11711185
Changed<NotShadowReceiver>,
11721186
Changed<TransmittedShadowReceiver>,
11731187
Changed<NotShadowCaster>,
@@ -1179,7 +1193,7 @@ pub fn extract_meshes_for_gpu_building(
11791193
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
11801194
mut removed_global_transforms_query: Extract<RemovedComponents<GlobalTransform>>,
11811195
mut removed_meshes_query: Extract<RemovedComponents<Mesh3d>>,
1182-
cameras_query: Extract<Query<(), (With<Camera>, With<GpuCulling>)>>,
1196+
cameras_query: Extract<Query<(), (With<Camera>, Without<NoIndirectDrawing>)>>,
11831197
) {
11841198
let any_gpu_culling = !cameras_query.is_empty();
11851199
for render_mesh_instance_queue in render_mesh_instance_queues.iter_mut() {
@@ -1209,6 +1223,7 @@ pub fn extract_meshes_for_gpu_building(
12091223
lightmap,
12101224
aabb,
12111225
mesh,
1226+
no_frustum_culling,
12121227
not_shadow_receiver,
12131228
transmitted_receiver,
12141229
not_shadow_caster,
@@ -1231,6 +1246,7 @@ pub fn extract_meshes_for_gpu_building(
12311246
let mesh_flags = MeshFlags::from_components(
12321247
transform,
12331248
lod_index,
1249+
no_frustum_culling,
12341250
not_shadow_receiver,
12351251
transmitted_receiver,
12361252
);

crates/bevy_pbr/src/render/mesh_preprocess.wgsl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// mesh's transform on the previous frame and writes it into the `MeshUniform`
88
// so that TAA works.
99

10-
#import bevy_pbr::mesh_types::Mesh
10+
#import bevy_pbr::mesh_types::{Mesh, MESH_FLAGS_NO_FRUSTUM_CULLING_BIT}
1111
#import bevy_render::maths
1212
#import bevy_render::view::View
1313

@@ -145,13 +145,15 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3<u32>) {
145145

146146
// Cull if necessary.
147147
#ifdef FRUSTUM_CULLING
148-
let aabb_center = mesh_culling_data[input_index].aabb_center.xyz;
149-
let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz;
150-
151-
// Do an OBB-based frustum cull.
152-
let model_center = world_from_local * vec4(aabb_center, 1.0);
153-
if (!view_frustum_intersects_obb(world_from_local, model_center, aabb_half_extents)) {
154-
return;
148+
if ((current_input[input_index].flags & MESH_FLAGS_NO_FRUSTUM_CULLING_BIT) == 0u) {
149+
let aabb_center = mesh_culling_data[input_index].aabb_center.xyz;
150+
let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz;
151+
152+
// Do an OBB-based frustum cull.
153+
let model_center = world_from_local * vec4(aabb_center, 1.0);
154+
if (!view_frustum_intersects_obb(world_from_local, model_center, aabb_half_extents)) {
155+
return;
156+
}
155157
}
156158
#endif
157159

crates/bevy_pbr/src/render/mesh_types.wgsl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ struct MorphWeights {
3737

3838
// [2^0, 2^16)
3939
const MESH_FLAGS_VISIBILITY_RANGE_INDEX_BITS: u32 = 65535u;
40+
// 2^28
41+
const MESH_FLAGS_NO_FRUSTUM_CULLING_BIT: u32 = 268435456u;
4042
// 2^29
4143
const MESH_FLAGS_SHADOW_RECEIVER_BIT: u32 = 536870912u;
4244
// 2^30

0 commit comments

Comments
 (0)