Keep a recording going behind another browser tab
Chrome stops calling requestAnimationFrame for a hidden tab, and Pixi's ticker hangs on it. The run, the drawing and the recording all stood still as soon as the user switched to another tab. A plain run keeps standing still there, so a forgotten game does not play on unheard. - backgroundBeat.ts ticks the editor from a worker at the recording's fps while a recording goes and the tab is hidden, or the page has not drawn a frame of its own for 250 ms. Once the page draws again, the beat keeps out of the way. - The worker looks every 4 ms whether a tick is due. A 16 ms interval came in bursts behind a busy page, and the recording threw away the second tick of each burst. - backgroundTimers.ts and timerWorker.ts give the short waits of a recording timers in a worker. Chrome slows the page's own timers to once a second in a hidden tab. This covers the waits while saving and the wait for a video's picture after a seek. - Behind a tab Chrome does not call requestVideoFrameCallback, but the picture is there at seeked. The wait there is 10 ms instead of 150 ms while the tab is hidden. - The beat runs for Start recording, the exported game and Start recording... in the main menu. Tests: - tsc, eslint, Prettier clean - tests/viewport: 374 green, 3 new in background-beat.test.ts - e2e 68k (3 checks) on melektaus-atx with the tab really hidden: not in real time all 120 frames, colour changes every 30 frames, the blip 8 ms from green; in real time 97 of 120 frames (73 with the tab in front) - Counter-check without the beat: all three 68k checks red, 3 frames in 60 s not in real time, 7 frames in real time - Gate on melektaus-atx: green, 311 files, 4821 tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
87b2ee9402
commit
abb8fa51d8
10 files changed
+474
-94
No files matched your search
@@ -21,6 +21,7 @@ import {
|
||||
Take,
|
||||
type TakeInfo,
|
||||
} from '../viewport/RecordingTake';
|
||||
import { BackgroundBeat } from '../viewport/backgroundBeat';
|
||||
|
||||
/** What the editor recorder needs of the editor. */
|
||||
export interface EditorRecorderHost {
|
||||
@@ -44,6 +45,8 @@ export interface EditorRecorderHost {
|
||||
export class EditorRecorder {
|
||||
private take: Take | null = null;
|
||||
private source: CanvasSource | null = null;
|
||||
/** Ticks the editor on while it is behind another tab and records. */
|
||||
private beat: BackgroundBeat | null = null;
|
||||
/** Every recording of this page, newest last (test hook). */
|
||||
private readonly takes: Take[] = [];
|
||||
private seen = -1;
|
||||
@@ -96,6 +99,8 @@ export class EditorRecorder {
|
||||
this.host.showBadge(true);
|
||||
// After the render loop (`UPDATE_PRIORITY.LOW`): the canvas holds this tick's picture then.
|
||||
app.ticker.add(this.tick, this, UPDATE_PRIORITY.UTILITY);
|
||||
this.beat ??= new BackgroundBeat(app.ticker);
|
||||
this.beat.start(settings.fps);
|
||||
requestRender();
|
||||
}
|
||||
|
||||
@@ -112,6 +117,7 @@ export class EditorRecorder {
|
||||
this.source = null;
|
||||
const app = ui().app;
|
||||
app.ticker.remove(this.tick, this);
|
||||
this.beat?.stop();
|
||||
if (this.screenResolution !== null) {
|
||||
app.renderer.resize(Math.round(app.screen.width), Math.round(app.screen.height), this.screenResolution);
|
||||
this.screenResolution = null;
|
||||
|
||||
Reference in new issue
Block a user