import { expect, test } from '../helpers/serve-dist'; // Issue 2 (cold-GPU first frame): the build links the world's programs with // renderer.compileAsync() against the composer's target, and frame 0 draws // everything once as a safety net. After frame 0 no program may be created: // each later one is a 70-280 ms hitch inside the opening shot. Test builds // dispatch portfolio-program-count whenever renderer.info.programs changes. interface ProgramCountEvent { generation: number; frame: number; programs: number; warmup: { outcome: 'ready' | 'timeout' | 'failed'; waited: number; parallel: boolean; programs: number }; } test('compiles every scene program before the first frame and none after it', async ({ page, distUrl }) => { await page.addInitScript(() => { const events: unknown[] = []; (window as unknown as { __programCounts: unknown[] }).__programCounts = events; window.addEventListener('portfolio-program-count', (event) => { events.push(structuredClone((event as CustomEvent).detail)); }); }); await page.goto(distUrl, { waitUntil: 'domcontentloaded' }); await expect(page.locator('canvas.journey-canvas')).toHaveAttribute('data-lit', '', { timeout: 20_000 }); // The ignition hold, then the pull-away and the first chapters. await page.waitForTimeout(3_500); await page.mouse.move(200, 300); for (let step = 0; step < 12; step += 1) { await page.mouse.wheel(0, 400); await page.waitForTimeout(120); } await page.waitForTimeout(500); const events = await page.evaluate( () => (window as unknown as { __programCounts: ProgramCountEvent[] }).__programCounts, ); expect(events.length).toBeGreaterThan(0); for (const generation of new Set(events.map((event) => event.generation))) { const own = events.filter((event) => event.generation === generation); const first = own[0]!; // The first report comes right after frame 0. expect(first.frame).toBe(1); // The warm-up compiled the scene before frame 0 (or gave up on a slow driver). expect(['ready', 'timeout']).toContain(first.warmup.outcome); expect(first.warmup.programs).toBeGreaterThan(0); expect(first.programs).toBeGreaterThanOrEqual(first.warmup.programs); // Frame 0 adds only the post passes (9 on desktop). A warm-up keyed for // the wrong target or light count would add the whole scene again (~60). expect(first.programs - first.warmup.programs).toBeLessThanOrEqual(12); // No growth afterwards. expect(own.slice(1)).toEqual([]); } });