From 92cce839d41b5fb01fb230167f1be43654478893 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 10 Dec 2024 15:01:36 -0800 Subject: [PATCH 1/6] Make indirect drawing opt-out instead of opt-in, enabling multidraw by default. 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**. --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 14 ++++----- crates/bevy_pbr/src/render/gpu_preprocess.rs | 12 ++++---- crates/bevy_pbr/src/render/light.rs | 20 ++++++------- crates/bevy_pbr/src/render/mesh.rs | 22 ++++++++++++-- .../bevy_pbr/src/render/mesh_preprocess.wgsl | 18 +++++++----- crates/bevy_pbr/src/render/mesh_types.wgsl | 2 ++ .../src/batching/gpu_preprocessing.rs | 29 +++++++++++-------- crates/bevy_render/src/camera/camera.rs | 25 ++++++++-------- crates/bevy_render/src/view/mod.rs | 13 ++++++++- examples/stress_tests/many_cubes.rs | 10 +++---- 10 files changed, 100 insertions(+), 65 deletions(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index d7e9b446e5fb3..c4559d3e5b663 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -69,7 +69,7 @@ use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, mesh::allocator::SlabId, render_phase::PhaseItemBinKey, - view::GpuCulling, + view::NoIndirectDrawing, }; pub use camera_3d::*; pub use main_opaque_pass_3d_node::*; @@ -569,20 +569,20 @@ pub fn extract_core_3d_camera_phases( mut alpha_mask_3d_phases: ResMut>, mut transmissive_3d_phases: ResMut>, mut transparent_3d_phases: ResMut>, - cameras_3d: Extract), With>>, + cameras_3d: Extract), With>>, mut live_entities: Local, gpu_preprocessing_support: Res, ) { live_entities.clear(); - for (entity, camera, has_gpu_culling) in &cameras_3d { + for (entity, camera, no_indirect_drawing) 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 { + let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing { GpuPreprocessingMode::Culling } else { GpuPreprocessingMode::PreprocessingOnly @@ -616,7 +616,7 @@ pub fn extract_camera_prepass_phase( ( RenderEntity, &Camera, - Has, + Has, Has, Has, Has, @@ -633,7 +633,7 @@ pub fn extract_camera_prepass_phase( for ( entity, camera, - gpu_culling, + no_indirect_drawing, depth_prepass, normal_prepass, motion_vector_prepass, @@ -646,7 +646,7 @@ pub fn extract_camera_prepass_phase( // 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 { + let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing { GpuPreprocessingMode::Culling } else { GpuPreprocessingMode::PreprocessingOnly diff --git a/crates/bevy_pbr/src/render/gpu_preprocess.rs b/crates/bevy_pbr/src/render/gpu_preprocess.rs index dd09438eb706f..434ed64135a2e 100644 --- a/crates/bevy_pbr/src/render/gpu_preprocess.rs +++ b/crates/bevy_pbr/src/render/gpu_preprocess.rs @@ -33,7 +33,7 @@ use bevy_render::{ SpecializedComputePipeline, SpecializedComputePipelines, }, renderer::{RenderContext, RenderDevice, RenderQueue}, - view::{GpuCulling, ViewUniform, ViewUniformOffset, ViewUniforms}, + view::{NoIndirectDrawing, ViewUniform, ViewUniformOffset, ViewUniforms}, Render, RenderApp, RenderSet, }; use bevy_utils::tracing::warn; @@ -70,7 +70,7 @@ pub struct GpuPreprocessNode { Entity, Read, Read, - Has, + Has, ), Without, >, @@ -202,7 +202,7 @@ impl Node for GpuPreprocessNode { }); // Run the compute passes. - for (view, bind_group, view_uniform_offset, gpu_culling) in + for (view, bind_group, view_uniform_offset, no_indirect_drawing) in self.view_query.iter_manual(world) { // Grab the index buffer for this view. @@ -213,7 +213,7 @@ impl Node for GpuPreprocessNode { // Select the right pipeline, depending on whether GPU culling is in // use. - let maybe_pipeline_id = if gpu_culling { + let maybe_pipeline_id = if !no_indirect_drawing { preprocess_pipelines.gpu_culling.pipeline_id } else { preprocess_pipelines.direct.pipeline_id @@ -235,7 +235,7 @@ impl Node for GpuPreprocessNode { compute_pass.set_pipeline(preprocess_pipeline); let mut dynamic_offsets: SmallVec<[u32; 1]> = smallvec![]; - if gpu_culling { + if !no_indirect_drawing { dynamic_offsets.push(view_uniform_offset.offset); } compute_pass.set_bind_group(0, &bind_group.0, &dynamic_offsets); @@ -422,7 +422,7 @@ pub fn prepare_preprocess_bind_groups( ) .ok(); - let bind_group = if index_buffer_vec.gpu_culling { + let bind_group = if !index_buffer_vec.no_indirect_drawing { let ( Some(indirect_parameters_buffer), Some(mesh_culling_data_buffer), diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index e13db2a8bbe18..3705424adfb8b 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -15,7 +15,7 @@ use bevy_render::{ batching::gpu_preprocessing::{GpuPreprocessingMode, GpuPreprocessingSupport}, camera::SortedCameras, mesh::allocator::MeshAllocator, - view::GpuCulling, + view::NoIndirectDrawing, }; use bevy_render::{ diagnostic::RecordDiagnostics, @@ -687,7 +687,7 @@ pub fn prepare_lights( &ExtractedView, &ExtractedClusterConfig, Option<&RenderLayers>, - Has, + Has, ), With, >, @@ -1096,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, has_gpu_culling) in sorted_cameras + for (entity, extracted_view, clusters, maybe_layers, no_indirect_drawing) in sorted_cameras .0 .iter() .filter_map(|sorted_camera| views.get(sorted_camera.entity).ok()) @@ -1104,7 +1104,7 @@ 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 { + let gpu_preprocessing_mode = gpu_preprocessing_support.min(if !no_indirect_drawing { GpuPreprocessingMode::Culling } else { GpuPreprocessingMode::PreprocessingOnly @@ -1237,8 +1237,8 @@ pub fn prepare_lights( }, )); - if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) { - commands.entity(view_light_entity).insert(GpuCulling); + if !matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) { + commands.entity(view_light_entity).insert(NoIndirectDrawing); } view_lights.push(view_light_entity); @@ -1329,8 +1329,8 @@ pub fn prepare_lights( LightEntity::Spot { light_entity }, )); - if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) { - commands.entity(view_light_entity).insert(GpuCulling); + if !matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) { + commands.entity(view_light_entity).insert(NoIndirectDrawing); } view_lights.push(view_light_entity); @@ -1464,8 +1464,8 @@ pub fn prepare_lights( }, )); - if matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) { - commands.entity(view_light_entity).insert(GpuCulling); + if !matches!(gpu_preprocessing_mode, GpuPreprocessingMode::Culling) { + commands.entity(view_light_entity).insert(NoIndirectDrawing); } view_lights.push(view_light_entity); diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index c0f3db33d5f3e..35edf83b944f8 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -37,8 +37,8 @@ use bevy_render::{ renderer::{RenderDevice, RenderQueue}, texture::DefaultImageSampler, view::{ - prepare_view_targets, GpuCulling, RenderVisibilityRanges, ViewTarget, ViewUniformOffset, - ViewVisibility, VisibilityRange, + prepare_view_targets, NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges, + ViewTarget, ViewUniformOffset, ViewVisibility, VisibilityRange, }, Extract, }; @@ -421,6 +421,11 @@ bitflags::bitflags! { /// /// This will be `u16::MAX` if this mesh has no LOD. const LOD_INDEX_MASK = (1 << 16) - 1; + /// Disables frustum culling for this mesh. + /// + /// This corresponds to the + /// [`bevy_render::view::visibility::NoFrustumCulling`] component. + const NO_FRUSTUM_CULLING = 1 << 28; const SHADOW_RECEIVER = 1 << 29; const TRANSMITTED_SHADOW_RECEIVER = 1 << 30; // Indicates the sign of the determinant of the 3x3 model matrix. If the sign is positive, @@ -435,6 +440,7 @@ impl MeshFlags { fn from_components( transform: &GlobalTransform, lod_index: Option, + no_frustum_culling: bool, not_shadow_receiver: bool, transmitted_receiver: bool, ) -> MeshFlags { @@ -443,6 +449,9 @@ impl MeshFlags { } else { MeshFlags::SHADOW_RECEIVER }; + if no_frustum_culling { + mesh_flags |= MeshFlags::NO_FRUSTUM_CULLING; + } if transmitted_receiver { mesh_flags |= MeshFlags::TRANSMITTED_SHADOW_RECEIVER; } @@ -1046,6 +1055,7 @@ pub fn extract_meshes_for_cpu_building( &GlobalTransform, Option<&PreviousGlobalTransform>, &Mesh3d, + Has, Has, Has, Has, @@ -1063,6 +1073,7 @@ pub fn extract_meshes_for_cpu_building( transform, previous_transform, mesh, + no_frustum_culling, not_shadow_receiver, transmitted_receiver, not_shadow_caster, @@ -1084,6 +1095,7 @@ pub fn extract_meshes_for_cpu_building( let mesh_flags = MeshFlags::from_components( transform, lod_index, + no_frustum_culling, not_shadow_receiver, transmitted_receiver, ); @@ -1155,6 +1167,7 @@ pub fn extract_meshes_for_gpu_building( Option<&Lightmap>, Option<&Aabb>, &Mesh3d, + Has, Has, Has, Has, @@ -1168,6 +1181,7 @@ pub fn extract_meshes_for_gpu_building( Changed, Changed, Changed, + Changed, Changed, Changed, Changed, @@ -1179,7 +1193,7 @@ pub fn extract_meshes_for_gpu_building( mut removed_visibilities_query: Extract>, mut removed_global_transforms_query: Extract>, mut removed_meshes_query: Extract>, - cameras_query: Extract, With)>>, + cameras_query: Extract, Without)>>, ) { let any_gpu_culling = !cameras_query.is_empty(); for render_mesh_instance_queue in render_mesh_instance_queues.iter_mut() { @@ -1209,6 +1223,7 @@ pub fn extract_meshes_for_gpu_building( lightmap, aabb, mesh, + no_frustum_culling, not_shadow_receiver, transmitted_receiver, not_shadow_caster, @@ -1231,6 +1246,7 @@ pub fn extract_meshes_for_gpu_building( let mesh_flags = MeshFlags::from_components( transform, lod_index, + no_frustum_culling, not_shadow_receiver, transmitted_receiver, ); diff --git a/crates/bevy_pbr/src/render/mesh_preprocess.wgsl b/crates/bevy_pbr/src/render/mesh_preprocess.wgsl index 3300c4d925d6b..9836077a66614 100644 --- a/crates/bevy_pbr/src/render/mesh_preprocess.wgsl +++ b/crates/bevy_pbr/src/render/mesh_preprocess.wgsl @@ -7,7 +7,7 @@ // mesh's transform on the previous frame and writes it into the `MeshUniform` // so that TAA works. -#import bevy_pbr::mesh_types::Mesh +#import bevy_pbr::mesh_types::{Mesh, MESH_FLAGS_NO_FRUSTUM_CULLING_BIT} #import bevy_render::maths #import bevy_render::view::View @@ -145,13 +145,15 @@ fn main(@builtin(global_invocation_id) global_invocation_id: vec3) { // Cull if necessary. #ifdef FRUSTUM_CULLING - let aabb_center = mesh_culling_data[input_index].aabb_center.xyz; - let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz; - - // Do an OBB-based frustum cull. - let model_center = world_from_local * vec4(aabb_center, 1.0); - if (!view_frustum_intersects_obb(world_from_local, model_center, aabb_half_extents)) { - return; + if ((current_input[input_index].flags & MESH_FLAGS_NO_FRUSTUM_CULLING_BIT) == 0u) { + let aabb_center = mesh_culling_data[input_index].aabb_center.xyz; + let aabb_half_extents = mesh_culling_data[input_index].aabb_half_extents.xyz; + + // Do an OBB-based frustum cull. + let model_center = world_from_local * vec4(aabb_center, 1.0); + if (!view_frustum_intersects_obb(world_from_local, model_center, aabb_half_extents)) { + return; + } } #endif diff --git a/crates/bevy_pbr/src/render/mesh_types.wgsl b/crates/bevy_pbr/src/render/mesh_types.wgsl index f94074d813465..9a5e064c825a5 100644 --- a/crates/bevy_pbr/src/render/mesh_types.wgsl +++ b/crates/bevy_pbr/src/render/mesh_types.wgsl @@ -37,6 +37,8 @@ struct MorphWeights { // [2^0, 2^16) const MESH_FLAGS_VISIBILITY_RANGE_INDEX_BITS: u32 = 65535u; +// 2^28 +const MESH_FLAGS_NO_FRUSTUM_CULLING_BIT: u32 = 268435456u; // 2^29 const MESH_FLAGS_SHADOW_RECEIVER_BIT: u32 = 536870912u; // 2^30 diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index cdf0578855956..431863d639105 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -24,7 +24,7 @@ use crate::{ }, render_resource::{BufferVec, GpuArrayBufferable, RawBufferVec, UninitBufferVec}, renderer::{RenderAdapter, RenderDevice, RenderQueue}, - view::{ExtractedView, GpuCulling, ViewTarget}, + view::{ExtractedView, NoIndirectDrawing, ViewTarget}, Render, RenderApp, RenderSet, }; @@ -247,8 +247,8 @@ where pub struct PreprocessWorkItemBuffer { /// The buffer of work items. pub buffer: BufferVec, - /// True if we're using GPU culling. - pub gpu_culling: bool, + /// True if we're drawing directly instead of indirectly. + pub no_indirect_drawing: bool, } /// One invocation of the preprocessing shader: i.e. one mesh instance in a @@ -529,7 +529,7 @@ pub fn batch_and_prepare_sorted_render_phase( gpu_array_buffer: ResMut>, mut indirect_parameters_buffer: ResMut, mut sorted_render_phases: ResMut>, - mut views: Query<(Entity, Has), With>, + mut views: Query<(Entity, Has), With>, system_param_item: StaticSystemParam, ) where I: CachedRenderPipelinePhaseItem + SortedPhaseItem, @@ -542,7 +542,7 @@ pub fn batch_and_prepare_sorted_render_phase( .. } = gpu_array_buffer.into_inner(); - for (view, gpu_culling) in &mut views { + for (view, no_indirect_drawing) in &mut views { let Some(phase) = sorted_render_phases.get_mut(&view) else { continue; }; @@ -553,7 +553,7 @@ pub fn batch_and_prepare_sorted_render_phase( .entry(view) .or_insert_with(|| PreprocessWorkItemBuffer { buffer: BufferVec::new(BufferUsages::STORAGE), - gpu_culling, + no_indirect_drawing, }); // Walk through the list of phase items, building up batches as we go. @@ -604,7 +604,7 @@ pub fn batch_and_prepare_sorted_render_phase( } // Start a new batch. - let indirect_parameters_index = if gpu_culling { + let indirect_parameters_index = if !no_indirect_drawing { GFBD::get_batch_indirect_parameters_index( &system_param_item, &mut indirect_parameters_buffer, @@ -647,7 +647,7 @@ pub fn batch_and_prepare_binned_render_phase( gpu_array_buffer: ResMut>, mut indirect_parameters_buffer: ResMut, mut binned_render_phases: ResMut>, - mut views: Query<(Entity, Has), With>, + mut views: Query<(Entity, Has), With>, param: StaticSystemParam, ) where BPI: BinnedPhaseItem, @@ -661,7 +661,7 @@ pub fn batch_and_prepare_binned_render_phase( .. } = gpu_array_buffer.into_inner(); - for (view, gpu_culling) in &mut views { + for (view, no_indirect_drawing) in &mut views { let Some(phase) = binned_render_phases.get_mut(&view) else { continue; }; @@ -673,7 +673,7 @@ pub fn batch_and_prepare_binned_render_phase( .entry(view) .or_insert_with(|| PreprocessWorkItemBuffer { buffer: BufferVec::new(BufferUsages::STORAGE), - gpu_culling, + no_indirect_drawing, }); // Prepare batchables. @@ -697,6 +697,7 @@ pub fn batch_and_prepare_binned_render_phase( match batch { Some(ref mut batch) => { + // Append to the current batch. batch.instance_range.end = output_index + 1; work_item_buffer.buffer.push(PreprocessWorkItem { input_index: input_index.into(), @@ -710,7 +711,8 @@ pub fn batch_and_prepare_binned_render_phase( }); } - None if gpu_culling => { + None if !no_indirect_drawing => { + // Start a new batch, in indirect mode. let indirect_parameters_index = GFBD::get_batch_indirect_parameters_index( &system_param_item, &mut indirect_parameters_buffer, @@ -731,6 +733,7 @@ pub fn batch_and_prepare_binned_render_phase( } None => { + // Start a new batch, in direct mode. work_item_buffer.buffer.push(PreprocessWorkItem { input_index: input_index.into(), output_index, @@ -779,7 +782,9 @@ pub fn batch_and_prepare_binned_render_phase( }; let output_index = data_buffer.add() as u32; - if gpu_culling { + if !no_indirect_drawing { + // We're in indirect mode, so add an indirect parameters + // index. let indirect_parameters_index = GFBD::get_batch_indirect_parameters_index( &system_param_item, &mut indirect_parameters_buffer, diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 3c375d935cd72..849beccc439d7 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -9,7 +9,7 @@ use crate::{ sync_world::{RenderEntity, SyncToRenderWorld}, texture::GpuImage, view::{ - ColorGrading, ExtractedView, ExtractedWindows, GpuCulling, Msaa, RenderLayers, + ColorGrading, ExtractedView, ExtractedWindows, Msaa, NoIndirectDrawing, RenderLayers, RenderVisibleEntities, ViewUniformOffset, Visibility, VisibleEntities, }, Extract, @@ -32,7 +32,7 @@ use bevy_math::{ops, vec2, Dir3, Mat4, Ray3d, Rect, URect, UVec2, UVec4, Vec2, V use bevy_reflect::prelude::*; use bevy_render_macros::ExtractComponent; use bevy_transform::components::{GlobalTransform, Transform}; -use bevy_utils::{tracing::warn, warn_once, HashMap, HashSet}; +use bevy_utils::{tracing::warn, HashMap, HashSet}; use bevy_window::{ NormalizedWindowRef, PrimaryWindow, Window, WindowCreated, WindowRef, WindowResized, WindowScaleFactorChanged, @@ -1033,7 +1033,7 @@ pub fn extract_cameras( Option<&TemporalJitter>, Option<&RenderLayers>, Option<&Projection>, - Has, + Has, )>, >, primary_window: Extract>>, @@ -1053,7 +1053,7 @@ pub fn extract_cameras( temporal_jitter, render_layers, projection, - gpu_culling, + no_indirect_drawing, ) in query.iter() { if !camera.is_active { @@ -1064,7 +1064,7 @@ pub fn extract_cameras( TemporalJitter, RenderLayers, Projection, - GpuCulling, + NoIndirectDrawing, ViewUniformOffset, )>(); continue; @@ -1156,14 +1156,13 @@ pub fn extract_cameras( commands.insert(perspective.clone()); } - if gpu_culling { - if gpu_preprocessing_support.max_supported_mode == GpuPreprocessingMode::Culling { - commands.insert(GpuCulling); - } else { - warn_once!( - "GPU culling isn't supported on this platform; ignoring `GpuCulling`." - ); - } + if no_indirect_drawing + || !matches!( + gpu_preprocessing_support.max_supported_mode, + GpuPreprocessingMode::Culling + ) + { + commands.insert(NoIndirectDrawing); } }; } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index a626a17d1d592..06e2db79f28df 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -620,8 +620,19 @@ impl From for ColorGradingUniform { } } +/// Add this component to a camera to disable *indirect mode*. +/// +/// Indirect mode, automatically enabled on supported hardware, allows Bevy to +/// offload transform and cull operations to the GPU, reducing CPU overhead. +/// Doing this, however, reduces the amount of control that your app has over +/// instancing decisions. In certain circumstances, you may want to disable +/// indirect drawing so that your app can manually instance meshes as it sees +/// fit. See the `custom_shader_instancing` example. +/// +/// The vast majority of applications will not need to use this component, as it +/// generally reduces rendering performance. #[derive(Component)] -pub struct GpuCulling; +pub struct NoIndirectDrawing; #[derive(Component)] pub struct NoCpuCulling; diff --git a/examples/stress_tests/many_cubes.rs b/examples/stress_tests/many_cubes.rs index 2853bd5fa0fe0..fe8e8b45074df 100644 --- a/examples/stress_tests/many_cubes.rs +++ b/examples/stress_tests/many_cubes.rs @@ -20,7 +20,7 @@ use bevy::{ batching::NoAutomaticBatching, render_asset::RenderAssetUsages, render_resource::{Extent3d, TextureDimension, TextureFormat}, - view::{GpuCulling, NoCpuCulling, NoFrustumCulling}, + view::{NoCpuCulling, NoFrustumCulling, NoIndirectDrawing}, }, window::{PresentMode, WindowResolution}, winit::{UpdateMode, WinitSettings}, @@ -59,9 +59,9 @@ struct Args { #[argh(switch)] no_automatic_batching: bool, - /// whether to enable GPU culling. + /// whether to disable indirect drawing. #[argh(switch)] - gpu_culling: bool, + no_indirect_drawing: bool, /// whether to disable CPU culling. #[argh(switch)] @@ -176,8 +176,8 @@ fn setup( // camera let mut camera = commands.spawn(Camera3d::default()); - if args.gpu_culling { - camera.insert(GpuCulling); + if args.no_indirect_drawing { + camera.insert(NoIndirectDrawing); } if args.no_cpu_culling { camera.insert(NoCpuCulling); From 923ed30c4867f636ed5393ab1b868c1be5f119cf Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 10 Dec 2024 16:02:57 -0800 Subject: [PATCH 2/6] Fix docs --- crates/bevy_render/src/batching/gpu_preprocessing.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index 431863d639105..0a543160e3c9e 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -101,12 +101,13 @@ pub enum GpuPreprocessingMode { /// GPU preprocessing is in use, but GPU culling isn't. /// - /// This is used by default. + /// This is used when the [`NoIndirectDrawing`] component is present on the + /// camera. PreprocessingOnly, /// Both GPU preprocessing and GPU culling are in use. /// - /// This is used when the [`GpuCulling`] component is present on the camera. + /// This is used by default. Culling, } From bc167f370beb0d1923dbb6c8890aa8393d10cf35 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 11 Dec 2024 01:15:39 -0800 Subject: [PATCH 3/6] Add missing component to `custom_shader_instancing` --- examples/shader/custom_shader_instancing.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/shader/custom_shader_instancing.rs b/examples/shader/custom_shader_instancing.rs index 385b4875d6e34..24921de09454e 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -73,6 +73,10 @@ fn setup(mut commands: Commands, mut meshes: ResMut>) { commands.spawn(( Camera3d::default(), Transform::from_xyz(0.0, 0.0, 15.0).looking_at(Vec3::ZERO, Vec3::Y), + // We need this component because we use `draw_indexed` and `draw` + // instead of `draw_indirect_indexed` and `draw_indirect` in + // `DrawMeshInstanced::render`. + NoIndirectDrawing, )); } From f90271e8902dddae1682026b117d1523a77b96b2 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 11 Dec 2024 01:18:36 -0800 Subject: [PATCH 4/6] Add missing component harder --- examples/shader/custom_shader_instancing.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/shader/custom_shader_instancing.rs b/examples/shader/custom_shader_instancing.rs index 24921de09454e..3050cbe4cd728 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -34,6 +34,7 @@ use bevy::{ Render, RenderApp, RenderSet, }, }; +use bevy_render::view::NoIndirectDrawing; use bytemuck::{Pod, Zeroable}; /// This example uses a shader source file from the assets subdirectory From 02e4450bc91aaa717c45704ccb711c66f6a1283b Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 11 Dec 2024 01:30:13 -0800 Subject: [PATCH 5/6] Stop using `bevy_render` in example --- examples/shader/custom_shader_instancing.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/shader/custom_shader_instancing.rs b/examples/shader/custom_shader_instancing.rs index 3050cbe4cd728..2809c7d9944e5 100644 --- a/examples/shader/custom_shader_instancing.rs +++ b/examples/shader/custom_shader_instancing.rs @@ -30,11 +30,10 @@ use bevy::{ render_resource::*, renderer::RenderDevice, sync_world::MainEntity, - view::{ExtractedView, NoFrustumCulling}, + view::{ExtractedView, NoFrustumCulling, NoIndirectDrawing}, Render, RenderApp, RenderSet, }, }; -use bevy_render::view::NoIndirectDrawing; use bytemuck::{Pod, Zeroable}; /// This example uses a shader source file from the assets subdirectory From a46a298cf60be545dd9d31d9192658e1b1191018 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 11 Dec 2024 21:27:14 -0800 Subject: [PATCH 6/6] Try to fix WebGPU --- crates/bevy_render/src/batching/gpu_preprocessing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index 7b4890325eb1a..07379443a4081 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -383,7 +383,7 @@ impl FromWorld for GpuPreprocessingSupport { GpuPreprocessingMode::None } else if !device .features() - .contains(Features::INDIRECT_FIRST_INSTANCE) || + .contains(Features::INDIRECT_FIRST_INSTANCE | Features::MULTI_DRAW_INDIRECT) || !adapter.get_downlevel_capabilities().flags.contains( DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW) {