Files
ozone-tech_owl_prime/scripts/portable-browser-benchmark.mjs
Даня Архипов 4fcce5bff9 ops: finalize production domain and hardware benchmark tooling
Add /version.json build identity, deploy 16e7930 release markers,
DNS/Named Tunnel prep docs, portable browser benchmark, and replay
stability probe. Keep Quick Tunnel until permanent domain cutover.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 22:21:13 +00:00

234 lines
6.9 KiB
JavaScript

#!/usr/bin/env node
/**
* Portable headed browser benchmark for the presentation laptop.
* Uses the system Chrome/Chromium channel when available — no forced GPU flags.
*
* npm run perf:browser
* npm run perf:browser:export
*
* Env: PLAYWRIGHT_BASE_URL (default http://127.0.0.1:3100)
*/
import { mkdirSync, writeFileSync } from 'node:fs';
import { join, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process';
import { chromium } from 'playwright';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = resolve(__dirname, '..');
const MODES = ['low', 'medium', 'high', 'demo'];
const exportOnly = process.argv.includes('--export') || process.env.PERF_EXPORT === '1';
function baseUrl() {
const raw = process.env.PLAYWRIGHT_BASE_URL ?? 'http://127.0.0.1:3100';
return new URL(raw.includes('://') ? raw : `http://${raw}`);
}
function urlFor(mode) {
const u = baseUrl();
u.searchParams.set('perf', '1');
u.searchParams.set('quality', mode);
return u.toString();
}
function isSoft(renderer = '') {
return /swiftshader|llvmpipe|softpipe|software|mesa offscreen|microsoft basic render/i.test(
renderer,
);
}
async function sample(page) {
return (
(await page.evaluate(() => {
const s = window.__PERF_SNAPSHOT__;
return s ? { ...s } : null;
})) || {
mode: 'demo',
renderer: 'unknown',
averageFps: 0,
minimumFps: 0,
p95FrameTimeMs: 0,
p99FrameTimeMs: 0,
longFramesOver33: 0,
longFramesOver50: 0,
drawCalls: 0,
triangles: 0,
geometries: 0,
textures: 0,
programs: 0,
heapMb: 0,
dpr: 1,
shadows: false,
antialias: false,
hardwareAccelerated: false,
}
);
}
async function measureMode(browser, mode, notes) {
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
try {
await page.goto(urlFor(mode), { waitUntil: 'domcontentloaded', timeout: 90_000 });
await page.waitForSelector('canvas', { timeout: 60_000 });
await page.waitForTimeout(2000);
await page.evaluate(() => window.__PERF_RESET__?.());
await page.waitForTimeout(2500);
const idle = await sample(page);
await page.evaluate(() => window.__PERF_RESET__?.());
await page.getByTestId('demo-play').click({ timeout: 8_000 }).catch(() => notes.push(`${mode}: play skipped`));
await page.waitForTimeout(5000);
const play = await sample(page);
// Full-ish playlist: jump through cases quickly
for (const idx of [0, 2, 4, 8, 9]) {
await page.getByTestId(`demo-case-${idx}`).click().catch(() => undefined);
await page.waitForTimeout(900);
}
const afterPlaylist = await sample(page);
// Three replays (Replay Demo overlay or seek case 0 + play)
for (let i = 0; i < 3; i++) {
const finished = page.getByTestId('demo-finished');
if (await finished.isVisible().catch(() => false)) {
await finished.getByRole('button', { name: /Replay/i }).click().catch(() => undefined);
} else {
await page.getByTestId('demo-case-0').click().catch(() => undefined);
await page.getByTestId('demo-play').click().catch(() => undefined);
}
await page.waitForTimeout(1500);
}
const afterReplay = await sample(page);
const meta = await page.evaluate(() => ({
userAgent: navigator.userAgent,
viewport: { width: window.innerWidth, height: window.innerHeight },
webgl: (() => {
const c = document.createElement('canvas');
const gl = c.getContext('webgl2') || c.getContext('webgl');
if (!gl) return { ok: false };
const dbg = gl.getExtension('WEBGL_debug_renderer_info');
return {
ok: true,
vendor: dbg ? gl.getParameter(dbg.UNMASKED_VENDOR_WEBGL) : null,
renderer: dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER),
};
})(),
}));
return {
mode,
idle,
play,
afterPlaylist,
afterReplay,
meta,
software: isSoft(play.renderer || meta.webgl?.renderer || ''),
};
} finally {
await page.close();
}
}
function toMarkdown(report) {
const rows = report.modes
.map((m) => {
const s = m.play;
return `| ${m.mode} | ${s.renderer} | ${s.averageFps} | ${s.minimumFps} | ${s.p95FrameTimeMs} | ${s.drawCalls} | ${s.triangles} | ${s.heapMb} |`;
})
.join('\n');
const soft = report.softwareRendererDetected;
return `# HARDWARE_BROWSER_BENCHMARK
Measured: ${report.measuredAt}
Commit probe URL: ${report.baseUrl}
Browser: ${report.browserVersion}
Headed: yes (no forced GPU flags)
Software renderer: **${soft ? 'YES' : 'no'}**
## Mode table (after Play)
| Mode | Renderer | Avg FPS | Min FPS | p95 | Calls | Triangles | Heap |
| ---- | -------- | ------: | ------: | --: | ----: | --------: | ---: |
${rows}
## Notes
${report.notes.map((n) => `- ${n}`).join('\n')}
Raw JSON: \`agent/reports/browser-benchmark.json\`
`;
}
async function main() {
const notes = [];
if (!process.env.DISPLAY && !process.env.WAYLAND_DISPLAY) {
notes.push(
'No DISPLAY — attempting headed launch may fail. Run on the presentation laptop with a real display.',
);
}
let browser;
try {
browser = await chromium.launch({
headless: false,
channel: process.env.PERF_CHROME_CHANNEL || undefined,
// Intentionally NO GPU override flags — measure the machine as the jury sees it.
});
} catch (err) {
notes.push(`channel launch failed (${err.message}); falling back to bundled Chromium headed`);
browser = await chromium.launch({ headless: false });
}
const modes = [];
try {
for (const mode of MODES) {
modes.push(await measureMode(browser, mode, notes));
}
} finally {
await browser.close();
}
const commit = spawnSync('git', ['rev-parse', '--short', 'HEAD'], {
cwd: ROOT,
encoding: 'utf8',
}).stdout.trim();
const report = {
measuredAt: new Date().toISOString(),
commit,
baseUrl: baseUrl().toString(),
browserVersion: modes[0]?.meta?.userAgent ?? 'unknown',
softwareRendererDetected: modes.some((m) => m.software),
modes,
notes,
};
mkdirSync(join(ROOT, 'agent', 'reports'), { recursive: true });
const jsonPath = join(ROOT, 'agent', 'reports', 'browser-benchmark.json');
const mdPath = join(ROOT, 'docs', 'HARDWARE_BROWSER_BENCHMARK_LATEST.md');
writeFileSync(jsonPath, JSON.stringify(report, null, 2));
writeFileSync(mdPath, toMarkdown(report));
console.log(
JSON.stringify(
{
ok: true,
exportOnly,
softwareRendererDetected: report.softwareRendererDetected,
demoFps: modes.find((m) => m.mode === 'demo')?.play.averageFps,
renderer: modes.find((m) => m.mode === 'demo')?.play.renderer,
jsonPath,
mdPath,
},
null,
2,
),
);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});