Skip to content

Commit a900f68

Browse files
Update the prepass shaders and fix the batching logic for bindless and multidraw. (#16755)
This commit resolves most of the failures seen in #16670. It contains two major fixes: 1. The prepass shaders weren't updated for bindless mode, so they were accessing `material` as a single element instead of as an array. I added the needed `BINDLESS` check. 2. If the mesh didn't support batch set keys (i.e. `get_batch_set_key()` returns `None`), and multidraw was enabled, the batching logic would try to multidraw all the meshes in a bin together instead of disabling multidraw. This is because we checked whether the `Option<BatchSetKey>` for the previous batch was equal to the `Option<BatchSetKey>` for the next batch to determine whether objects could be multidrawn together, which would return true if batch set keys were absent, causing an entire bin to be multidrawn together. This patch fixes the logic so that multidraw is only enabled if the batch set keys match *and are `Some`*. Additionally, this commit adds batch key support for bins that use `Opaque3dNoLightmapBinKey`, which in practice means prepasses. Consequently, this patch enables multidraw for the prepass when GPU culling is enabled. When testing this patch, try adding `GpuCulling` to the camera in the `deferred_rendering` and `ssr` examples. You can see that these examples break without this patch and work properly with it. --------- Co-authored-by: Alice Cecile <[email protected]>
1 parent 33a1a55 commit a900f68

File tree

7 files changed

+82
-43
lines changed

7 files changed

+82
-43
lines changed

crates/bevy_core_pipeline/src/core_3d/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ impl PhaseItem for AlphaMask3d {
368368

369369
#[inline]
370370
fn draw_function(&self) -> DrawFunctionId {
371-
self.key.draw_function
371+
self.key.batch_set_key.draw_function
372372
}
373373

374374
#[inline]
@@ -414,7 +414,7 @@ impl BinnedPhaseItem for AlphaMask3d {
414414
impl CachedRenderPipelinePhaseItem for AlphaMask3d {
415415
#[inline]
416416
fn cached_pipeline(&self) -> CachedRenderPipelineId {
417-
self.key.pipeline
417+
self.key.batch_set_key.pipeline
418418
}
419419
}
420420

crates/bevy_core_pipeline/src/deferred/mod.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ impl PhaseItem for Opaque3dDeferred {
4343

4444
#[inline]
4545
fn draw_function(&self) -> DrawFunctionId {
46-
self.key.draw_function
46+
self.key.batch_set_key.draw_function
4747
}
4848

4949
#[inline]
@@ -89,7 +89,7 @@ impl BinnedPhaseItem for Opaque3dDeferred {
8989
impl CachedRenderPipelinePhaseItem for Opaque3dDeferred {
9090
#[inline]
9191
fn cached_pipeline(&self) -> CachedRenderPipelineId {
92-
self.key.pipeline
92+
self.key.batch_set_key.pipeline
9393
}
9494
}
9595

@@ -118,7 +118,7 @@ impl PhaseItem for AlphaMask3dDeferred {
118118

119119
#[inline]
120120
fn draw_function(&self) -> DrawFunctionId {
121-
self.key.draw_function
121+
self.key.batch_set_key.draw_function
122122
}
123123

124124
#[inline]
@@ -163,6 +163,6 @@ impl BinnedPhaseItem for AlphaMask3dDeferred {
163163
impl CachedRenderPipelinePhaseItem for AlphaMask3dDeferred {
164164
#[inline]
165165
fn cached_pipeline(&self) -> CachedRenderPipelineId {
166-
self.key.pipeline
166+
self.key.batch_set_key.pipeline
167167
}
168168
}

crates/bevy_core_pipeline/src/prepass/mod.rs

+23-9
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,13 @@ pub struct Opaque3dPrepass {
149149
pub extra_index: PhaseItemExtraIndex,
150150
}
151151

152-
// TODO: Try interning these.
153-
/// The data used to bin each opaque 3D object in the prepass and deferred pass.
152+
/// Information that must be identical in order to place opaque meshes in the
153+
/// same *batch set* in the prepass and deferred pass.
154+
///
155+
/// A batch set is a set of batches that can be multi-drawn together, if
156+
/// multi-draw is in use.
154157
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
155-
pub struct OpaqueNoLightmap3dBinKey {
158+
pub struct OpaqueNoLightmap3dBatchSetKey {
156159
/// The ID of the GPU pipeline.
157160
pub pipeline: CachedRenderPipelineId,
158161

@@ -163,16 +166,27 @@ pub struct OpaqueNoLightmap3dBinKey {
163166
///
164167
/// In the case of PBR, this is the `MaterialBindGroupIndex`.
165168
pub material_bind_group_index: Option<u32>,
169+
}
170+
171+
// TODO: Try interning these.
172+
/// The data used to bin each opaque 3D object in the prepass and deferred pass.
173+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
174+
pub struct OpaqueNoLightmap3dBinKey {
175+
/// The key of the *batch set*.
176+
///
177+
/// As batches belong to a batch set, meshes in a batch must obviously be
178+
/// able to be placed in a single batch set.
179+
pub batch_set_key: OpaqueNoLightmap3dBatchSetKey,
166180

167181
/// The ID of the asset.
168182
pub asset_id: UntypedAssetId,
169183
}
170184

171185
impl PhaseItemBinKey for OpaqueNoLightmap3dBinKey {
172-
type BatchSetKey = ();
186+
type BatchSetKey = OpaqueNoLightmap3dBatchSetKey;
173187

174188
fn get_batch_set_key(&self) -> Option<Self::BatchSetKey> {
175-
None
189+
Some(self.batch_set_key.clone())
176190
}
177191
}
178192

@@ -188,7 +202,7 @@ impl PhaseItem for Opaque3dPrepass {
188202

189203
#[inline]
190204
fn draw_function(&self) -> DrawFunctionId {
191-
self.key.draw_function
205+
self.key.batch_set_key.draw_function
192206
}
193207

194208
#[inline]
@@ -234,7 +248,7 @@ impl BinnedPhaseItem for Opaque3dPrepass {
234248
impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
235249
#[inline]
236250
fn cached_pipeline(&self) -> CachedRenderPipelineId {
237-
self.key.pipeline
251+
self.key.batch_set_key.pipeline
238252
}
239253
}
240254

@@ -262,7 +276,7 @@ impl PhaseItem for AlphaMask3dPrepass {
262276

263277
#[inline]
264278
fn draw_function(&self) -> DrawFunctionId {
265-
self.key.draw_function
279+
self.key.batch_set_key.draw_function
266280
}
267281

268282
#[inline]
@@ -308,7 +322,7 @@ impl BinnedPhaseItem for AlphaMask3dPrepass {
308322
impl CachedRenderPipelinePhaseItem for AlphaMask3dPrepass {
309323
#[inline]
310324
fn cached_pipeline(&self) -> CachedRenderPipelineId {
311-
self.key.pipeline
325+
self.key.batch_set_key.pipeline
312326
}
313327
}
314328

crates/bevy_pbr/src/material.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use bevy_core_pipeline::{
1414
},
1515
oit::OrderIndependentTransparencySettings,
1616
prepass::{
17-
DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, OpaqueNoLightmap3dBinKey,
17+
DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass,
18+
OpaqueNoLightmap3dBatchSetKey, OpaqueNoLightmap3dBinKey,
1819
},
1920
tonemapping::{DebandDither, Tonemapping},
2021
};
@@ -906,10 +907,12 @@ pub fn queue_material_meshes<M: Material>(
906907
});
907908
} else if material.properties.render_method == OpaqueRendererMethod::Forward {
908909
let bin_key = OpaqueNoLightmap3dBinKey {
909-
draw_function: draw_alpha_mask_pbr,
910-
pipeline: pipeline_id,
910+
batch_set_key: OpaqueNoLightmap3dBatchSetKey {
911+
draw_function: draw_alpha_mask_pbr,
912+
pipeline: pipeline_id,
913+
material_bind_group_index: Some(material.binding.group.0),
914+
},
911915
asset_id: mesh_instance.mesh_asset_id.into(),
912-
material_bind_group_index: Some(material.binding.group.0),
913916
};
914917
alpha_mask_phase.add(
915918
bin_key,

crates/bevy_pbr/src/prepass/mod.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -951,21 +951,25 @@ pub fn queue_prepass_material_meshes<M: Material>(
951951
if deferred {
952952
opaque_deferred_phase.as_mut().unwrap().add(
953953
OpaqueNoLightmap3dBinKey {
954-
draw_function: opaque_draw_deferred,
955-
pipeline: pipeline_id,
954+
batch_set_key: OpaqueNoLightmap3dBatchSetKey {
955+
draw_function: opaque_draw_deferred,
956+
pipeline: pipeline_id,
957+
material_bind_group_index: Some(material.binding.group.0),
958+
},
956959
asset_id: mesh_instance.mesh_asset_id.into(),
957-
material_bind_group_index: Some(material.binding.group.0),
958960
},
959961
(*render_entity, *visible_entity),
960962
BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
961963
);
962964
} else if let Some(opaque_phase) = opaque_phase.as_mut() {
963965
opaque_phase.add(
964966
OpaqueNoLightmap3dBinKey {
965-
draw_function: opaque_draw_prepass,
966-
pipeline: pipeline_id,
967+
batch_set_key: OpaqueNoLightmap3dBatchSetKey {
968+
draw_function: opaque_draw_prepass,
969+
pipeline: pipeline_id,
970+
material_bind_group_index: Some(material.binding.group.0),
971+
},
967972
asset_id: mesh_instance.mesh_asset_id.into(),
968-
material_bind_group_index: Some(material.binding.group.0),
969973
},
970974
(*render_entity, *visible_entity),
971975
BinnedRenderPhaseType::mesh(mesh_instance.should_batch()),
@@ -976,10 +980,12 @@ pub fn queue_prepass_material_meshes<M: Material>(
976980
MeshPipelineKey::MAY_DISCARD => {
977981
if deferred {
978982
let bin_key = OpaqueNoLightmap3dBinKey {
979-
pipeline: pipeline_id,
980-
draw_function: alpha_mask_draw_deferred,
983+
batch_set_key: OpaqueNoLightmap3dBatchSetKey {
984+
draw_function: alpha_mask_draw_deferred,
985+
pipeline: pipeline_id,
986+
material_bind_group_index: Some(material.binding.group.0),
987+
},
981988
asset_id: mesh_instance.mesh_asset_id.into(),
982-
material_bind_group_index: Some(material.binding.group.0),
983989
};
984990
alpha_mask_deferred_phase.as_mut().unwrap().add(
985991
bin_key,
@@ -988,10 +994,12 @@ pub fn queue_prepass_material_meshes<M: Material>(
988994
);
989995
} else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
990996
let bin_key = OpaqueNoLightmap3dBinKey {
991-
pipeline: pipeline_id,
992-
draw_function: alpha_mask_draw_prepass,
997+
batch_set_key: OpaqueNoLightmap3dBatchSetKey {
998+
draw_function: alpha_mask_draw_prepass,
999+
pipeline: pipeline_id,
1000+
material_bind_group_index: Some(material.binding.group.0),
1001+
},
9931002
asset_id: mesh_instance.mesh_asset_id.into(),
994-
material_bind_group_index: Some(material.binding.group.0),
9951003
};
9961004
alpha_mask_phase.add(
9971005
bin_key,

crates/bevy_pbr/src/render/pbr_prepass.wgsl

+15-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
pbr_functions,
77
pbr_functions::SampleBias,
88
prepass_io,
9+
mesh_bindings::mesh,
910
mesh_view_bindings::view,
1011
}
1112

@@ -28,6 +29,15 @@ fn fragment(
2829
let is_front = true;
2930
#else // MESHLET_MESH_MATERIAL_PASS
3031

32+
#ifdef BINDLESS
33+
let slot = mesh[in.instance_index].material_bind_group_slot;
34+
let flags = pbr_bindings::material[slot].flags;
35+
let uv_transform = pbr_bindings::material[slot].uv_transform;
36+
#else // BINDLESS
37+
let flags = pbr_bindings::material.flags;
38+
let uv_transform = pbr_bindings::material.uv_transform;
39+
#endif // BINDLESS
40+
3141
// If we're in the crossfade section of a visibility range, conditionally
3242
// discard the fragment according to the visibility pattern.
3343
#ifdef VISIBILITY_RANGE_DITHER
@@ -45,8 +55,8 @@ fn fragment(
4555

4656
#ifdef NORMAL_PREPASS
4757
// NOTE: Unlit bit not set means == 0 is true, so the true case is if lit
48-
if (material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
49-
let double_sided = (material.flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u;
58+
if (flags & pbr_types::STANDARD_MATERIAL_FLAGS_UNLIT_BIT) == 0u {
59+
let double_sided = (flags & pbr_types::STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u;
5060

5161
let world_normal = pbr_functions::prepare_world_normal(
5262
in.world_normal,
@@ -62,9 +72,9 @@ fn fragment(
6272

6373
// TODO: Transforming UVs mean we need to apply derivative chain rule for meshlet mesh material pass
6474
#ifdef STANDARD_MATERIAL_NORMAL_MAP_UV_B
65-
let uv = (material.uv_transform * vec3(in.uv_b, 1.0)).xy;
75+
let uv = (uv_transform * vec3(in.uv_b, 1.0)).xy;
6676
#else
67-
let uv = (material.uv_transform * vec3(in.uv, 1.0)).xy;
77+
let uv = (uv_transform * vec3(in.uv, 1.0)).xy;
6878
#endif
6979

7080
// Fill in the sample bias so we can sample from textures.
@@ -100,7 +110,7 @@ fn fragment(
100110
let TBN = pbr_functions::calculate_tbn_mikktspace(normal, in.world_tangent);
101111

102112
normal = pbr_functions::apply_normal_mapping(
103-
material.flags,
113+
flags,
104114
TBN,
105115
double_sided,
106116
is_front,

crates/bevy_render/src/batching/gpu_preprocessing.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -683,7 +683,7 @@ pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
683683
// batch sets. This variable stores the last batch set key that we've
684684
// seen. If our current batch set key is identical to this one, we can
685685
// merge the current batch into the last batch set.
686-
let mut last_multidraw_key = None;
686+
let mut maybe_last_multidraw_key = None;
687687

688688
for key in &phase.batchable_mesh_keys {
689689
let mut batch: Option<BinnedRenderPhaseBatch> = None;
@@ -756,12 +756,16 @@ pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
756756
// We're in multi-draw mode. Check to see whether our
757757
// batch set key is the same as the last one. If so,
758758
// merge this batch into the preceding batch set.
759-
let this_multidraw_key = key.get_batch_set_key();
760-
if last_multidraw_key.as_ref() == Some(&this_multidraw_key) {
761-
batch_sets.last_mut().unwrap().push(batch);
762-
} else {
763-
last_multidraw_key = Some(this_multidraw_key);
764-
batch_sets.push(vec![batch]);
759+
match (&maybe_last_multidraw_key, key.get_batch_set_key()) {
760+
(Some(ref last_multidraw_key), Some(this_multidraw_key))
761+
if *last_multidraw_key == this_multidraw_key =>
762+
{
763+
batch_sets.last_mut().unwrap().push(batch);
764+
}
765+
(_, maybe_this_multidraw_key) => {
766+
maybe_last_multidraw_key = maybe_this_multidraw_key;
767+
batch_sets.push(vec![batch]);
768+
}
765769
}
766770
}
767771
}

0 commit comments

Comments
 (0)