@hyperframes/producer 0.9.22 → 0.9.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/distributed.js +22 -27
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +68 -68
- package/dist/index.js +22 -27
- package/dist/public-server.js +20 -25
- package/dist/services/distributed/renderChunk.d.ts +2 -3
- package/dist/services/fileServer.d.ts +4 -36
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -286,9 +286,24 @@ function parseRangeHeader(header, size, contentType = "application/octet-stream"
|
|
|
286
286
|
const end = Math.min(requestedEnd, size - 1);
|
|
287
287
|
return { kind: "satisfiable", start, end };
|
|
288
288
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
289
|
+
var VIRTUAL_TIME_SHIM = String.raw`(function() {
|
|
290
|
+
if (window.__HF_VIRTUAL_TIME__) return;
|
|
291
|
+
|
|
292
|
+
var virtualNowMs = 0;
|
|
293
|
+
var rafId = 1;
|
|
294
|
+
var rafQueue = [];
|
|
295
|
+
var OriginalDate = Date;
|
|
296
|
+
var originalSetTimeout = window.setTimeout.bind(window);
|
|
297
|
+
var originalClearTimeout = window.clearTimeout.bind(window);
|
|
298
|
+
var originalSetInterval = window.setInterval.bind(window);
|
|
299
|
+
var originalClearInterval = window.clearInterval.bind(window);
|
|
300
|
+
var originalRequestAnimationFrame = window.requestAnimationFrame
|
|
301
|
+
? window.requestAnimationFrame.bind(window)
|
|
302
|
+
: null;
|
|
303
|
+
var originalCancelAnimationFrame = window.cancelAnimationFrame
|
|
304
|
+
? window.cancelAnimationFrame.bind(window)
|
|
305
|
+
: null;
|
|
306
|
+
|
|
292
307
|
// Seeded Math.random / crypto.getRandomValues, keyed by virtual time.
|
|
293
308
|
// Mulberry32: single uint32 state, deterministic, fast.
|
|
294
309
|
var rngState = 0;
|
|
@@ -330,26 +345,7 @@ function buildVirtualTimeShim(options) {
|
|
|
330
345
|
window.crypto.getRandomValues = __seededGetRandomValues;
|
|
331
346
|
} catch (e) {}
|
|
332
347
|
}
|
|
333
|
-
` : "";
|
|
334
|
-
const seekToTimeReseedCall = seedRandomFromFrame ? "reseedRngFromTime(safeTimeMs);\n " : "";
|
|
335
|
-
return String.raw`(function() {
|
|
336
|
-
if (window.__HF_VIRTUAL_TIME__) return;
|
|
337
348
|
|
|
338
|
-
var virtualNowMs = 0;
|
|
339
|
-
var rafId = 1;
|
|
340
|
-
var rafQueue = [];
|
|
341
|
-
var OriginalDate = Date;
|
|
342
|
-
var originalSetTimeout = window.setTimeout.bind(window);
|
|
343
|
-
var originalClearTimeout = window.clearTimeout.bind(window);
|
|
344
|
-
var originalSetInterval = window.setInterval.bind(window);
|
|
345
|
-
var originalClearInterval = window.clearInterval.bind(window);
|
|
346
|
-
var originalRequestAnimationFrame = window.requestAnimationFrame
|
|
347
|
-
? window.requestAnimationFrame.bind(window)
|
|
348
|
-
: null;
|
|
349
|
-
var originalCancelAnimationFrame = window.cancelAnimationFrame
|
|
350
|
-
? window.cancelAnimationFrame.bind(window)
|
|
351
|
-
: null;
|
|
352
|
-
${seededRandomBlock}
|
|
353
349
|
function flushAnimationFrame() {
|
|
354
350
|
if (!rafQueue.length) return;
|
|
355
351
|
var current = rafQueue.slice();
|
|
@@ -480,7 +476,8 @@ ${seededRandomBlock}
|
|
|
480
476
|
// page starts there keep the schedule a one-worker render gives them.
|
|
481
477
|
if (safeTimeMs > 0) seekToTime(0);
|
|
482
478
|
}
|
|
483
|
-
|
|
479
|
+
reseedRngFromTime(safeTimeMs);
|
|
480
|
+
fireDueTimers(safeTimeMs);
|
|
484
481
|
virtualNowMs = safeTimeMs;
|
|
485
482
|
flushAnimationFrame();
|
|
486
483
|
return virtualNowMs;
|
|
@@ -499,8 +496,6 @@ ${seededRandomBlock}
|
|
|
499
496
|
},
|
|
500
497
|
};
|
|
501
498
|
})();`;
|
|
502
|
-
}
|
|
503
|
-
var VIRTUAL_TIME_SHIM = buildVirtualTimeShim({ seedRandomFromFrame: false });
|
|
504
499
|
var RENDER_SEEK_MODE = process.env.PRODUCER_RUNTIME_RENDER_SEEK_MODE === "strict-boundary" ? "strict-boundary" : "preview-phase";
|
|
505
500
|
var RENDER_SEEK_DIAGNOSTICS = process.env.PRODUCER_DEBUG_SEEK_DIAGNOSTICS === "true";
|
|
506
501
|
var RENDER_SEEK_STEP = Math.max(
|
|
@@ -17060,7 +17055,7 @@ async function measurePlanSecondsPerFrame(input) {
|
|
|
17060
17055
|
projectDir: compiledDir,
|
|
17061
17056
|
compiledDir,
|
|
17062
17057
|
port: 0,
|
|
17063
|
-
preHeadScripts: [
|
|
17058
|
+
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
17064
17059
|
fps: fpsRatio
|
|
17065
17060
|
});
|
|
17066
17061
|
let session = null;
|
|
@@ -19988,7 +19983,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19988
19983
|
projectDir: compiledDir,
|
|
19989
19984
|
compiledDir,
|
|
19990
19985
|
port: 0,
|
|
19991
|
-
preHeadScripts: [
|
|
19986
|
+
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
19992
19987
|
// These dimensions are frozen by the controller from the render job, so
|
|
19993
19988
|
// chunk runtime seek quantization stays on the same fps grid as capture.
|
|
19994
19989
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen }
|
package/dist/public-server.js
CHANGED
|
@@ -303,9 +303,24 @@ function parseRangeHeader(header, size, contentType = "application/octet-stream"
|
|
|
303
303
|
const end = Math.min(requestedEnd, size - 1);
|
|
304
304
|
return { kind: "satisfiable", start, end };
|
|
305
305
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
306
|
+
var VIRTUAL_TIME_SHIM = String.raw`(function() {
|
|
307
|
+
if (window.__HF_VIRTUAL_TIME__) return;
|
|
308
|
+
|
|
309
|
+
var virtualNowMs = 0;
|
|
310
|
+
var rafId = 1;
|
|
311
|
+
var rafQueue = [];
|
|
312
|
+
var OriginalDate = Date;
|
|
313
|
+
var originalSetTimeout = window.setTimeout.bind(window);
|
|
314
|
+
var originalClearTimeout = window.clearTimeout.bind(window);
|
|
315
|
+
var originalSetInterval = window.setInterval.bind(window);
|
|
316
|
+
var originalClearInterval = window.clearInterval.bind(window);
|
|
317
|
+
var originalRequestAnimationFrame = window.requestAnimationFrame
|
|
318
|
+
? window.requestAnimationFrame.bind(window)
|
|
319
|
+
: null;
|
|
320
|
+
var originalCancelAnimationFrame = window.cancelAnimationFrame
|
|
321
|
+
? window.cancelAnimationFrame.bind(window)
|
|
322
|
+
: null;
|
|
323
|
+
|
|
309
324
|
// Seeded Math.random / crypto.getRandomValues, keyed by virtual time.
|
|
310
325
|
// Mulberry32: single uint32 state, deterministic, fast.
|
|
311
326
|
var rngState = 0;
|
|
@@ -347,26 +362,7 @@ function buildVirtualTimeShim(options) {
|
|
|
347
362
|
window.crypto.getRandomValues = __seededGetRandomValues;
|
|
348
363
|
} catch (e) {}
|
|
349
364
|
}
|
|
350
|
-
` : "";
|
|
351
|
-
const seekToTimeReseedCall = seedRandomFromFrame ? "reseedRngFromTime(safeTimeMs);\n " : "";
|
|
352
|
-
return String.raw`(function() {
|
|
353
|
-
if (window.__HF_VIRTUAL_TIME__) return;
|
|
354
365
|
|
|
355
|
-
var virtualNowMs = 0;
|
|
356
|
-
var rafId = 1;
|
|
357
|
-
var rafQueue = [];
|
|
358
|
-
var OriginalDate = Date;
|
|
359
|
-
var originalSetTimeout = window.setTimeout.bind(window);
|
|
360
|
-
var originalClearTimeout = window.clearTimeout.bind(window);
|
|
361
|
-
var originalSetInterval = window.setInterval.bind(window);
|
|
362
|
-
var originalClearInterval = window.clearInterval.bind(window);
|
|
363
|
-
var originalRequestAnimationFrame = window.requestAnimationFrame
|
|
364
|
-
? window.requestAnimationFrame.bind(window)
|
|
365
|
-
: null;
|
|
366
|
-
var originalCancelAnimationFrame = window.cancelAnimationFrame
|
|
367
|
-
? window.cancelAnimationFrame.bind(window)
|
|
368
|
-
: null;
|
|
369
|
-
${seededRandomBlock}
|
|
370
366
|
function flushAnimationFrame() {
|
|
371
367
|
if (!rafQueue.length) return;
|
|
372
368
|
var current = rafQueue.slice();
|
|
@@ -497,7 +493,8 @@ ${seededRandomBlock}
|
|
|
497
493
|
// page starts there keep the schedule a one-worker render gives them.
|
|
498
494
|
if (safeTimeMs > 0) seekToTime(0);
|
|
499
495
|
}
|
|
500
|
-
|
|
496
|
+
reseedRngFromTime(safeTimeMs);
|
|
497
|
+
fireDueTimers(safeTimeMs);
|
|
501
498
|
virtualNowMs = safeTimeMs;
|
|
502
499
|
flushAnimationFrame();
|
|
503
500
|
return virtualNowMs;
|
|
@@ -516,8 +513,6 @@ ${seededRandomBlock}
|
|
|
516
513
|
},
|
|
517
514
|
};
|
|
518
515
|
})();`;
|
|
519
|
-
}
|
|
520
|
-
var VIRTUAL_TIME_SHIM = buildVirtualTimeShim({ seedRandomFromFrame: false });
|
|
521
516
|
var RENDER_SEEK_MODE = process.env.PRODUCER_RUNTIME_RENDER_SEEK_MODE === "strict-boundary" ? "strict-boundary" : "preview-phase";
|
|
522
517
|
var RENDER_SEEK_DIAGNOSTICS = process.env.PRODUCER_DEBUG_SEEK_DIAGNOSTICS === "true";
|
|
523
518
|
var RENDER_SEEK_STEP = Math.max(
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
* - Browser is launched with `browserGpuMode: "software"` and verified
|
|
22
22
|
* against `chrome://gpu` via `assertSwiftShader` — a non-SwiftShader
|
|
23
23
|
* backend trips a non-retryable `BROWSER_GPU_NOT_SOFTWARE`.
|
|
24
|
-
* - The file server serves
|
|
25
|
-
* (`buildVirtualTimeShim({ seedRandomFromFrame: true })`) so any
|
|
24
|
+
* - The file server serves `VIRTUAL_TIME_SHIM`, as every render path does, so any
|
|
26
25
|
* composition that uses `Math.random` / `crypto.getRandomValues`
|
|
27
26
|
* produces byte-identical pixels per `(planDir, chunkIndex)`.
|
|
28
27
|
* - No `lastFrameCache` priming: every frame seeks fresh DOM so the
|
|
@@ -30,7 +29,7 @@
|
|
|
30
29
|
* - The chunk's encode runs with `lockGopForChunkConcat: true` and
|
|
31
30
|
* `gopSize === framesInChunk` so concat-copy at assemble time is safe.
|
|
32
31
|
*
|
|
33
|
-
* Every determinism toggle above is opt-in — only this primitive enables them.
|
|
32
|
+
* Every other determinism toggle above is opt-in — only this primitive enables them.
|
|
34
33
|
* In-process renders (`executeRenderJob`) leave them off.
|
|
35
34
|
*/
|
|
36
35
|
import { assertSwiftShader, type CaptureOptions, type CaptureMode, type CapturePerfSummary, type CaptureSession, closeCaptureSession, createCaptureSession, type EngineConfig, initializeSession, probeBeginFrameLiveness, readWebGlVendorInfoFromCanvas } from "@hyperframes/engine";
|
|
@@ -76,42 +76,10 @@ export type RangeRequest = {
|
|
|
76
76
|
*/
|
|
77
77
|
export declare function parseRangeHeader(header: string | null | undefined, size: number, contentType?: string): RangeRequest;
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
* When `true`, the shim additionally replaces `Math.random` and
|
|
84
|
-
* `crypto.getRandomValues` with a Mulberry32-seeded PRNG keyed by the
|
|
85
|
-
* current frame's virtual time. Compositions that call `Math.random()`
|
|
86
|
-
* during render then produce byte-identical pixels across machines and
|
|
87
|
-
* across replays of the same `(planDir, chunkIndex)` pair.
|
|
88
|
-
*
|
|
89
|
-
* Default `false`: leaves `Math.random` / `crypto.getRandomValues` native,
|
|
90
|
-
* preserving the in-process renderer's non-deterministic behavior for
|
|
91
|
-
* compositions that rely on it.
|
|
92
|
-
*/
|
|
93
|
-
seedRandomFromFrame: boolean;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Build the page-side virtual-time shim script.
|
|
97
|
-
*
|
|
98
|
-
* The shim freezes `Date.now`, `performance.now`, rAF, and the timers set
|
|
99
|
-
* after the first seek, so a render seek can deterministically advance the
|
|
100
|
-
* page's notion of "now". The renderer issues `__HF_VIRTUAL_TIME__.seekToTime(ms)`
|
|
101
|
-
* before every frame capture; everything timing-related on the page sees
|
|
102
|
-
* exactly `ms` until the next seek.
|
|
103
|
-
*
|
|
104
|
-
* When `options.seedRandomFromFrame` is `true`, the returned script also
|
|
105
|
-
* installs a seeded `Math.random` / `crypto.getRandomValues` keyed by the
|
|
106
|
-
* current virtual time — so compositions with stochastic visuals retry
|
|
107
|
-
* identically. When `false`, the shim emits no random-override code; the
|
|
108
|
-
* page's native `Math.random` is left alone (the in-process default).
|
|
109
|
-
*/
|
|
110
|
-
export declare function buildVirtualTimeShim(options: VirtualTimeShimOptions): string;
|
|
111
|
-
/**
|
|
112
|
-
* Default in-process virtual-time shim — `seedRandomFromFrame: false`.
|
|
113
|
-
* Existing call sites (`renderOrchestrator`, `probeStage`) import this
|
|
114
|
-
* constant. Distributed callers build their own with seeding enabled.
|
|
79
|
+
* Page-side virtual-time shim, installed before page scripts on every render path.
|
|
80
|
+
* Holds Date.now, performance.now, rAF and later timers at the seek time, and seeds Math.random and
|
|
81
|
+
* crypto.getRandomValues from it, so a re-render repeats. Draws at page load or inside a frame also match
|
|
82
|
+
* across worker counts; a value resolved on a tween's first render depends on the frame a worker reaches first.
|
|
115
83
|
*/
|
|
116
84
|
declare const VIRTUAL_TIME_SHIM: string;
|
|
117
85
|
export declare function resolveRenderFpsConfig(fps: Fps | undefined): {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperframes/producer",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.25",
|
|
4
4
|
"description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
"postcss": "^8.4.0",
|
|
36
36
|
"puppeteer": "~25.11.0",
|
|
37
37
|
"puppeteer-core": "^25.11.0",
|
|
38
|
-
"@hyperframes/
|
|
39
|
-
"@hyperframes/
|
|
40
|
-
"@hyperframes/
|
|
41
|
-
"@hyperframes/
|
|
42
|
-
"@hyperframes/
|
|
38
|
+
"@hyperframes/lint": "0.9.25",
|
|
39
|
+
"@hyperframes/engine": "0.9.25",
|
|
40
|
+
"@hyperframes/studio-server": "0.9.25",
|
|
41
|
+
"@hyperframes/core": "0.9.25",
|
|
42
|
+
"@hyperframes/parsers": "0.9.25"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^25.0.10",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"tsx": "^4.21.0",
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vitest": "^4.1.11",
|
|
51
|
-
"@hyperframes/aws-lambda": "0.9.
|
|
51
|
+
"@hyperframes/aws-lambda": "0.9.25"
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=22"
|