From b7ee23a59efc4d5ec9c21e3be1bf6263ab3d53fa Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:16:06 -0800 Subject: [PATCH] Remove meshlet builder retry queue (#16941) Revert the retry queue for stuck meshlet groups that couldn't simplify added in https://github.com/bevyengine/bevy/pull/15886. It was a hack that didn't really work, that was intended to help solve meshlets getting stuck and never getting simplified further. The actual solution is a new DAG building algorithm that I have coming in a followup PR. With that PR, there will be no need for the retry queue, as meshlets will rarely ever get stuck (I checked, the code never gets called). I split this off into it's own PR for easier reviewing. Meshlet IDs during building are back to being relative to the overall list of meshlets across all LODs, instead of starting at 0 for the first meshlet in the simplification queue for the current LOD, regardless of how many meshlets there are in the asset total. Not going to bother to regenerate the bunny asset for this PR. --- crates/bevy_pbr/src/meshlet/from_mesh.rs | 65 ++++++++++-------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/crates/bevy_pbr/src/meshlet/from_mesh.rs b/crates/bevy_pbr/src/meshlet/from_mesh.rs index 14b46e55ba46e..697d3d4aacd82 100644 --- a/crates/bevy_pbr/src/meshlet/from_mesh.rs +++ b/crates/bevy_pbr/src/meshlet/from_mesh.rs @@ -9,7 +9,7 @@ use bevy_render::{ }; use bevy_utils::HashMap; use bitvec::{order::Lsb0, vec::BitVec, view::BitView}; -use core::iter; +use core::{iter, ops::Range}; use half::f16; use itertools::Itertools; use meshopt::{ @@ -105,12 +105,11 @@ impl MeshletMesh { let mut vertex_locks = vec![false; vertices.vertex_count]; // Build further LODs - let mut simplification_queue = Vec::from_iter(0..meshlets.len()); - let mut retry_queue = Vec::new(); + let mut simplification_queue = 0..meshlets.len(); while simplification_queue.len() > 1 { // For each meshlet build a list of connected meshlets (meshlets that share a vertex) let connected_meshlets_per_meshlet = find_connected_meshlets( - &simplification_queue, + simplification_queue.clone(), &meshlets, &position_only_vertex_remap, position_only_vertex_count, @@ -118,7 +117,10 @@ impl MeshletMesh { // Group meshlets into roughly groups of size TARGET_MESHLETS_PER_GROUP, // grouping meshlets with a high number of shared vertices - let groups = group_meshlets(&connected_meshlets_per_meshlet, &simplification_queue); + let groups = group_meshlets( + &connected_meshlets_per_meshlet, + simplification_queue.clone(), + ); // Lock borders between groups to prevent cracks when simplifying lock_group_borders( @@ -131,9 +133,8 @@ impl MeshletMesh { let next_lod_start = meshlets.len(); for group_meshlets in groups.into_iter() { - // If the group only has a single meshlet, we can't simplify it well, so retry later + // If the group only has a single meshlet we can't simplify it if group_meshlets.len() == 1 { - retry_queue.push(group_meshlets[0]); continue; } @@ -146,8 +147,7 @@ impl MeshletMesh { vertex_stride, &vertex_locks, ) else { - // Couldn't simplify the group enough, retry its meshlets later - retry_queue.extend_from_slice(&group_meshlets); + // Couldn't simplify the group enough continue; }; @@ -187,12 +187,8 @@ impl MeshletMesh { ); } - // Set simplification queue to the list of newly created (and retrying) meshlets - simplification_queue.clear(); - simplification_queue.extend(next_lod_start..meshlets.len()); - if !simplification_queue.is_empty() { - simplification_queue.append(&mut retry_queue); - } + // Set simplification queue to the list of newly created meshlets + simplification_queue = next_lod_start..meshlets.len(); } // Copy vertex attributes per meshlet and compress @@ -252,22 +248,22 @@ fn compute_meshlets(indices: &[u32], vertices: &VertexDataAdapter) -> Meshlets { } fn find_connected_meshlets( - simplification_queue: &[usize], + simplification_queue: Range, meshlets: &Meshlets, position_only_vertex_remap: &[u32], position_only_vertex_count: usize, ) -> Vec> { // For each vertex, build a list of all meshlets that use it let mut vertices_to_meshlets = vec![Vec::new(); position_only_vertex_count]; - for (meshlet_queue_id, meshlet_id) in simplification_queue.iter().enumerate() { - let meshlet = meshlets.get(*meshlet_id); + for meshlet_id in simplification_queue.clone() { + let meshlet = meshlets.get(meshlet_id); for index in meshlet.triangles { let vertex_id = position_only_vertex_remap[meshlet.vertices[*index as usize] as usize]; let vertex_to_meshlets = &mut vertices_to_meshlets[vertex_id as usize]; // Meshlets are added in order, so we can just check the last element to deduplicate, // in the case of two triangles sharing the same vertex within a single meshlet - if vertex_to_meshlets.last() != Some(&meshlet_queue_id) { - vertex_to_meshlets.push(meshlet_queue_id); + if vertex_to_meshlets.last() != Some(&meshlet_id) { + vertex_to_meshlets.push(meshlet_id); } } } @@ -275,14 +271,9 @@ fn find_connected_meshlets( // For each meshlet pair, count how many vertices they share let mut meshlet_pair_to_shared_vertex_count = >::default(); for vertex_meshlet_ids in vertices_to_meshlets { - for (meshlet_queue_id1, meshlet_queue_id2) in - vertex_meshlet_ids.into_iter().tuple_combinations() - { + for (meshlet_id1, meshlet_id2) in vertex_meshlet_ids.into_iter().tuple_combinations() { let count = meshlet_pair_to_shared_vertex_count - .entry(( - meshlet_queue_id1.min(meshlet_queue_id2), - meshlet_queue_id1.max(meshlet_queue_id2), - )) + .entry((meshlet_id1.min(meshlet_id2), meshlet_id1.max(meshlet_id2))) .or_insert(0); *count += 1; } @@ -290,12 +281,12 @@ fn find_connected_meshlets( // For each meshlet, gather all other meshlets that share at least one vertex along with their shared vertex count let mut connected_meshlets_per_meshlet = vec![Vec::new(); simplification_queue.len()]; - for ((meshlet_queue_id1, meshlet_queue_id2), shared_count) in - meshlet_pair_to_shared_vertex_count - { + for ((meshlet_id1, meshlet_id2), shared_vertex_count) in meshlet_pair_to_shared_vertex_count { // We record both id1->id2 and id2->id1 as adjacency is symmetrical - connected_meshlets_per_meshlet[meshlet_queue_id1].push((meshlet_queue_id2, shared_count)); - connected_meshlets_per_meshlet[meshlet_queue_id2].push((meshlet_queue_id1, shared_count)); + connected_meshlets_per_meshlet[meshlet_id1 - simplification_queue.start] + .push((meshlet_id2, shared_vertex_count)); + connected_meshlets_per_meshlet[meshlet_id2 - simplification_queue.start] + .push((meshlet_id1, shared_vertex_count)); } // The order of meshlets depends on hash traversal order; to produce deterministic results, sort them @@ -309,15 +300,15 @@ fn find_connected_meshlets( // METIS manual: https://github.com/KarypisLab/METIS/blob/e0f1b88b8efcb24ffa0ec55eabb78fbe61e58ae7/manual/manual.pdf fn group_meshlets( connected_meshlets_per_meshlet: &[Vec<(usize, usize)>], - simplification_queue: &[usize], + simplification_queue: Range, ) -> Vec> { let mut xadj = Vec::with_capacity(simplification_queue.len() + 1); let mut adjncy = Vec::new(); let mut adjwgt = Vec::new(); for connected_meshlets in connected_meshlets_per_meshlet { xadj.push(adjncy.len() as i32); - for (connected_meshlet_queue_id, shared_vertex_count) in connected_meshlets { - adjncy.push(*connected_meshlet_queue_id as i32); + for (connected_meshlet_id, shared_vertex_count) in connected_meshlets { + adjncy.push((connected_meshlet_id - simplification_queue.start) as i32); adjwgt.push(*shared_vertex_count as i32); // TODO: Additional weight based on meshlet spatial proximity } @@ -336,8 +327,8 @@ fn group_meshlets( .unwrap(); let mut groups = vec![SmallVec::new(); partition_count]; - for (meshlet_queue_id, meshlet_group) in group_per_meshlet.into_iter().enumerate() { - groups[meshlet_group as usize].push(simplification_queue[meshlet_queue_id]); + for (i, meshlet_group) in group_per_meshlet.into_iter().enumerate() { + groups[meshlet_group as usize].push(i + simplification_queue.start); } groups }