Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...

    Module core/app/debug/capture-readiness

    Readiness verdict over a __luxarDebug.getState() snapshot — "does this scene graph carry drawable elements, and are they the ones the store describes?".

    THREE WAYS THE SECOND HALF FAILS, and all three are refusals here. A node can be truncated by the renderer's per-node element-texture clamp (totalDroppedElements); progressive refinement can stop scene-wide at the residency byte ceiling (refinementResidency), leaving a partial scene whose composition depends on which nodes were offered first; and the GPU buffer pool can go over its VRAM byte budget and shed pooled geometry to get back under it (gpuPool.byteBudgetEvictions). In every case the element counts stop being a property of the STORE and become a property of the machine and the run — so a capture is not comparable against another build's, and an A/B is measuring the ceiling rather than the change (royerlab/luxar#2508). The residency stop used to surface as one console warning that nothing downstream read, which made "grep the console before comparing two builds" a convention instead of a gate.

    BOTH NEW SIGNALS ARE LOADER-SCOPED AND CUMULATIVE WITHIN THAT LIFE, AND THAT IS DELIBERATE: each says "this happened at some point while this scene was loaded", NOT "this is true right now", so a scene that stopped at the ceiling and then refined fully after a view change is still refused. The contract in full — why that is the verdict a capture tool wants, and why an in-page dataset switch clears it without a reload — lives on RefinementResidencyStop in data/scene-loader/progressive/residency-budget.ts.

    WHAT IT DOES NOT MEASURE: whether the framebuffer will contain pixels. The DebugState totals deliberately include HIDDEN nodes (see the totalLines / totalTriangles field docs there) and sum EVERY level of a substitutive kind=lod group, not just the active one. So an all-hidden scene, or one whose only content is an inactive LOD level, reports ok: true and the screenshot can still be blank. That is intentional: the verdict mirrors debug-state.ts's aggregate contract rather than re-deciding visibility here, so the two can never disagree. Filter on the per-node visible flags in the snapshot if you need the stricter question.

    WHY THIS IS A MODULE AND NOT THREE LINES INSIDE page.evaluate: tools/capture-hires.ts used to compute the verdict inside the browser closure, where nothing can unit-test it — and it was WRONG for every scene ever captured. It read the totals from a state.performance sub-object that debug-state.ts has never produced (DebugState is FLAT), so perf was always {}, every total was undefined, and undefined > 0 made ok false for points, lines, gsplats and mesh alike. The bug was invisible because JSON.stringify drops undefined keys, so the printed diagnostics simply omitted the totals rather than showing undefined (royerlab/luxar#1579).

    So the verdict lives OUT here, as a pure function over a plain object: the browser side only has to hand back getState() verbatim. Deliberately dependency-free — no THREE, no browser globals — so it is importable both from a vitest suite and from a tsx-run Node tool.

    Every field is read defensively (finite-number / Array.isArray guards) because the input crosses a page.evaluate serialisation boundary and may come from an older viewer build. A per-type total that is missing — or present but unusable — reads as 0 AND is named in reason, because an unannounced 0 is indistinguishable from a genuinely empty geometry type; a snapshot carrying NO recognisable total at all is reported as a shape mismatch rather than as an empty scene. Either way the summary never leaks a NaN or an undefined.

    CaptureReadinessSummary
    summarizeCaptureReadiness