PrivatetimeoutsPrivateconsecutivePrivatedisabledPrivateeverSticky counterpart of disabled: true once the latch has fired at any
point in the run, and never cleared by a re-arm. The latch itself comes
back on, so it cannot answer "were any frames captured without waiting?" at
the end of the run — this can. It doubles as the warn-once guard, so a run
that latches and re-arms repeatedly logs one line rather than one per latch.
PrivatepredicatePrivate ReadonlydepsFrames that timed out waiting. The exact report's numerator.
PrivatereadRead the injected quiescence predicate, tri-state and throw-safe.
null means "do not wait": either this scene has no lod_group to wait
for (no hook, no registry, or a registry with none in it), or the
predicate threw. The guard mirrors
AnimationController.pacingSuspended() and exists for the same
reason — this is a guard on the INJECTION POINT, not on a known
thrower. Unguarded, a throw would be caught by the loop's outer
handler, reported as "Recording failed" and discard the whole
sequence, which is a catastrophic price for a diagnostic predicate.
The degradation is FRAME-SCOPED, not run-scoped: the predicate is re-probed on the next frame, so a transient throw costs that one frame's wait and nothing more. Only the WARNING is once-per-run, so a persistently throwing provider does not emit one line per frame.
A frame whose opening probe throws is skipped whole: it counts no
settle timeout AND does not reset consecutiveTimeouts. So an
alternating throw/timeout run latches disabled over more
than MAX_CONSECUTIVE_LOD_TIMEOUTS frames. That is intended — the
streak measures consecutive timeouts, not consecutive frames — but
it is worth stating rather than rediscovering.
Wait for this frame's pose to settle, if this scene has anything to wait for and the latch is not currently holding the wait off.
MUST be called after the orbit callback has been removed: the camera pose is fixed from that point on, so the extra frames only let pending loads land. Draining before it would keep advancing the turntable while we wait, smearing the sweep.
PrivatepollThe bounded wait itself: one mandatory selector-catch-up tick, then poll.
whether the scene settled. false also covers "a Stop or abort
broke us out" — the caller distinguishes the two, because only a
genuine give-up counts as a timeout.
PrivaterecordA genuine timeout: count it, and latch the drain off after a streak.
Make a degraded sequence visible rather than a mystery: some frames were captured before their LOD levels finished loading, so they may not show the level the selector had settled on.
"May not show the settled level" rather than "may be coarse", in both
branches, because the predicate is direction-blind: displayed !== active also fires while the never-downgrade gate legitimately holds a
FINER previously-displayed level over a coarser aspiration that is
still streaming. Those frames look better than the selection, not
worse, and telling the user they are coarse would be wrong.
The two branches report genuinely different things, and conflating
them was actively misleading. Once the latch has fired (sticky
everDisabled, since the latch itself re-arms),
timeouts stops describing the run: it counts only the
frames that WAITED and gave up, while every frame captured while the
wait was off was taken with no wait at all — so on a 600-frame capture
a handful of counted timeouts can sit in front of hundreds of undrained
frames. (It is NOT pinned at MAX_CONSECUTIVE_LOD_TIMEOUTS: that
constant bounds the consecutive STREAK, and a run that alternates
timeout/settle can reach any total before three land in a row.) So: an
exact count only when waiting stayed on for the whole run; otherwise
say what actually happened and claim no number. The exact branch's
denominator is attemptedFrames, not capturedFrames — a timeout is
counted before the capture attempt, so a frame that timed out and then
threw would otherwise be in the numerator but not the denominator.
the toast to show, or null when the run needs no report.
The per-frame LOD settle wait, plus the run-scoped bookkeeping its end-of-run report needs.
One instance per capture run: the latch, the streak counter and the warn-once flags are all run-scoped.