← back to Wild Orbs Opus

test/profile.mjs

63 lines

/** Per-phase render profiler. `node test/profile.mjs [--headed]` */
import { chromium } from 'playwright';
import { fileURLToPath, pathToFileURL } from 'node:url';
import path from 'node:path';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const GAME = pathToFileURL(path.join(__dirname, '..', 'index.html')).href;
const headed = process.argv.includes('--headed');

const browser = await chromium.launch({
  headless: !headed,
  args: headed ? [] : ['--use-angle=metal', '--enable-gpu', '--ignore-gpu-blocklist']
});
const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
await page.goto(GAME);
await page.waitForTimeout(400);
await page.keyboard.press('Enter');
await page.waitForTimeout(400);

const out = await page.evaluate(async () => {
  // heavy but representative field
  orbs.length = 0;
  for (let i = 0; i < 30; i++) orbs.push(makeOrb(Math.random() * innerWidth, Math.random() * innerHeight, 3));
  for (let i = 0; i < 10; i++) hunters.push({ x: Math.random() * innerWidth, y: Math.random() * innerHeight, vx: 1, vy: 1, r: 6, life: 9e5, max: 9e5, a: 0, wig: 0, spd: .1 });
  for (let i = 0; i < 200; i++) burst(Math.random() * innerWidth, Math.random() * innerHeight, 6, '#ffcc88', 3, { life: 9e5 });
  ship.inv = 9e5;

  const phases = {
    bg:        () => { ctx.drawImage(bgc, 0, 0, W, H); },
    twinkle:   drawTwinkle,
    orbs:      drawOrbs,
    particles: drawParticles,
    hunters:   drawHunters,
    bullets:   drawBullets,
    ship:      drawShip,
    vignette:  drawVignette,
    hud:       drawHUD,
    fullFrame: render
  };
  const res = {};
  for (const [name, fn] of Object.entries(phases)) {
    fn(); // warm
    const t0 = performance.now();
    const N = 30;
    for (let i = 0; i < N; i++) fn();
    res[name] = +((performance.now() - t0) / N).toFixed(2);
  }
  res._counts = { orbs: orbs.length, parts: parts.length, hunters: hunters.length };
  return res;
});

console.log(headed ? 'HEADED (real GPU)' : 'HEADLESS');
console.log(JSON.stringify(out, null, 2));

// real-world sustained fps
const fps = await page.evaluate(async () => {
  let n = 0; const t0 = performance.now();
  await new Promise(res => { const t = () => { n++; (performance.now() - t0 < 2000) ? requestAnimationFrame(t) : res(); }; requestAnimationFrame(t); });
  return +(n / ((performance.now() - t0) / 1000)).toFixed(1);
});
console.log('sustained fps:', fps);
await browser.close();