Factored from data/gsplats/lod-refinement.ts so all four geometry
types (Points, Lines, GSplats, Mesh) can drive the same
requestAnimationFrame() yield + cancellable-via-queue contract
with their own typed loader / processor / commit closures.
Per-frame contract:
Yield via requestAnimationFrame (synchronous in test envs).
Check the view-state queue for a pending update from the user.
If pending → hand off to retriggerUpdate and return.
For each loader, call the user-supplied processLoader
closure — which derives view-state, fetches the next LOD,
processes, and commits.
Call updateVisibleCountsInMonitor once per pass.
If loaders still have more LODs and every one of them succeeded
but none advanced, report the stalled rung(s), release the
SceneLoader lock, and exit.
Otherwise repeat until every loader finishes its ladder.
Generic progressive-LOD refinement loop.
Factored from
data/gsplats/lod-refinement.tsso all four geometry types (Points, Lines, GSplats, Mesh) can drive the samerequestAnimationFrame()yield + cancellable-via-queue contract with their own typed loader / processor / commit closures.Per-frame contract:
requestAnimationFrame(synchronous in test envs).retriggerUpdateand return.processLoaderclosure — which derives view-state, fetches the next LOD, processes, and commits.updateVisibleCountsInMonitoronce per pass.