From 6aebd91ab8dd603edc837907ed73c79c578b16bf Mon Sep 17 00:00:00 2001 From: Miguel Angel Simon Sierra Date: Sun, 1 Nov 2026 00:04:38 -0400 Subject: [PATCH] fix(cli): judge a WebGL draw by the visibility it was made in --- .../cli/src/commands/layout-audit.browser.js | 17 ++++++++++------- .../src/commands/layout-audit.browser.test.ts | 16 +++++++++------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/packages/cli/src/commands/layout-audit.browser.js b/packages/cli/src/commands/layout-audit.browser.js index 844b08ff79..aa09033311 100644 --- a/packages/cli/src/commands/layout-audit.browser.js +++ b/packages/cli/src/commands/layout-audit.browser.js @@ -2206,9 +2206,16 @@ return issues; } - // Canvases whose WebGL context drew since the last audit; the author's own hf-seek loop decides what draws. + // Canvases that drew WebGL while hidden since the last audit, judged when the draw happens; the author's own + // hf-seek loop decides what draws. const webglDraws = (window.__hyperframesWebglDraws ??= new Set()); const drawGate = Symbol.for("hyperframes.webglDrawGate"); + window.__hyperframesRecordWebglDraw = (canvas) => { + if (!canvas || webglDraws.has(canvas)) return; + if (canvas.closest(RUNTIME_HIDDEN_CLIP) || canvas.checkVisibility?.() === false) { + webglDraws.add(canvas); + } + }; for (const name of ["WebGLRenderingContext", "WebGL2RenderingContext"]) { const proto = window[name]?.prototype; if (!proto || proto.__hyperframesDrawCount) continue; @@ -2223,7 +2230,7 @@ const draw = proto[method]; if (typeof draw !== "function") continue; proto[method] = function (...args) { - if (!window[drawGate]?.skip) webglDraws.add(this.canvas); + if (!window[drawGate]?.skip) window.__hyperframesRecordWebglDraw(this.canvas); return draw.apply(this, args); }; } @@ -2232,11 +2239,7 @@ function inactiveWebglIssues(root, time) { const drawn = [...webglDraws].filter((canvas) => canvas?.isConnected); webglDraws.clear(); - return drawn - .filter( - (canvas) => canvas.closest(RUNTIME_HIDDEN_CLIP) || canvas.checkVisibility?.() === false, - ) - .map((canvas) => { + return drawn.map((canvas) => { const scene = canvas.closest("[data-composition-id]:not([data-width])") ?? canvas.closest("[data-composition-id]"); diff --git a/packages/cli/src/commands/layout-audit.browser.test.ts b/packages/cli/src/commands/layout-audit.browser.test.ts index 7a1f7de8ac..14d282a247 100644 --- a/packages/cli/src/commands/layout-audit.browser.test.ts +++ b/packages/cli/src/commands/layout-audit.browser.test.ts @@ -1328,11 +1328,13 @@ describe("layout-audit.browser coordinate-frame findings", () => { return document.getElementById("shader") as HTMLCanvasElement; } - const drawn = () => - (window as unknown as { __hyperframesWebglDraws: Set }).__hyperframesWebglDraws; + const draw = (canvas: HTMLCanvasElement) => + ( + window as unknown as { __hyperframesRecordWebglDraw: (canvas: HTMLCanvasElement) => void } + ).__hyperframesRecordWebglDraw(canvas); it("names the scene whose canvas draws while its clip is hidden", () => { - drawn().add(mountScenes(true)); + draw(mountScenes(true)); const found = runAudit().filter((issue) => issue.code === "webgl_draws_while_inactive"); @@ -1341,12 +1343,12 @@ describe("layout-audit.browser coordinate-frame findings", () => { expect(found[0]?.message).toContain("scene2"); }); - it("stays quiet for a canvas drawing on screen, and reports a draw once", () => { - drawn().add(mountScenes(false)); + it("judges a draw by the state it was made in, and reports it once", () => { + draw(mountScenes(false)); + document.getElementById("scene2")?.setAttribute("data-hf-clip-hidden", ""); expect(runAudit().some((issue) => issue.code === "webgl_draws_while_inactive")).toBe(false); - drawn().add(document.getElementById("shader") as HTMLCanvasElement); - document.getElementById("scene2")?.setAttribute("data-hf-clip-hidden", ""); + draw(document.getElementById("shader") as HTMLCanvasElement); expect(runAudit().filter((i) => i.code === "webgl_draws_while_inactive")).toHaveLength(1); expect(runAudit().some((issue) => issue.code === "webgl_draws_while_inactive")).toBe(false); });