diff --git a/src/core/lib/validateImageBitmap.test.ts b/src/core/lib/validateImageBitmap.test.ts index bee51ba..bc014d5 100644 --- a/src/core/lib/validateImageBitmap.test.ts +++ b/src/core/lib/validateImageBitmap.test.ts @@ -44,6 +44,10 @@ function createFakeGl(readbackRed: number, framebufferComplete = true) { ), deleteFramebuffer: vi.fn(), deleteTexture: vi.fn(), + // The probe releases its throwaway context via WEBGL_lose_context once done. + getExtension: vi.fn((name: string) => + name === 'WEBGL_lose_context' ? { loseContext: vi.fn() } : null, + ), }; } @@ -116,4 +120,15 @@ describe('detectPremultiplyAlphaHonored', () => { false, ); }); + + it('releases the throwaway context so it does not leak a GL slot', async () => { + const lose = { loseContext: vi.fn() }; + const gl = createFakeGl(128); + gl.getExtension = vi.fn((name: string) => + name === 'WEBGL_lose_context' ? lose : null, + ); + await detectPremultiplyAlphaHonored(createPlatform(gl)); + expect(gl.getExtension).toHaveBeenCalledWith('WEBGL_lose_context'); + expect(lose.loseContext).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/core/lib/validateImageBitmap.ts b/src/core/lib/validateImageBitmap.ts index 8c4abcf..5fab8c6 100644 --- a/src/core/lib/validateImageBitmap.ts +++ b/src/core/lib/validateImageBitmap.ts @@ -172,5 +172,13 @@ export async function detectPremultiplyAlphaHonored( gl.deleteTexture(tex); bitmap.close?.(); + // Release this throwaway context immediately. Embedded TV browsers cap the + // number of live WebGL contexts very low; since this probe runs AFTER the + // main render context is created, a leaked context here is the newest one + // and its lingering presence can push the page over the limit, evicting the + // OLDEST context (the live render context) — which then fails every + // createTexture. Don't wait for GC to reclaim the canvas; drop it now. + gl.getExtension('WEBGL_lose_context')?.loseContext(); + return result; }