Skip to content

Commit 56784de

Browse files
authored
Fix the ordering of the systems introduced in #18734. (#18825)
There's still a race resulting in blank materials whenever a material of type A is added on the same frame that a material of type B is removed. PR #18734 improved the situation, but ultimately didn't fix the race because of two issues: 1. The `late_sweep_material_instances` system was never scheduled. This PR fixes the problem by scheduling that system. 2. `early_sweep_material_instances` needs to be called after *every* material type has been extracted, not just when the material of *that* type has been extracted. The `chain()` added during the review process in PR #18734 broke this logic. This PR reverts that and fixes the ordering by introducing a new `SystemSet` that contains all material extraction systems. I also took the opportunity to switch a manual reference to `AssetId::<StandardMaterial>::invalid()` to the new `DUMMY_MESH_MATERIAL` constant for clarity. Because this is a bug that can affect any application that switches material types in a single frame, I think this should be uplifted to Bevy 0.16.
1 parent 59bdaca commit 56784de

File tree

4 files changed

+46
-37
lines changed

4 files changed

+46
-37
lines changed

crates/bevy_pbr/src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,14 @@ impl Plugin for PbrPlugin {
466466

467467
// Extract the required data from the main world
468468
render_app
469-
.add_systems(ExtractSchedule, (extract_clusters, extract_lights))
469+
.add_systems(
470+
ExtractSchedule,
471+
(
472+
extract_clusters,
473+
extract_lights,
474+
late_sweep_material_instances,
475+
),
476+
)
470477
.add_systems(
471478
Render,
472479
(

crates/bevy_pbr/src/material.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -316,13 +316,10 @@ where
316316
.add_systems(
317317
ExtractSchedule,
318318
(
319-
(
320-
extract_mesh_materials::<M>,
321-
early_sweep_material_instances::<M>,
322-
)
323-
.chain()
324-
.before(late_sweep_material_instances)
325-
.before(ExtractMeshesSet),
319+
extract_mesh_materials::<M>.in_set(ExtractMaterialsSet),
320+
early_sweep_material_instances::<M>
321+
.after(ExtractMaterialsSet)
322+
.before(late_sweep_material_instances),
326323
extract_entities_needs_specialization::<M>.after(extract_cameras),
327324
),
328325
)
@@ -415,7 +412,8 @@ where
415412
///
416413
/// See the comments in [`RenderMaterialInstances::mesh_material`] for more
417414
/// information.
418-
static DUMMY_MESH_MATERIAL: AssetId<StandardMaterial> = AssetId::<StandardMaterial>::invalid();
415+
pub(crate) static DUMMY_MESH_MATERIAL: AssetId<StandardMaterial> =
416+
AssetId::<StandardMaterial>::invalid();
419417

420418
/// A key uniquely identifying a specialized [`MaterialPipeline`].
421419
pub struct MaterialPipelineKey<M: Material> {
@@ -637,6 +635,10 @@ pub struct RenderMaterialInstance {
637635
last_change_tick: Tick,
638636
}
639637

638+
/// A [`SystemSet`] that contains all `extract_mesh_materials` systems.
639+
#[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)]
640+
pub struct ExtractMaterialsSet;
641+
640642
pub const fn alpha_mode_pipeline_key(alpha_mode: AlphaMode, msaa: &Msaa) -> MeshPipelineKey {
641643
match alpha_mode {
642644
// Premultiplied and Add share the same pipeline key
@@ -782,7 +784,7 @@ fn early_sweep_material_instances<M>(
782784
/// This runs after all invocations of [`early_sweep_material_instances`] and is
783785
/// responsible for bumping [`RenderMaterialInstances::current_change_tick`] in
784786
/// preparation for a new frame.
785-
fn late_sweep_material_instances(
787+
pub(crate) fn late_sweep_material_instances(
786788
mut material_instances: ResMut<RenderMaterialInstances>,
787789
mut removed_visibilities_query: Extract<RemovedComponents<ViewVisibility>>,
788790
) {

crates/bevy_pbr/src/meshlet/instance_manager.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use super::{meshlet_mesh_manager::MeshletMeshManager, MeshletMesh, MeshletMesh3d};
22
use crate::{
3-
Material, MaterialBindingId, MeshFlags, MeshTransforms, MeshUniform, NotShadowCaster,
4-
NotShadowReceiver, PreviousGlobalTransform, RenderMaterialBindings, RenderMaterialInstances,
5-
StandardMaterial,
3+
material::DUMMY_MESH_MATERIAL, Material, MaterialBindingId, MeshFlags, MeshTransforms,
4+
MeshUniform, NotShadowCaster, NotShadowReceiver, PreviousGlobalTransform,
5+
RenderMaterialBindings, RenderMaterialInstances,
66
};
7-
use bevy_asset::{AssetEvent, AssetId, AssetServer, Assets, UntypedAssetId};
7+
use bevy_asset::{AssetEvent, AssetServer, Assets, UntypedAssetId};
88
use bevy_ecs::{
99
entity::{Entities, Entity, EntityHashMap},
1010
event::EventReader,
@@ -113,16 +113,15 @@ impl InstanceManager {
113113
};
114114

115115
let mesh_material = mesh_material_ids.mesh_material(instance);
116-
let mesh_material_binding_id =
117-
if mesh_material != AssetId::<StandardMaterial>::invalid().untyped() {
118-
render_material_bindings
119-
.get(&mesh_material)
120-
.cloned()
121-
.unwrap_or_default()
122-
} else {
123-
// Use a dummy binding ID if the mesh has no material
124-
MaterialBindingId::default()
125-
};
116+
let mesh_material_binding_id = if mesh_material != DUMMY_MESH_MATERIAL.untyped() {
117+
render_material_bindings
118+
.get(&mesh_material)
119+
.cloned()
120+
.unwrap_or_default()
121+
} else {
122+
// Use a dummy binding ID if the mesh has no material
123+
MaterialBindingId::default()
124+
};
126125

127126
let mesh_uniform = MeshUniform::new(
128127
&transforms,

crates/bevy_pbr/src/render/mesh.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,9 @@ impl Plugin for MeshRenderPlugin {
196196
.init_resource::<RenderMaterialInstances>()
197197
.configure_sets(
198198
ExtractSchedule,
199-
ExtractMeshesSet.after(view::extract_visibility_ranges),
199+
ExtractMeshesSet
200+
.after(view::extract_visibility_ranges)
201+
.after(late_sweep_material_instances),
200202
)
201203
.add_systems(
202204
ExtractSchedule,
@@ -1131,19 +1133,18 @@ impl RenderMeshInstanceGpuBuilder {
11311133
// yet loaded. In that case, add the mesh to
11321134
// `meshes_to_reextract_next_frame` and bail.
11331135
let mesh_material = mesh_material_ids.mesh_material(entity);
1134-
let mesh_material_binding_id =
1135-
if mesh_material != AssetId::<StandardMaterial>::invalid().untyped() {
1136-
match render_material_bindings.get(&mesh_material) {
1137-
Some(binding_id) => *binding_id,
1138-
None => {
1139-
meshes_to_reextract_next_frame.insert(entity);
1140-
return None;
1141-
}
1136+
let mesh_material_binding_id = if mesh_material != DUMMY_MESH_MATERIAL.untyped() {
1137+
match render_material_bindings.get(&mesh_material) {
1138+
Some(binding_id) => *binding_id,
1139+
None => {
1140+
meshes_to_reextract_next_frame.insert(entity);
1141+
return None;
11421142
}
1143-
} else {
1144-
// Use a dummy material binding ID.
1145-
MaterialBindingId::default()
1146-
};
1143+
}
1144+
} else {
1145+
// Use a dummy material binding ID.
1146+
MaterialBindingId::default()
1147+
};
11471148
self.shared.material_bindings_index = mesh_material_binding_id;
11481149

11491150
let lightmap_slot = match render_lightmaps.render_lightmaps.get(&entity) {

0 commit comments

Comments
 (0)