fix(viewer): drive the frame loop with a timer when ?disable=draw (#483)
With no real frames submitted, Chromium's no-damage scheduler throttles requestAnimationFrame to 1Hz on Linux — measured in the headless bake worker: every useFrame system ticked once per second, so a heavy scene needed 300+ seconds of wall time just to run ~300 frames of build work (and exceeded the capture deadline on the worker's slower cores). The empty-scene clear from #482 avoids this on macOS but not on Linux. When `draw` is disabled, drive advance() with setInterval instead of rAF — plain timers are never throttled on a visible page, so the loop pacing is deterministic (fps prop) regardless of compositor heuristics. Normal rendering paths are untouched. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
bbe0e2b0b5
commit
aa30899090
@@ -6,6 +6,20 @@ type FrameLimiterProps = {
|
||||
fps?: number
|
||||
}
|
||||
|
||||
// `?disable=draw` (see post-processing.tsx): the page renders no real frames,
|
||||
// and Chromium's no-damage scheduler then throttles requestAnimationFrame to
|
||||
// 1Hz on Linux (measured in the headless bake worker — every useFrame system
|
||||
// ticked once per second and a heavy scene took 300+ seconds to settle). A
|
||||
// plain timer is never throttled on a visible page, so drive the loop with
|
||||
// setInterval instead of rAF when nothing is drawn.
|
||||
const DRAW_DISABLED =
|
||||
typeof window !== 'undefined' &&
|
||||
new Set(
|
||||
(new URLSearchParams(window.location.search).get('disable') ?? '')
|
||||
.split(',')
|
||||
.map((s) => s.trim()),
|
||||
).has('draw')
|
||||
|
||||
const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
|
||||
const { advance, set, frameloop: initFrameloop, scene, clock } = useThree()
|
||||
const renderer = useThree((state) => state.gl)
|
||||
@@ -18,6 +32,7 @@ const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
|
||||
let then = 0
|
||||
let i = 0
|
||||
let raf: number | null = null
|
||||
let timer: ReturnType<typeof setInterval> | null = null
|
||||
const interval = 1000 / fps
|
||||
function tick(t: DOMHighResTimeStamp) {
|
||||
raf = requestAnimationFrame(tick)
|
||||
@@ -30,13 +45,23 @@ const FrameLimiter: React.FC<FrameLimiterProps> = ({ fps = 50 }) => {
|
||||
}
|
||||
// Set frameloop to never, it will shut down the default render loop
|
||||
set({ frameloop: 'never' })
|
||||
// Kick off custom render loop
|
||||
raf = requestAnimationFrame(tick)
|
||||
if (DRAW_DISABLED) {
|
||||
timer = setInterval(() => {
|
||||
i += interval / 1000
|
||||
advance(i)
|
||||
}, interval)
|
||||
} else {
|
||||
// Kick off custom render loop
|
||||
raf = requestAnimationFrame(tick)
|
||||
}
|
||||
// Restore initial setting
|
||||
return () => {
|
||||
if (raf) {
|
||||
cancelAnimationFrame(raf)
|
||||
}
|
||||
if (timer) {
|
||||
clearInterval(timer)
|
||||
}
|
||||
set({ frameloop: initFrameloop })
|
||||
}
|
||||
}, [fps, advance, set, initFrameloop, renderPaused])
|
||||
|
||||
Reference in New Issue
Block a user