Repository navigation
Uploader Auto LOD: skip the prune runs and preparations it throws away - #575
Merged
Merged
Conversation
A distant level of detail (LOD 1 and 0) is simplified with meshoptimizer's Prune. Where that run dropped a part larger than PRUNE_LIMIT, or every part, simplifyMesh threw it away and simplified again: first pruning the parts within the limit, then without pruning. In Auto, over the mesh cache, 2,225 of 3,626 prune runs were thrown away like that, each a whole attribute-aware simplification spent for nothing. meshoptimizer prunes only whole components, and its components are the parts largestDropped measures: the same float-equal position remap, the same union of each triangle's corners. So where every part is larger than PRUNE_LIMIT, both as largestDropped measures it (with every distance finite) and as meshopt_simplifyPrune does (it keeps every index at the limit), the prune run comes to the simplification that prunes nothing, whatever it does. If it drops no part, it is that simplification bit for bit: Prune changes nothing else in meshoptimizer 1.3's edge collapse. If it drops one, it is thrown away, and the second path prunes nothing before simplifying. The simplifier now finds this once, when it prepares the model, and goes straight to the plain simplification. largestDropped and the new check share measureParts, so both see each part's radius alike. Faces simplified one at a time (PRECISE and SLOPPY) keep the old path. Measured on the mesh cache (4,000 meshes, 1,310 models of 1,000+ triangles, 3,930 Auto LOD cases), replaying develop's simplifier and this one side by side on each case: - Every LOD's faces are identical to develop's, indices and attributes. - In the 2,563 prune runs where every part was large, 1,590 of them thrown away, the old path's result was compared with the plain simplification. None differed. - Attribute-aware simplifications: 7,369 -> 5,779. - Auto: 33.0 -> 29.2 s. - Preparation costs about 0.25 ms more a model, for the check. The new test fails if the shortcut prunes; the existing small-parts test fails if the check takes a model with small parts as all large. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UV and NONE differ only in UV protecting the vertices on texture seams. Where every such vertex is at a position another face has too, which both modes protect already, they hold the same vertices with the same options and make the same simplification. Auto reaches NONE only once UV has fallen short; NONE then falls exactly as short, is never the closer, and is thrown away. ALMeshSimplifier::same says when two modes are one, and the Auto loop stops before NONE there. Measured on the mesh cache (3,930 Auto LOD cases, develop's simplifier replayed beside this one): - 131 of Auto's 520 NONE passes are provably UV's. Comparing outputs, 138 came out the same; the other 7 only happened to. - Every LOD's faces are identical to develop's. - Attribute-aware simplifications: 5,779 -> 5,642, with the previous change. ALL and UV came out the same in 204 of 694 UV passes. Proving that needs meshoptimizer's own vertex classification (every vertex it takes as a seam or locked would have to be protected), so it is left alone. The new test fails if same() takes a model with a texture seam inside a face as one whose UV and NONE simplifications agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
genMeshOptimizerLODs built an ALMeshSimplifier for every model on every call. Auto generates each level of detail in a call of its own, so every model was prepared three or four times over: the face welds, attribute weights, seam and border flags, and now the parts check, all the same each time. That preparation was 13% of Auto's time. LLModelPreview now keeps a simplifier for each of mBaseModel's models in mSimplifiers, made the first time a level of detail is generated from the model and used for every level after. A simplifier reads its base as it was, so an entry is made again whenever its model is another object, or its material list differs from the one it was made with. rebuildUploadData's matchMaterialOrder reorders a model's faces only together with its material list. Every entry is dropped where generateNormals or restoreNormals rewrite faces, and where the base models are replaced. The entry's LLPointer keeps its model alive, so a new model can never take a freed one's address and pass for it. Measured on the mesh cache (3,930 Auto LOD cases for LODs 2 to 0, develop replayed beside this branch): - Every LOD's faces are identical to develop's. - Preparations: 3,930 -> 1,310. - Auto: 32.7 -> 25.2 s, -23% with the two changes before it, of which 23.4 s is the simplifications and 1.9 s the one preparation of each model. The viewer also regenerates LOD 3 through the simplifier when it generates every level, which shares the preparation once more. The new test fails for a simplifier whose result depends on what it simplified before, which a kept simplifier must not do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 14 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Cuts the two costs #571 found left in the mesh uploader's Auto LOD mode: the prune check's double run and a simplifier prepared again for every LOD. Every LOD Auto makes is unchanged, to the bit.
Measured on the mesh cache (4,000 meshes, 1,310 models of 1,000+ triangles, 3,930 Auto cases for LODs 2 to 0). Develop's simplifier was replayed beside this branch's on each case and the faces compared bit for bit.
Absolute times moved with the machine's load from run to run. Every figure here compares develop and the branch within one run.
A distant simplification of large parts runs once (ebea7a8)
LODs 1 and 0 simplify with meshoptimizer's Prune. Where that run dropped a part larger than 5% of the model, or every part,
simplifyMeshthrew it away and simplified again. The second pass prunes the parts within 5% first, then simplifies without pruning. That happened in 2,225 of the cache's 3,626 prune runs, each a whole simplification thrown away. That is far more than the 389 cases of six simplifications #571 counted.meshoptimizer prunes only whole components. Its components are exactly the parts
largestDroppedmeasures: the same float-equal position remap, and the same union of each triangle's corners. So where every part is larger than 5%, the prune run always ends as the simplification that prunes nothing:"Larger than 5%" has to hold both ways: as
largestDroppedmeasures a part, with every distance finite, and asmeshopt_simplifyPrunedoes. The simplifier checks that once per model, while preparing it. Where it holds, it goes straight to the plain simplification.Faces simplified one at a time (Precise and Sloppy) keep the old path.
Auto skips NONE where it is UV (fe2dadf)
UV and NONE differ only in UV protecting vertices on texture seams. Where every such vertex is also at a position another face has, both modes already protect it, so they make the same simplification. Auto reaches NONE only after UV fell short, so NONE would fall exactly as short and be thrown away.
ALMeshSimplifier::samesays when two modes are the same simplification, and the loop stops before NONE there. That holds for 131 of the cache's 520 NONE passes.ALL and UV came out the same in 204 of 694 UV passes. Proving it needs meshoptimizer's own vertex classification, so it is left alone.
One simplifier for each base model (0965df5)
genMeshOptimizerLODsbuilt a simplifier for every model on every call, and Auto generates each LOD in a call of its own.LLModelPreviewnow keeps one per base model inmSimplifiers, used for every LOD after the first. An entry is made again when its model is another object, or its material list has changed:matchMaterialOrderreorders faces only together with the material list. Every entry is dropped:generateNormalsandrestoreNormals, which rewrite faces;The entry's
LLPointerkeeps its model alive, so a new model can't land on a freed one's address and pass for it. Preparations went from 3,930 to 1,310.Testing
llmath_tests: 482 cases (develop's 479 plus 3 new). All pass apart from the corpus case, which skips without a corpus.[almeshsimplifier][corpus]case passes on 1,472 cached meshes.same()ignoring texture seams: the sphere check fails;llmodelpreview.cppcompiles against a freshly built viewer PCH.Left out
The per-face prune check, which Precise and Sloppy use, still runs twice. The same check would apply there face by face, but it wasn't measured, so it is unchanged.
🤖 Generated with Claude Code