Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turn on GPU culling automatically if the target platform supports it. #16670

Closed
wants to merge 10 commits into from
31 changes: 7 additions & 24 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,9 @@ pub const DEPTH_TEXTURE_SAMPLING_SUPPORTED: bool = true;

use core::ops::Range;

use bevy_render::batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport};
use bevy_render::batching::gpu_preprocessing::GpuPreprocessingSupport;
use bevy_render::mesh::allocator::SlabId;
use bevy_render::render_phase::PhaseItemBinKey;
use bevy_render::view::GpuCulling;
pub use camera_3d::*;
pub use main_opaque_pass_3d_node::*;
pub use main_transparent_pass_3d_node::*;
Expand Down Expand Up @@ -568,24 +567,20 @@ pub fn extract_core_3d_camera_phases(
mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
cameras_3d: Extract<Query<(RenderEntity, &Camera, Has<GpuCulling>), With<Camera3d>>>,
cameras_3d: Extract<Query<(RenderEntity, &Camera), With<Camera3d>>>,
mut live_entities: Local<EntityHashSet>,
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
) {
live_entities.clear();

for (entity, camera, has_gpu_culling) in &cameras_3d {
for (entity, camera) in &cameras_3d {
if !camera.is_active {
continue;
}

// If GPU culling is in use, use it (and indirect mode); otherwise, just
// preprocess the meshes.
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if has_gpu_culling {
GpuPreprocessingMode::Culling
} else {
GpuPreprocessingMode::PreprocessingOnly
});
let gpu_preprocessing_mode = gpu_preprocessing_support.max_supported_mode;

opaque_3d_phases.insert_or_clear(entity, gpu_preprocessing_mode);
alpha_mask_3d_phases.insert_or_clear(entity, gpu_preprocessing_mode);
Expand Down Expand Up @@ -615,7 +610,6 @@ pub fn extract_camera_prepass_phase(
(
RenderEntity,
&Camera,
Has<GpuCulling>,
Has<DepthPrepass>,
Has<NormalPrepass>,
Has<MotionVectorPrepass>,
Expand All @@ -629,27 +623,16 @@ pub fn extract_camera_prepass_phase(
) {
live_entities.clear();

for (
entity,
camera,
gpu_culling,
depth_prepass,
normal_prepass,
motion_vector_prepass,
deferred_prepass,
) in cameras_3d.iter()
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
cameras_3d.iter()
{
if !camera.is_active {
continue;
}

// If GPU culling is in use, use it (and indirect mode); otherwise, just
// preprocess the meshes.
let gpu_preprocessing_mode = gpu_preprocessing_support.min(if gpu_culling {
GpuPreprocessingMode::Culling
} else {
GpuPreprocessingMode::PreprocessingOnly
});
let gpu_preprocessing_mode = gpu_preprocessing_support.max_supported_mode;

if depth_prepass || normal_prepass || motion_vector_prepass {
opaque_3d_prepass_phases.insert_or_clear(entity, gpu_preprocessing_mode);
Expand Down
128 changes: 66 additions & 62 deletions crates/bevy_pbr/src/render/gpu_preprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ use bevy_asset::{load_internal_asset, Handle};
use bevy_ecs::{
component::Component,
entity::Entity,
query::{Has, QueryState, Without},
query::{QueryState, Without},
schedule::{common_conditions::resource_exists, IntoSystemConfigs as _},
system::{lifetimeless::Read, Commands, Res, ResMut, Resource},
world::{FromWorld, World},
};
use bevy_render::{
batching::gpu_preprocessing::{
BatchedInstanceBuffers, GpuPreprocessingSupport, IndirectParameters,
BatchedInstanceBuffers, GpuPreprocessingMode, GpuPreprocessingSupport, IndirectParameters,
IndirectParametersBuffer, PreprocessWorkItem,
},
graph::CameraDriverLabel,
Expand All @@ -33,7 +33,7 @@ use bevy_render::{
SpecializedComputePipeline, SpecializedComputePipelines,
},
renderer::{RenderContext, RenderDevice, RenderQueue},
view::{GpuCulling, ViewUniform, ViewUniformOffset, ViewUniforms},
view::{ViewUniform, ViewUniformOffset, ViewUniforms},
Render, RenderApp, RenderSet,
};
use bevy_utils::tracing::warn;
Expand Down Expand Up @@ -66,12 +66,7 @@ pub struct GpuMeshPreprocessPlugin {
/// The render node for the mesh uniform building pass.
pub struct GpuPreprocessNode {
view_query: QueryState<
(
Entity,
Read<PreprocessBindGroup>,
Read<ViewUniformOffset>,
Has<GpuCulling>,
),
(Entity, Read<PreprocessBindGroup>, Read<ViewUniformOffset>),
Without<SkipGpuPreprocess>,
>,
}
Expand Down Expand Up @@ -190,6 +185,7 @@ impl Node for GpuPreprocessNode {
..
} = world.resource::<BatchedInstanceBuffers<MeshUniform, MeshInputUniform>>();

let gpu_preprocessing_support = world.resource::<GpuPreprocessingSupport>();
let pipeline_cache = world.resource::<PipelineCache>();
let preprocess_pipelines = world.resource::<PreprocessPipelines>();

Expand All @@ -202,9 +198,7 @@ impl Node for GpuPreprocessNode {
});

// Run the compute passes.
for (view, bind_group, view_uniform_offset, gpu_culling) in
self.view_query.iter_manual(world)
{
for (view, bind_group, view_uniform_offset) in self.view_query.iter_manual(world) {
// Grab the index buffer for this view.
let Some(index_buffer) = index_buffers.get(&view) else {
warn!("The preprocessing index buffer wasn't present");
Expand All @@ -213,10 +207,11 @@ impl Node for GpuPreprocessNode {

// Select the right pipeline, depending on whether GPU culling is in
// use.
let maybe_pipeline_id = if gpu_culling {
preprocess_pipelines.gpu_culling.pipeline_id
} else {
preprocess_pipelines.direct.pipeline_id
let maybe_pipeline_id = match gpu_preprocessing_support.max_supported_mode {
GpuPreprocessingMode::Culling => preprocess_pipelines.gpu_culling.pipeline_id,
GpuPreprocessingMode::None | GpuPreprocessingMode::PreprocessingOnly => {
preprocess_pipelines.direct.pipeline_id
}
};

// Fetch the pipeline.
Expand All @@ -235,7 +230,10 @@ impl Node for GpuPreprocessNode {
compute_pass.set_pipeline(preprocess_pipeline);

let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![];
if gpu_culling {
if matches!(
gpu_preprocessing_support.max_supported_mode,
GpuPreprocessingMode::Culling
) {
dynamic_offsets.push(view_uniform_offset.offset);
}
compute_pass.set_bind_group(0, &bind_group.0, &dynamic_offsets);
Expand Down Expand Up @@ -384,6 +382,7 @@ impl PreprocessPipeline {

/// A system that attaches the mesh uniform buffers to the bind groups for the
/// variants of the mesh preprocessing compute shader.
#[allow(clippy::too_many_arguments)]
pub fn prepare_preprocess_bind_groups(
mut commands: Commands,
render_device: Res<RenderDevice>,
Expand All @@ -392,6 +391,7 @@ pub fn prepare_preprocess_bind_groups(
mesh_culling_data_buffer: Res<MeshCullingDataBuffer>,
view_uniforms: Res<ViewUniforms>,
pipelines: Res<PreprocessPipelines>,
gpu_preprocessing_support: Res<GpuPreprocessingSupport>,
) {
// Grab the `BatchedInstanceBuffers`.
let BatchedInstanceBuffers {
Expand Down Expand Up @@ -422,52 +422,56 @@ pub fn prepare_preprocess_bind_groups(
)
.ok();

let bind_group = if index_buffer_vec.gpu_culling {
let (
Some(indirect_parameters_buffer),
Some(mesh_culling_data_buffer),
Some(view_uniforms_binding),
) = (
indirect_parameters_buffer.buffer(),
mesh_culling_data_buffer.buffer(),
view_uniforms.uniforms.binding(),
)
else {
continue;
};
let bind_group = match gpu_preprocessing_support.max_supported_mode {
GpuPreprocessingMode::Culling => {
let (
Some(indirect_parameters_buffer),
Some(mesh_culling_data_buffer),
Some(view_uniforms_binding),
) = (
indirect_parameters_buffer.buffer(),
mesh_culling_data_buffer.buffer(),
view_uniforms.uniforms.binding(),
)
else {
continue;
};

PreprocessBindGroup(render_device.create_bind_group(
"preprocess_gpu_culling_bind_group",
&pipelines.gpu_culling.bind_group_layout,
&BindGroupEntries::sequential((
current_input_buffer.as_entire_binding(),
previous_input_buffer.as_entire_binding(),
BindingResource::Buffer(BufferBinding {
buffer: index_buffer,
offset: 0,
size: index_buffer_size,
}),
data_buffer.as_entire_binding(),
indirect_parameters_buffer.as_entire_binding(),
mesh_culling_data_buffer.as_entire_binding(),
view_uniforms_binding,
)),
))
}

PreprocessBindGroup(render_device.create_bind_group(
"preprocess_gpu_culling_bind_group",
&pipelines.gpu_culling.bind_group_layout,
&BindGroupEntries::sequential((
current_input_buffer.as_entire_binding(),
previous_input_buffer.as_entire_binding(),
BindingResource::Buffer(BufferBinding {
buffer: index_buffer,
offset: 0,
size: index_buffer_size,
}),
data_buffer.as_entire_binding(),
indirect_parameters_buffer.as_entire_binding(),
mesh_culling_data_buffer.as_entire_binding(),
view_uniforms_binding,
)),
))
} else {
PreprocessBindGroup(render_device.create_bind_group(
"preprocess_direct_bind_group",
&pipelines.direct.bind_group_layout,
&BindGroupEntries::sequential((
current_input_buffer.as_entire_binding(),
previous_input_buffer.as_entire_binding(),
BindingResource::Buffer(BufferBinding {
buffer: index_buffer,
offset: 0,
size: index_buffer_size,
}),
data_buffer.as_entire_binding(),
)),
))
GpuPreprocessingMode::None | GpuPreprocessingMode::PreprocessingOnly => {
PreprocessBindGroup(render_device.create_bind_group(
"preprocess_direct_bind_group",
&pipelines.direct.bind_group_layout,
&BindGroupEntries::sequential((
current_input_buffer.as_entire_binding(),
previous_input_buffer.as_entire_binding(),
BindingResource::Buffer(BufferBinding {
buffer: index_buffer,
offset: 0,
size: index_buffer_size,
}),
data_buffer.as_entire_binding(),
)),
))
}
};

commands.entity(*view).insert(bind_group);
Expand Down
25 changes: 3 additions & 22 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ use bevy_ecs::{
};
use bevy_math::{ops, Mat4, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_render::{
batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport},
camera::SortedCameras,
batching::gpu_preprocessing::GpuPreprocessingSupport, camera::SortedCameras,
mesh::allocator::MeshAllocator,
view::GpuCulling,
};
use bevy_render::{
diagnostic::RecordDiagnostics,
Expand Down Expand Up @@ -687,7 +685,6 @@ pub fn prepare_lights(
&ExtractedView,
&ExtractedClusterConfig,
Option<&RenderLayers>,
Has<GpuCulling>,
),
With<Camera3d>,
>,
Expand Down Expand Up @@ -1096,19 +1093,15 @@ 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, has_gpu_culling) in sorted_cameras
for (entity, extracted_view, clusters, maybe_layers) in sorted_cameras
.0
.iter()
.filter_map(|sorted_camera| views.get(sorted_camera.entity).ok())
{
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 gpu_preprocessing_mode = gpu_preprocessing_support.max_supported_mode;

let is_orthographic = extracted_view.clip_from_view.w_axis.w == 1.0;
let cluster_factors_zw = calculate_cluster_factors(
Expand Down Expand Up @@ -1237,10 +1230,6 @@ 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 {
Expand Down Expand Up @@ -1329,10 +1318,6 @@ 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 {
Expand Down Expand Up @@ -1464,10 +1449,6 @@ pub fn prepare_lights(
},
));

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
Expand Down
Loading
Loading