Decide what a flush that confirmed NOTHING means, and fail only when the
evidence says the renderer really is not drawing. Call this AFTER the
detector's own dump and expect, so a real GL error is the error you see.
WHY a verdict rather than a plain assertion: zero CONFIRMED ticks does not
imply a frozen counter. Measured on an idle box (#1651), during the stall
the counter kept advancing (426 → 672) while a trivial page.evaluate went
unanswered for 5 s twelve times running — it is the OBSERVATION that
starves, not the rendering. So the zero case takes ONE bounded probe (the
counter, in the same wrapped shape flushRenderTicks uses so null
still means only "the deadline won", plus the context-lost flag and
document.visibilityState, in a single round trip) and fails only when ALL
of these hold: the probe was answered, the context is not reported lost, the
flush had a baseline, the counter still reads exactly that baseline (neither
advanced past it nor RESET below it), the page reports itself visible, and
at least one kick was ANSWERED. That last one matters because the animation
loop auto-pauses after ~2 s of idleness: if every kick went unanswered,
nothing was confirmed to have asked the renderer to draw, so a frozen counter
is fully explained without a renderer defect. Every other case is one
warning: a starved channel, a lost context, a counter that restarted from a
re-created renderer, or a non-visible page (which gets no rAF callbacks at
all) is the environment, and the detector above keeps its evidence either way
because it listens on page.on('console'), which goes on delivering
throughout.
Decide what a flush that confirmed NOTHING means, and fail only when the evidence says the renderer really is not drawing. Call this AFTER the detector's own dump and
expect, so a real GL error is the error you see.WHY a verdict rather than a plain assertion: zero CONFIRMED ticks does not imply a frozen counter. Measured on an idle box (#1651), during the stall the counter kept advancing (426 → 672) while a trivial
page.evaluatewent unanswered for 5 s twelve times running — it is the OBSERVATION that starves, not the rendering. So the zero case takes ONE bounded probe (the counter, in the same wrapped shape flushRenderTicks uses sonullstill means only "the deadline won", plus the context-lost flag anddocument.visibilityState, in a single round trip) and fails only when ALL of these hold: the probe was answered, the context is not reported lost, the flush had a baseline, the counter still reads exactly that baseline (neither advanced past it nor RESET below it), the page reports itselfvisible, and at least one kick was ANSWERED. That last one matters because the animation loop auto-pauses after ~2 s of idleness: if every kick went unanswered, nothing was confirmed to have asked the renderer to draw, so a frozen counter is fully explained without a renderer defect. Every other case is one warning: a starved channel, a lost context, a counter that restarted from a re-created renderer, or a non-visible page (which gets no rAF callbacks at all) is the environment, and the detector above keeps its evidence either way because it listens onpage.on('console'), which goes on delivering throughout.