Skip to content

Commit

Permalink
Implement GPU frustum culling. (#12889)
Browse files Browse the repository at this point in the history
This commit implements opt-in GPU frustum culling, built on top of the
infrastructure in #12773. To
enable it on a camera, add the `GpuCulling` component to it. To
additionally disable CPU frustum culling, add the `NoCpuCulling`
component. Note that adding `GpuCulling` without `NoCpuCulling`
*currently* does nothing useful. The reason why `GpuCulling` doesn't
automatically imply `NoCpuCulling` is that I intend to follow this patch
up with GPU two-phase occlusion culling, and CPU frustum culling plus
GPU occlusion culling seems like a very commonly-desired mode.

Adding the `GpuCulling` component to a view puts that view into
*indirect mode*. This mode makes all drawcalls indirect, relying on the
mesh preprocessing shader to allocate instances dynamically. In indirect
mode, the `PreprocessWorkItem` `output_index` points not to a
`MeshUniform` instance slot but instead to a set of `wgpu`
`IndirectParameters`, from which it allocates an instance slot
dynamically if frustum culling succeeds. Batch building has been updated
to allocate and track indirect parameter slots, and the AABBs are now
supplied to the GPU as `MeshCullingData`.

A small amount of code relating to the frustum culling has been borrowed
from meshlets and moved into `maths.wgsl`. Note that standard Bevy
frustum culling uses AABBs, while meshlets use bounding spheres; this
means that not as much code can be shared as one might think.

This patch doesn't provide any way to perform GPU culling on shadow
maps, to avoid making this patch bigger than it already is. That can be
a followup.

## Changelog

### Added

* Frustum culling can now optionally be done on the GPU. To enable it,
add the `GpuCulling` component to a camera.
* To disable CPU frustum culling, add `NoCpuCulling` to a camera. Note
that `GpuCulling` doesn't automatically imply `NoCpuCulling`.
  • Loading branch information
pcwalton authored Apr 28, 2024
1 parent 45bb625 commit 16531fb
Show file tree
Hide file tree
Showing 31 changed files with 1,706 additions and 480 deletions.
13 changes: 6 additions & 7 deletions crates/bevy_core_pipeline/src/core_2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ use bevy_render::{
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
render_phase::{
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
SortedPhaseItem, SortedRenderPhase,
PhaseItemExtraIndex, SortedPhaseItem, SortedRenderPhase,
},
render_resource::CachedRenderPipelineId,
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
use nonmax::NonMaxU32;

use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};

Expand Down Expand Up @@ -91,7 +90,7 @@ pub struct Transparent2d {
pub pipeline: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
pub extra_index: PhaseItemExtraIndex,
}

impl PhaseItem for Transparent2d {
Expand All @@ -116,13 +115,13 @@ impl PhaseItem for Transparent2d {
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand Down
57 changes: 28 additions & 29 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ use bevy_render::{
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
render_phase::{
sort_phase_system, BinnedPhaseItem, BinnedRenderPhase, CachedRenderPipelinePhaseItem,
DrawFunctionId, DrawFunctions, PhaseItem, SortedPhaseItem, SortedRenderPhase,
DrawFunctionId, DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem,
SortedRenderPhase,
},
render_resource::{
BindGroupId, CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor,
Expand All @@ -70,7 +71,6 @@ use bevy_render::{
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
use bevy_utils::{tracing::warn, HashMap};
use nonmax::NonMaxU32;

use crate::{
core_3d::main_transmissive_pass_3d_node::MainTransmissivePass3dNode,
Expand Down Expand Up @@ -183,8 +183,9 @@ pub struct Opaque3d {
pub representative_entity: Entity,
/// The ranges of instances.
pub batch_range: Range<u32>,
/// The dynamic offset.
pub dynamic_offset: Option<NonMaxU32>,
/// An extra index, which is either a dynamic offset or an index in the
/// indirect parameters list.
pub extra_index: PhaseItemExtraIndex,
}

/// Data that must be identical in order to batch meshes together.
Expand Down Expand Up @@ -229,14 +230,12 @@ impl PhaseItem for Opaque3d {
&mut self.batch_range
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand All @@ -248,13 +247,13 @@ impl BinnedPhaseItem for Opaque3d {
key: Self::BinKey,
representative_entity: Entity,
batch_range: Range<u32>,
dynamic_offset: Option<NonMaxU32>,
extra_index: PhaseItemExtraIndex,
) -> Self {
Opaque3d {
key,
representative_entity,
batch_range,
dynamic_offset,
extra_index,
}
}
}
Expand All @@ -270,7 +269,7 @@ pub struct AlphaMask3d {
pub key: OpaqueNoLightmap3dBinKey,
pub representative_entity: Entity,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
pub extra_index: PhaseItemExtraIndex,
}

impl PhaseItem for AlphaMask3d {
Expand All @@ -295,13 +294,13 @@ impl PhaseItem for AlphaMask3d {
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand All @@ -313,13 +312,13 @@ impl BinnedPhaseItem for AlphaMask3d {
key: Self::BinKey,
representative_entity: Entity,
batch_range: Range<u32>,
dynamic_offset: Option<NonMaxU32>,
extra_index: PhaseItemExtraIndex,
) -> Self {
Self {
key,
representative_entity,
batch_range,
dynamic_offset,
extra_index,
}
}
}
Expand All @@ -337,7 +336,7 @@ pub struct Transmissive3d {
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
pub extra_index: PhaseItemExtraIndex,
}

impl PhaseItem for Transmissive3d {
Expand Down Expand Up @@ -373,13 +372,13 @@ impl PhaseItem for Transmissive3d {
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand Down Expand Up @@ -411,7 +410,7 @@ pub struct Transparent3d {
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
pub extra_index: PhaseItemExtraIndex,
}

impl PhaseItem for Transparent3d {
Expand All @@ -436,13 +435,13 @@ impl PhaseItem for Transparent3d {
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand Down
34 changes: 18 additions & 16 deletions crates/bevy_core_pipeline/src/deferred/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use std::ops::Range;

use bevy_ecs::prelude::*;
use bevy_render::{
render_phase::{BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
render_phase::{
BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem,
PhaseItemExtraIndex,
},
render_resource::{CachedRenderPipelineId, TextureFormat},
};
use nonmax::NonMaxU32;

use crate::prepass::OpaqueNoLightmap3dBinKey;

Expand All @@ -26,7 +28,7 @@ pub struct Opaque3dDeferred {
pub key: OpaqueNoLightmap3dBinKey,
pub representative_entity: Entity,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
pub extra_index: PhaseItemExtraIndex,
}

impl PhaseItem for Opaque3dDeferred {
Expand All @@ -51,13 +53,13 @@ impl PhaseItem for Opaque3dDeferred {
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand All @@ -69,13 +71,13 @@ impl BinnedPhaseItem for Opaque3dDeferred {
key: Self::BinKey,
representative_entity: Entity,
batch_range: Range<u32>,
dynamic_offset: Option<NonMaxU32>,
extra_index: PhaseItemExtraIndex,
) -> Self {
Self {
key,
representative_entity,
batch_range,
dynamic_offset,
extra_index,
}
}
}
Expand All @@ -96,7 +98,7 @@ pub struct AlphaMask3dDeferred {
pub key: OpaqueNoLightmap3dBinKey,
pub representative_entity: Entity,
pub batch_range: Range<u32>,
pub dynamic_offset: Option<NonMaxU32>,
pub extra_index: PhaseItemExtraIndex,
}

impl PhaseItem for AlphaMask3dDeferred {
Expand All @@ -121,13 +123,13 @@ impl PhaseItem for AlphaMask3dDeferred {
}

#[inline]
fn dynamic_offset(&self) -> Option<NonMaxU32> {
self.dynamic_offset
fn extra_index(&self) -> PhaseItemExtraIndex {
self.extra_index
}

#[inline]
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32> {
&mut self.dynamic_offset
fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
(&mut self.batch_range, &mut self.extra_index)
}
}

Expand All @@ -138,13 +140,13 @@ impl BinnedPhaseItem for AlphaMask3dDeferred {
key: Self::BinKey,
representative_entity: Entity,
batch_range: Range<u32>,
dynamic_offset: Option<NonMaxU32>,
extra_index: PhaseItemExtraIndex,
) -> Self {
Self {
key,
representative_entity,
batch_range,
dynamic_offset,
extra_index,
}
}
}
Expand Down
Loading

0 comments on commit 16531fb

Please sign in to comment.