Skip to content

Commit 7cb7362

Browse files
committed
Merge branch 'particle-billboard-batch-key'
2 parents 49d450e + cad31b4 commit 7cb7362

5 files changed

Lines changed: 90 additions & 1 deletion

File tree

crates/renderide/src/passes/world_mesh_forward/material_batch.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ impl<'a> MaterialDrawResolver<'a> {
500500
{
501501
bits = remap_variant_bits_for_billboard(source_stem, bits);
502502
}
503-
if crate::particles::is_generated_billboard_mesh_asset_id(item.mesh_asset_id) {
503+
if batch_key.uses_render_buffer_billboard {
504504
Some(ensure_render_buffer_billboard_variant_bits(bits))
505505
} else {
506506
source_bits.map(|_| bits)
@@ -692,6 +692,35 @@ mod tests {
692692
));
693693
}
694694

695+
#[test]
696+
fn render_buffer_billboard_draws_split_material_batch_boundaries() {
697+
let mut ordinary = dummy_world_mesh_draw_item(DummyDrawItemSpec {
698+
material_asset_id: 42,
699+
property_block: None,
700+
skinned: false,
701+
sorting_order: 0,
702+
mesh_asset_id: 7,
703+
node_id: 1,
704+
slot_index: 0,
705+
collect_order: 0,
706+
alpha_blended: false,
707+
});
708+
ordinary.batch_key.pipeline = embedded_pipeline("billboardunlit_default");
709+
ordinary.batch_key.shader_asset_id = 42;
710+
711+
let mut render_buffer = ordinary.clone();
712+
render_buffer.mesh_asset_id = crate::particles::billboard_render_buffer_mesh_asset_id(3)
713+
.expect("valid render-buffer billboard id");
714+
render_buffer.batch_key.uses_render_buffer_billboard = true;
715+
716+
let draws = vec![ordinary, render_buffer];
717+
let mut boundaries = Vec::new();
718+
719+
collect_material_batch_boundaries_into(&draws, &mut boundaries);
720+
721+
assert_eq!(boundaries, vec![(0, 0), (1, 1)]);
722+
}
723+
695724
/// A draw batch snapshot that stayed Null still requires empty group 1 even if routing changes.
696725
#[test]
697726
fn stale_draw_batch_pipeline_requires_empty_group1_even_if_current_route_is_embedded() {

crates/renderide/src/world_mesh/draw_prep/tests.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ fn property_block_splits_batch_keys() {
134134
pipeline: RasterPipelineKind::Null,
135135
shader_asset_id: -1,
136136
shader_specialization: MaterialShaderSpecializationKey::disabled(),
137+
uses_render_buffer_billboard: false,
137138
material_asset_id: 1,
138139
property_block_slot0: None,
139140
skinned: false,
@@ -165,6 +166,7 @@ fn property_block_splits_batch_keys() {
165166
pipeline: RasterPipelineKind::Null,
166167
shader_asset_id: -1,
167168
shader_specialization: MaterialShaderSpecializationKey::disabled(),
169+
uses_render_buffer_billboard: false,
168170
material_asset_id: 1,
169171
property_block_slot0: Some(99),
170172
skinned: false,

crates/renderide/src/world_mesh/materials/key.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub struct MaterialDrawBatchKey {
1919
pub shader_asset_id: i32,
2020
/// Renderer-local shader specialization constants for material keyword branches.
2121
pub shader_specialization: MaterialShaderSpecializationKey,
22+
/// Whether Billboard/Unlit embedded binds must force generated render-buffer variant bits.
23+
pub uses_render_buffer_billboard: bool,
2224
/// Material asset id for this renderer material slot (or `-1` when missing).
2325
pub material_asset_id: i32,
2426
/// Per-slot property block id when present; `None` is distinct from `Some` for batching.
@@ -208,4 +210,17 @@ mod tests {
208210
key.render_queue = UNITY_TRANSPARENT_RENDER_QUEUE_MIN;
209211
assert!(!key.pass_routing().alpha_test);
210212
}
213+
214+
#[test]
215+
fn render_buffer_billboard_splits_batch_identity() {
216+
let mut ordinary = key(false);
217+
let mut render_buffer = ordinary.clone();
218+
render_buffer.uses_render_buffer_billboard = true;
219+
220+
assert_ne!(ordinary, render_buffer);
221+
assert!(ordinary < render_buffer || render_buffer < ordinary);
222+
223+
ordinary.uses_render_buffer_billboard = true;
224+
assert_eq!(ordinary, render_buffer);
225+
}
211226
}

crates/renderide/src/world_mesh/materials/resolve.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ pub(crate) fn apply_render_buffer_mesh_pipeline_override(
353353
if !crate::particles::is_generated_billboard_mesh_asset_id(mesh_asset_id) {
354354
return;
355355
}
356+
batch_key.uses_render_buffer_billboard = true;
356357
if let RasterPipelineKind::EmbeddedStem(stem) = &batch_key.pipeline
357358
&& stem.as_ref().starts_with("billboardunlit")
358359
{
@@ -424,6 +425,7 @@ fn batch_key_from_resolved(
424425
pipeline: r.pipeline.clone(),
425426
shader_asset_id: r.shader_asset_id,
426427
shader_specialization: r.shader_specialization,
428+
uses_render_buffer_billboard: false,
427429
material_asset_id,
428430
property_block_slot0: property_block_id,
429431
skinned,
@@ -787,13 +789,52 @@ mod ui_rect_clip_tests {
787789
panic!("expected embedded billboard pipeline");
788790
};
789791
assert_eq!(stem.as_ref(), "billboardunlit_default");
792+
assert!(key.uses_render_buffer_billboard);
790793
assert!(key.embedded_needs_uv0);
791794
assert!(key.embedded_needs_color);
792795
assert!(key.embedded_needs_tangent);
793796
assert!(key.embedded_raw_tangent_payload);
794797
assert!(key.embedded_raw_normal_payload);
795798
}
796799

800+
#[test]
801+
fn generated_billboard_mesh_marks_existing_billboard_pipeline_as_render_buffer() {
802+
let mut store = MaterialPropertyStore::new();
803+
store.set_shader_asset_for_material(7, 99);
804+
let dict = MaterialDictionary::new(&store);
805+
let mut router = MaterialRouter::new(RasterPipelineKind::Null);
806+
router.set_shader_pipeline(
807+
99,
808+
RasterPipelineKind::EmbeddedStem(Arc::from("billboardunlit_default")),
809+
);
810+
let ids = MaterialPipelinePropertyIds::new(&PropertyIdRegistry::new());
811+
let resolved =
812+
resolve_material_batch(7, None, &dict, &router, &ids, ShaderPermutation::default());
813+
let mut key = batch_key_from_resolved(
814+
7,
815+
None,
816+
false,
817+
RasterFrontFace::Clockwise,
818+
RasterPrimitiveTopology::TriangleList,
819+
&resolved,
820+
);
821+
let mesh_asset_id = crate::particles::billboard_render_buffer_mesh_asset_id(3).unwrap();
822+
823+
assert!(!key.uses_render_buffer_billboard);
824+
825+
apply_render_buffer_mesh_pipeline_override(
826+
&mut key,
827+
mesh_asset_id,
828+
ShaderPermutation::default(),
829+
);
830+
831+
assert!(key.uses_render_buffer_billboard);
832+
assert_eq!(
833+
key.shader_specialization,
834+
MaterialShaderSpecializationKey::disabled()
835+
);
836+
}
837+
797838
#[test]
798839
fn generated_billboard_mesh_preserves_transparent_material_class() {
799840
let registry = PropertyIdRegistry::new();
@@ -946,5 +987,6 @@ mod ui_rect_clip_tests {
946987
);
947988

948989
assert_eq!(key.pipeline, original);
990+
assert!(!key.uses_render_buffer_billboard);
949991
}
950992
}

crates/renderide/src/world_mesh/test_fixtures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ pub fn dummy_world_mesh_draw_item(spec: DummyDrawItemSpec) -> WorldMeshDrawItem
6666
pipeline: RasterPipelineKind::Null,
6767
shader_asset_id: -1,
6868
shader_specialization: MaterialShaderSpecializationKey::disabled(),
69+
uses_render_buffer_billboard: false,
6970
material_asset_id: mid,
7071
property_block_slot0: pb,
7172
skinned,

0 commit comments

Comments
 (0)