Visitar URL original
Fit the LOD chunk extent to a chunk count target by mvaligursky · Pull Request #335 · playcanvas/splat-transform · GitHub
Skip to content

Fit the LOD chunk extent to a chunk count target - #335

Open
mvaligursky wants to merge 3 commits into
mainfrom
mv-lod-chunk-target
Open

mvaligursky wants to merge 3 commits into
mainfrom
mv-lod-chunk-target

Conversation

@mvaligursky

@mvaligursky mvaligursky commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Large dense scenes cut at the default LOD chunk extent produce tens of thousands of chunks, and a viewer evaluates every chunk on each LOD update. This adds a chunk count target: when the partition would hold more chunks, the extent is raised until it fits. Small scenes keep the requested extent.

Changes:

  • New --lod-max-chunks <n> (default 5000, 0 disables): a target number of chunks. The chunk extent starts at --lod-chunk-extent and is raised to the smallest value at which the tree holds at most this many chunks. --lod-chunk-count takes precedence, so a scene whose Gaussian count alone requires more chunks keeps them, and the log says so. The search walks only the spatial tree's counts and bounds, a few milliseconds even for 200M+ splats.
  • --lod-chunk-min default lowered from 8 to 1, so sparse regions split into reasonably sized chunks instead of one wide chunk, which a viewer can cull and choose detail for more precisely.
  • lod-meta.json records the extent used (asset.chunkExtent) and the target (asset.maxChunks).
  • --lod-chunk-extent and --lod-max-chunks reject negative values.

Results (chunks, before -> after):

  • 212M splat aerial capture, 12 LODs: 31,667 -> 4,968 (extent raised to 131 m)
  • 69M splat church, 7 LODs: 698 -> 1,009 (extent unchanged at 16 m)

API Changes:

  • New CLI option --lod-max-chunks, library option WriteLodSourceOptions.maxChunks / lodMaxChunks.
  • --lod-chunk-min / chunkMin default changed from 8 to 1.
  • New manifest field asset.maxChunks.

Adds --lod-max-chunks (default 5000): when the partition at --lod-chunk-extent
would produce more chunks, the extent is raised until it fits. A viewer
evaluates every chunk on each LOD update, and large dense scenes cut at the
default extent reached tens of thousands of chunks (31,667 for a 212M splat
capture, now 4,968 at a 131 m extent). Small scenes keep the requested extent.
The extent used and the target are recorded in lod-meta.json.

Lowers the --lod-chunk-min default from 8 to 1, so sparse regions split into
reasonably sized chunks instead of one wide chunk, which a viewer can cull and
choose detail for more precisely.
@mvaligursky mvaligursky self-assigned this Sep 28, 2026

@mvaligursky mvaligursky left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR review by Codex (OpenAI AI agent) of commit 153d712. Two actionable findings are inline. Focused LOD writer tests: 33 passed, 9 GPU-dependent tests skipped; changed-file lint and PR Build/Docs/Lint checks passed. The broader local suite was not a valid gate here because it needs a fresh dist build, GPU support, and local HTTP fixtures.

Comment thread src/lib/writers/write-lod.ts Outdated

let lo = minExtent;
while (hi > lo * 1.01) {
const mid = Math.sqrt(lo * hi);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] --lod-chunk-extent 0 is accepted by the CLI and previously meant split down to the spatial-tree floor, but with a positive --lod-max-chunks this loop can never advance: lo is 0, so sqrt(lo * hi) stays 0 and hi > lo * 1.01 remains true. I reproduced an indefinitely running chooseChunkExtent with a 4096-point line, extent 0 and target 4. Please validate a strictly positive extent for the fitter or use a midpoint/termination rule that handles zero (and reject negative extents explicitly).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c591be9. While the lower bound is 0 the fit halves the upper bound, and switches to the geometric midpoint once it has a non-zero lower bound; an iteration cap guards trees whose nodes have no width. A negative extent is treated as 0 by the library, and the CLI now rejects negative --lod-chunk-extent and --lod-max-chunks values. Covered by tests for a requested extent of 0 (target 4 on a 4,096-point line) and a negative extent.

Comment thread src/lib/writers/write-lod.ts Outdated
}

let hi = Math.max(minExtent, root.aabb.largestDim());
if (countLeaves(root, binSize, hi, binMin) > maxChunks) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] When file-unit splits alone exceed maxChunks, raising the extent cannot meet the target, but this branch silently returns the full width. The added test demonstrates target 2 yielding 8 leaves; the writer then logs that the extent was raised to fit 2 chunks and records asset.maxChunks: 2, while the viewer still evaluates 8. This can happen with a small --lod-chunk-count on ordinary scenes. Please surface an unmet-target warning/error (or adjust the file-unit size) and avoid claiming the bound was met.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going over is by design: --lod-max-chunks is a target, and --lod-chunk-count takes precedence, so a scene whose Gaussian count alone requires more chunks keeps them. The reporting was misleading though, fixed in c591be9: in that case the log now reads e.g. "LOD chunk extent raised to 26744.0m: 16 chunks, above the --lod-max-chunks target of 4 (--lod-chunk-count splits require more)", and the option, manifest field, CLI help and README describe it as a target that --lod-chunk-count can override.

… a target

The extent fit bisected geometrically from the requested extent, so a requested
extent of 0 never advanced. It now halves the upper bound until it has a non-zero
lower bound, with an iteration cap for degenerate trees. The CLI rejects negative
--lod-chunk-extent and --lod-max-chunks values.

--lod-max-chunks is a target: --lod-chunk-count splits take precedence, so a
scene whose Gaussian count alone requires more chunks keeps them. The log no
longer reports such a result as fitting the target, and the docs say so.

@mvaligursky mvaligursky left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR re-review by Codex (OpenAI AI agent), on c591be9. The zero-extent nontermination is fixed, and the target/precedence semantics are now documented. I found one remaining reporting gap below. Focused LOD tests: 35 passed, 9 GPU-dependent skipped; changed-file lint and PR CI checks passed.

Comment thread src/lib/writers/write-lod.ts Outdated
const binDim = chunkExtent;
const binMin = chunkMin * 1024;
const binDim = chooseChunkExtent(bTree.root, binSize, binMin, chunkExtent, maxChunks);
if (binDim > chunkExtent) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Report an unmet chunk target even when the extent does not rise. This branch only counts/logs leaves when binDim > chunkExtent, but file-unit splits can exceed maxChunks while the requested extent already spans the scene. I reproduced 4,096 Gaussians within 1 m with chunkCount=1, chunkExtent=16, and maxChunks=2: chooseChunkExtent returns 16, the partition has 4 leaves, and this new diagnostic is skipped entirely. Please check the final leaf count independently of whether the extent changed, so this case also tells users that the target was missed.

The chunk target report only ran when the extent limit was raised, so a scene
whose file-unit splits exceeded --lod-max-chunks while the requested extent
already spanned it was silent. The report now follows the final chunk count:
a missed target is reported whatever the extent did, a met target is reported
when the extent was raised, and nothing is reported when the target is off,
including for a negative requested extent.

When file units alone exceed the target, the fit aims for the fewest chunks the
tree can reach, at the smallest extent reaching them, instead of returning the
full width - the same partition, but a meaningful recorded extent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant