Skip to content

Commit c73af06

Browse files
committed
Merge branch 'codex/film-runtime-bridge' into codex/film-scene-timing
2 parents 40e53e2 + c4b8763 commit c73af06

2 files changed

Lines changed: 58 additions & 0 deletions

File tree

‎packages/core/src/runtime/init.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3902,6 +3902,9 @@ export function initSandboxRuntimeModular(): void {
39023902
activateChildren: true,
39033903
suppressEvents: options?.suppressEvents,
39043904
});
3905+
// The explicit seek owns this paused frame; the transport must not redraw after capture waits.
3906+
lastTransportSeekTime = state.currentTime;
3907+
lastTransportSeekTimeline = state.capturedTimeline;
39053908
runAdapters("pause", 0, pageAnimations);
39063909
syncMediaForCurrentState();
39073910
colorGrading.redraw();

‎packages/producer/src/services/coreRuntimeBrowser.test.ts‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,61 @@ describe("core runtime browser contract", () => {
9393
}
9494
});
9595

96+
it.each(["preview", "export"])(
97+
"does not redraw async sources on transport ticks after an explicit render seek (%s)",
98+
async (mode) => {
99+
const sourcePage = await browser.newPage();
100+
try {
101+
await sourcePage.setContent(
102+
`<div id="source" data-no-timeline data-composition-id="root" data-duration="2" data-width="320" data-height="180"></div>`,
103+
);
104+
if (mode === "export")
105+
await sourcePage.addScriptTag({
106+
content: "window.__HF_EXPORT_RENDER_SEEK_CONFIG={fps:30};",
107+
});
108+
await sourcePage.addScriptTag({ content: readFileSync(RUNTIME_PATH, "utf8") });
109+
await sourcePage.waitForFunction(() => window.__playerReady && window.__renderReady);
110+
await sourcePage.evaluate(() => {
111+
const element = document.getElementById("source")!;
112+
window.__hyperframes!.registerFrameSource({
113+
element,
114+
render: async (time) => {
115+
await new Promise((resolve) => setTimeout(resolve, 20));
116+
element.setAttribute(
117+
"data-draws",
118+
(element.getAttribute("data-draws") ?? "") + time + ",",
119+
);
120+
},
121+
});
122+
});
123+
for (const [index, time] of [0.5, 0.5, 0.2].entries()) {
124+
await sourcePage.evaluate((t) => window.__player!.renderSeek!(t), time);
125+
await waitForPendingSeekCompletion(sourcePage);
126+
// Let the transport run after the capture barrier has already settled.
127+
await sourcePage.evaluate(async () => {
128+
for (let frame = 0; frame < 4; frame++)
129+
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
130+
await window.__hfWaitForSeekCompletion!();
131+
});
132+
expect(await sourcePage.$eval("#source", (el) => el.getAttribute("data-draws"))).toBe(
133+
[0.5, 0.5, 0.2].slice(0, index + 1).join(",") + ",",
134+
);
135+
}
136+
await sourcePage.evaluate(() => window.__player!.play());
137+
await sourcePage.waitForFunction(
138+
() =>
139+
(document.getElementById("source")!.getAttribute("data-draws") ?? "")
140+
.split(",")
141+
.filter(Boolean).length > 3,
142+
);
143+
await sourcePage.evaluate(() => window.__player!.pause());
144+
await waitForPendingSeekCompletion(sourcePage);
145+
} finally {
146+
await sourcePage.close();
147+
}
148+
},
149+
);
150+
96151
it("initializes the public player contract and seeks the CSS adapter", async () => {
97152
const result = await page.evaluate(() => {
98153
const runtimeWindow = window as unknown as {

0 commit comments

Comments
 (0)