Skip to content

A prim every LOD builds alike is generated once, and its LOD changes rebuild nothing - #558

Merged
RyeMutt merged 5 commits into
rye/prim-vertex-reductionfrom
rye/prim-lod-invariant
Oct 25, 2026
Merged

RyeMutt merged 5 commits into
rye/prim-vertex-reductionfrom
rye/prim-lod-invariant

Conversation

@RyeMutt

@RyeMutt RyeMutt commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Now that a straight, untwisted, untapered prim gets one quad per profile edge, a box, a prism, or a hollow, cut, tapered or sheared box builds exactly the same geometry at all four LODs. Today each LOD is still built separately, and an LOD change still rebuilds the volume and its spatial group's buffers. With this PR, such a shape is built once, and its LOD changes rebuild nothing.

This is part 3 of a stack of 7. Merge them in order: each PR is based on the one before it and shows only its own commits.

  1. Prim normals point straight out of their surfaces, caps face out, and a flexible prim rebuilds in place #556 prim normals
  2. A straight prim gets one quad per profile edge, and rows its parameters collapse emit no triangles #557 vertex reduction
  3. A prim every LOD builds alike is generated once, and its LOD changes rebuild nothing #558 LOD-invariant prims ← this PR
  4. Mesh and GLTF tangents come from meshoptimizer's MikkTSpace mode, welded and ordered in one pass #559 mesh tangents
  5. Prims, sculpts, GLTF primitives and avatar meshes are ordered for the vertex cache #560 vertex cache order
  6. Planar texgen maps a face that faces one way as one plane, and its normal map takes the planar frame #561 planar texgen
  7. The uploader's LODs and normals come from meshoptimizer's attribute-aware simplifier #562 uploader LODs and normals

Changes

  • LLVolume::isLODInvariant(params): answers from the parameters whether every LOD builds the shape alike. The split rule generate applies is now profile_split(), so the check and the generator can't drift apart.

    • A hole that the clamp pulls in to its wall keeps its split points, so it varies by LOD. The check lays the profile out once to see that, when a parameter set's LOD group is made.
    • Sculpts, meshes, flexible prims, and the unique single-face volume a sculpt unload makes are never LOD-invariant.
  • LLVolumeLODGroup::refLOD: hands out one volume, the LOD 3 one, for every LOD of an invariant shape. derefLOD already finds a volume by pointer, so the counts stay consistent.

  • LLVOVolume::updateLOD: an invariant volume now takes the path of an LOD that didn't change. mLOD still moves, for the LOD display, object weights and streaming cost, but nothing is refetched or rebuilt. LLPrimitive::setVolume also returns early for the same invariant parameters, so that a later setVolume doesn't report a change.

  • Tracy plots, per frame:

    • "volume LOD moves";
    • "volume LOD rebuilds";
    • "volume LOD change faces";
    • "volume group rebuilds".

    Two captures of the same camera sweep can compare rebuilds directly.

Testing

  • llmath_tests, shuffled: 411 cases. 410 pass and 1 is skipped.
    • Every sweep shape the predicate accepts builds byte-identical positions, normals, UVs, tangents, indices and faces at all four LODs.
    • Every shape it rejects differs at some LOD.
    • refLOD and derefLOD in mixed order leave the group clean.
  • llprimitive_tests: all 48 cases pass.
  • Viewer: not yet checked. A Tracy capture of a camera sweep over a box-heavy region, before and after, would measure the rebuilds this saves.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You'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 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6f5dfb67-92c8-4bca-a1e7-7f9c73d602b9
📥 Commits

Reviewing files that changed from the base of the PR and between 327b53f and c93b7cf.

📒 Files selected for processing: 9
  • indra/llmath/llvolume.cpp
  • indra/llmath/llvolume.h
  • indra/llmath/llvolumemgr.cpp
  • indra/llmath/llvolumemgr.h
  • indra/llmath/tests/llvolume_bench.cpp
  • indra/llmath/tests/llvolume_test.cpp
  • indra/llprimitive/llprimitive.cpp
  • indra/llprimitive/tests/llprimitive_test.cpp
  • indra/newview/llvovolume.cpp
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

RyeMutt and others added 4 commits October 23, 2026 09:04
Since a straight path with no twist and no taper takes one quad per
profile edge, a square or triangle profile on it, with no hole or a
square or triangle one, builds the same geometry at every detail. The
split rule LLVolume::generate applies is now profile_split(), and
LLVolume::isLODInvariant(params) asks it whether the split is 0 at every
detail, then requires equal twist begin and end, a square or triangle
profile, a hole that is not round, and no sculpt or mesh. A hole the
clamp pulls in to its wall keeps its split points and so varies by LOD;
the check lays the profile out once to see it, when a parameter set's
LOD group is made, and each volume reads the same answer off its own
profile in generate().

A test builds all four LODs of every sweep shape the predicate accepts
and holds them byte-identical: positions, normals, UVs, tangents,
indices and faces. Every shape it rejects differs at some LOD.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LLVolumeLODGroup::refLOD built a volume for each LOD an object asked
for, though a box, a prism or a hollow, cut, tapered or sheared box
comes out the same at all four. For a shape whose parameters say so,
refLOD now hands out one volume, its LOD 3 one, for every LOD.
derefLOD already finds a volume by pointer, so the counts stay
consistent; a test refs and unrefs in mixed order and checks the group
cleans up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When calcLOD moved an object's LOD, updateLOD marked REBUILD_VOLUME
before anything asked whether the geometry would change, so a box
crossing an LOD threshold refetched its volume, rebuilt its faces and
had its spatial group's buffers rebuilt, for geometry that was the same.
An LOD-invariant volume now takes the path an unchanged LOD takes:
mLOD still moves, for the LOD display, object weights and streaming
cost, and nothing is rebuilt.

LLPrimitive::setVolume also returns early for an LOD-invariant volume
with the same parameters. Without that, a later setVolume at the
object's current LOD took the path that hands back the same volume and
reported a change. Sculpts, meshes, flexible prims and the unique
single-face volume a sculpt unload makes are never LOD-invariant, so
their paths are as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… per frame

Tracy plots, per frame: volumes whose LOD moved ("volume LOD moves"),
those of them marked for a volume rebuild ("volume LOD rebuilds"), the
faces those rebuilds touched ("volume LOD change faces", sNumLODChanges)
and volume spatial groups whose geometry was rebuilt ("volume group
rebuilds"). Two captures of the same camera sweep with equal LOD moves
compare their rebuilds directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@RyeMutt
RyeMutt force-pushed the rye/prim-lod-invariant branch from 1428725 to 5f2c58f Compare October 23, 2026 13:10
@RyeMutt
RyeMutt merged commit 5fe299a into develop Oct 25, 2026
39 of 40 checks passed
@RyeMutt
RyeMutt deleted the rye/prim-lod-invariant branch October 25, 2026 06:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant