From 4950fdc3f8866f0af0de89d8f0777ec48c9eaef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Fri, 23 Oct 2026 23:31:53 -0400 Subject: [PATCH 1/2] fix(core): color grading rebuilds the graded clips when a lost WebGL context is restored A clip shown while the context was lost never got an entry. --- .../core/src/runtime/colorGrading.test.ts | 39 +++++++++++++++++++ packages/core/src/runtime/colorGrading.ts | 2 + 2 files changed, 41 insertions(+) diff --git a/packages/core/src/runtime/colorGrading.test.ts b/packages/core/src/runtime/colorGrading.test.ts index 66b241fe9e9..b157540e70e 100644 --- a/packages/core/src/runtime/colorGrading.test.ts +++ b/packages/core/src/runtime/colorGrading.test.ts @@ -2205,6 +2205,45 @@ describe("createColorGradingRuntime", () => { }); }); +describe("createColorGradingRuntime after a context loss", () => { + it("grades a clip that came on screen while the context was lost once it is restored", () => { + let lost = false; + const gl = createMockWebGl(); + gl.createTexture = vi.fn(() => (lost ? null : ({} as WebGLTexture))); + const spy = vi + .spyOn(HTMLCanvasElement.prototype, "getContext") + .mockImplementation(((type: string) => + type === "webgl" ? gl : type === "2d" ? mock2d() : null) as never); + try { + document.body.innerHTML = ""; + const first = makeDrawableVideo(); + first.id = "first"; + const second = makeDrawableVideo(); + second.id = "second"; + second.style.visibility = "hidden"; + document.body.append(first, second); + const runtime = createColorGradingRuntime(); + const shared = spy.mock.instances[0] as HTMLCanvasElement; + + lost = true; + shared.dispatchEvent(new Event("webglcontextlost", { cancelable: true })); + second.style.visibility = "visible"; + runtime.refresh(); + expect(runtime.getStatus("#second").state).toBe("unavailable"); + + lost = false; + shared.dispatchEvent(new Event("webglcontextrestored")); + + expect(runtime.getStatus("#first").state).toBe("active"); + expect(runtime.getStatus("#second").state).toBe("active"); + runtime.destroy(); + } finally { + spy.mockRestore(); + document.body.innerHTML = ""; + } + }); +}); + describe("installAuthoredOpacityCapture", () => { it("stamps graded elements at insertion and never overwrites the stamp", async () => { installAuthoredOpacityCapture(); diff --git a/packages/core/src/runtime/colorGrading.ts b/packages/core/src/runtime/colorGrading.ts index 110774fc1c0..2f586bf3282 100644 --- a/packages/core/src/runtime/colorGrading.ts +++ b/packages/core/src/runtime/colorGrading.ts @@ -3867,6 +3867,8 @@ export function createColorGradingRuntime(pausedMediaLease?: { if (entry.contextLost) restoreSourceElement(entry); else drawEntry(entry); } + // A clip shown while the context was gone got no entry; grade it now, not at the next scan. + refresh(); }); shared = { canvas, gl }; return shared; From 5896694de1fe41fd0d2ec96f9c8abfdcd2ed5ef7 Mon Sep 17 00:00:00 2001 From: Miguel Angel Simon Sierra Date: Sat, 24 Oct 2026 00:03:17 -0400 Subject: [PATCH 2/2] fix(core): drop the added comment line from the color grading restore fix --- packages/core/src/runtime/colorGrading.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/runtime/colorGrading.ts b/packages/core/src/runtime/colorGrading.ts index 2f586bf3282..6aacb0a6f31 100644 --- a/packages/core/src/runtime/colorGrading.ts +++ b/packages/core/src/runtime/colorGrading.ts @@ -3867,7 +3867,6 @@ export function createColorGradingRuntime(pausedMediaLease?: { if (entry.contextLost) restoreSourceElement(entry); else drawEntry(entry); } - // A clip shown while the context was gone got no entry; grade it now, not at the next scan. refresh(); }); shared = { canvas, gl };