Repository navigation
partition: Implement followup spatial partition merging - #964
Merged
Merged
Conversation
This helps us validate the partitioning results at a glance.
Instead of using a separate array for bounds, move this data into ClusterGroup. This makes it more obvious that the bounds data refers to updated group bounds as the algorithm is merging groups, and makes it easier to debug. These were initially separate to ensure that the cost for bounds is nil when unused, but since the algorithm is going to use bounds-only merging as a post-process in the future, the main and preferred mode of operation will involve included positions and bounds, so it makes less sense to separate. The performance effect of this change seems to be below the measurement noise threshold when bounds are unused regardless.
When set (defaults to true), this corresponds to passing the vertex positions to partitioning algorithm. This matches what we're doing right now; setting it to false may be useful to understand the impact spatial grouping has. Note that right now, meshopt_partitionClusters behavior is similar between having position data and not; however, that's going to change soon and the effect will be more meaningful. For best DAG quality it's recommented to keep this as true, hence it's true in the default configuration. Also slightly trim the partition size reservation to reflect the maximum.
This is helpful to understand the corner cases in partitioning where individual clusters have no connections to any other clusters and aren't currently grouped.
After we're done with topological merging, we may have groups that are too small because we only consider topological adjacency when picking merge candidates. To avoid downstream issues from the resulting small partitions, we now automatically merge the resulting groups further - using the same logic where we don't try to grow groups that are already >= target unless another group picks them as the target - using spatial partitioning. For that, we build a tree using spatial splits until the leaves are reasonably small, and then do pairwise merging within the leaves, using existing bounds scoring for prioritization. This significantly improves the partition sizes for meshes with many disconnected clusters which can naturally result during DAG construction, and makes it match the target much closer. To disable it, vertex_positions = NULL can be passed (which will disable spatial proximity scoring as well but it's much less consequential so pretty safe to disable).
For topological merging, because the merged group ends up back in the heap, the merging process is symmetrical (modulo bounds based scoring where the order matters a bit). However, since in the spatial merging we only evaluate each group once as a first group, it's important that first group gets merged into the second one. That way, we retain the ability to merge more groups into the same one, which increases our chances of reaching the target.
Use the same tail computation for both merging procedures; it's a little cleaner compared to doing the assignment in the last loop iteration and breaking out.
Note that if vertex positions are not specified, disconnected clusters will not be merged.
The new test validates that we don't merge disconnected clusters without spatial information, and merge the two closer ones together when we have it.
Owner
Author
zeux
marked this pull request as ready for review
October 4, 2025 18:10
Instead of using the bounding box size, which is a bit more prone to be affected by one-off outliers, use variance to pick the axis; Welford's online algorithm is used to compute both mean & variance in one pass. The precision of computing mean is also higher compared to our old naive computation as it doesn't suffer from catastrophic cancellation as much. This is the same code we use in the clusterizer to build the kD-tree; using the same approach should hopefully mean we have the same properties between the two.
Elsewhere in the codebase we use int axis, so switch to int here as well. The axis storage inside the KD nodes has to stay as unsigned int as it's a bitfield.
Document the fact that absence of vertex positions can result in small groups when the input clusters don't share vertices. Also document partition sizing more explicitly based on current implementation details.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


After we're done with topological merging, we may have groups that are
too small because we only consider topological adjacency when picking
merge candidates.
To avoid downstream issues from the resulting small partitions, we now
automatically merge the resulting groups further - using the same logic
where we don't try to grow groups that are already >= target unless
another group picks them as the target - using spatial partitioning.
For that, we build a tree using spatial splits until the leaves are
reasonably small, and then do pairwise merging within the leaves, using
existing bounds scoring for prioritization.
This significantly improves the partition sizes for meshes with many
disconnected clusters which can naturally result during DAG
construction, and makes it match the target much closer. To disable it,
vertex_positions = NULLcan be passed (which will disable spatialproximity scoring as well but it's much less consequential so pretty
safe to disable).
When using
clodBuild, spatial partitioning is on by default but canbe disabled via
clodConfig::partition_spatial.This contribution is sponsored by Valve.