Served with isolation headers; start-screen capability notices; tests on the real GPU (t88 phase 1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 19:11:42 -04:00
co-authored by Claude Opus 5.5
parent 9781aa3ee1
commit b2c9ad9533
22 changed files with 376 additions and 91 deletions
+65
View File
@@ -0,0 +1,65 @@
"""Shared isolated HTTP sessions; callers attach hooks before navigating to url."""
import argparse
from contextlib import contextmanager
import os
from pathlib import Path
import sys
from urllib.parse import quote
from server import DIST, isolated_server
GPU_ARGS = ['--use-angle=vulkan', '--enable-features=Vulkan,VulkanFromANGLE', '--enable-gpu',
'--ignore-gpu-blocklist', '--enable-gpu-rasterization', '--enable-unsafe-webgpu']
SOFT_ARGS = ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader']
DEFAULT_GPU = 'real'
def gpu_option(parser):
parser.add_argument('--gpu', choices=['real', 'soft'],
default=os.environ.get('DEEPGRAIN_GPU', DEFAULT_GPU))
def consume_gpu_option():
"""Keep the older scripts' positional arguments."""
parser = argparse.ArgumentParser(add_help=False)
gpu_option(parser)
options, remaining = parser.parse_known_args()
os.environ['DEEPGRAIN_GPU'] = options.gpu
sys.argv[1:] = remaining
return options.gpu
@contextmanager
def launch(*, gpu=None, browser_name='chromium', viewport=None, page_path=None, args=()):
from playwright.sync_api import sync_playwright
gpu = gpu or os.environ.get('DEEPGRAIN_GPU', DEFAULT_GPU)
if gpu not in ('real', 'soft'):
raise ValueError('GPU must be real or soft')
path = Path(page_path).resolve() if page_path else DIST / 'index.html'
with isolated_server(path.parent) as base, sync_playwright() as p:
flags = (GPU_ARGS if gpu == 'real' else SOFT_ARGS) if browser_name == 'chromium' else []
browser = getattr(p, browser_name).launch(args=[*flags, *args])
try:
context = browser.new_context(viewport=viewport or {'width': 1280, 'height': 760})
page = context.new_page()
if gpu == 'soft':
page.add_init_script('window.__allowSoftwareGL = true;')
yield browser, page, base + '/' + quote(path.name)
finally:
browser.close()
def installed(browser_name):
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
return Path(getattr(p, browser_name).executable_path).is_file()
def capabilities(page):
page.wait_for_function('CAPS.ready')
return page.evaluate("""() => {
const gl = document.createElement('canvas').getContext('webgl2');
const dbg = gl && gl.getExtension('WEBGL_debug_renderer_info');
return { isolated: CAPS.isolated, webgpu: CAPS.webgpu, adapterInfo: CAPS.adapterInfo,
renderer: gl ? gl.getParameter(dbg ? dbg.UNMASKED_RENDERER_WEBGL : gl.RENDERER) : null };
}""")
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env python3
"""Print isolation, WebGPU adapter info and GL renderer for installed browsers."""
import argparse
import json
from use_venv import ensure_playwright
ensure_playwright()
from browser import capabilities, gpu_option, installed, launch
def main():
parser = argparse.ArgumentParser(description=__doc__)
gpu_option(parser)
options = parser.parse_args()
for name in ['chromium', 'firefox']:
if not installed(name):
print(f'SKIP {name}: Playwright browser is not installed')
continue
with launch(gpu=options.gpu, browser_name=name) as (browser, page, url):
page.goto(url)
caps = capabilities(page)
print(f'{name} {browser.version}: {json.dumps(caps)}', flush=True)
assert caps['isolated'], 'Server did not isolate the page'
if name == 'chromium' and options.gpu == 'real':
assert caps['webgpu'] and caps['adapterInfo'] is not None, 'No headless WebGPU adapter'
assert caps['adapterInfo']['architecture'] != 'swiftshader', 'Software WebGPU in real mode'
assert not any(s in (caps['renderer'] or '').lower() for s in ['swiftshader', 'llvmpipe', 'softpipe']), 'Software GL in real mode'
if __name__ == '__main__':
main()
+7 -10
View File
@@ -11,12 +11,10 @@ import subprocess
import sys
import time
from server import isolated_server
from browser import launch, gpu_option, GPU_ARGS, SOFT_ARGS
from use_venv import ensure_playwright
ROOT = Path(__file__).resolve().parent.parent
GPU_ARGS = ['--use-angle=vulkan', '--enable-features=Vulkan', '--enable-gpu',
'--ignore-gpu-blocklist', '--enable-gpu-rasterization']
def size(value):
@@ -107,6 +105,7 @@ def main():
ap.add_argument('--seconds', type=float, default=10)
ap.add_argument('--json', type=Path)
ap.add_argument('--compare', type=Path)
gpu_option(ap)
args = ap.parse_args()
try:
zooms = list(dict.fromkeys(float(z) for z in args.zooms.split(',')))
@@ -129,7 +128,6 @@ def main():
if baseline and (baseline.get('schema') != 1 or baseline['options'] != options):
ap.error('baseline schema/options differ; use the same matrix, dimensions and seconds')
ensure_playwright()
from playwright.sync_api import sync_playwright
started = time.monotonic()
subprocess.run([sys.executable, str(ROOT / 'build.py')], cwd=ROOT, check=True, stdout=sys.stderr)
# Derive coverage from the source so a newly added sim step cannot silently disappear.
@@ -141,13 +139,12 @@ def main():
# boot's title draw is outside frame().
overlays.remove('drawTitle')
result = dict(schema=1, created_utc=datetime.now(timezone.utc).isoformat(), options=options,
gpu_args=GPU_ARGS, scenes={}, sim_targets=steps, overlay_targets=overlays)
files = sorted((ROOT / 'src').rglob('*')) + [Path(__file__), ROOT / 'tools/perf-scenes.js', ROOT / 'tools/perf-measure.js']
gpu_args=GPU_ARGS if args.gpu == 'real' else SOFT_ARGS, scenes={}, sim_targets=steps, overlay_targets=overlays)
files = sorted((ROOT / 'src').rglob('*')) + [Path(__file__), ROOT / 'tools/browser.py', ROOT / 'tools/server.py',
ROOT / 'tools/perf-scenes.js', ROOT / 'tools/perf-measure.js']
result['source_sha256'] = hashlib.sha256(b''.join(p.read_bytes() for p in files if p.is_file())).hexdigest()
errors = []
with isolated_server() as url, sync_playwright() as p:
browser = p.chromium.launch(args=GPU_ARGS)
pg = browser.new_page(viewport=dict(width=args.viewport[0], height=args.viewport[1]))
with launch(gpu=args.gpu, viewport=dict(width=args.viewport[0], height=args.viewport[1])) as (browser, pg, url):
pg.on('pageerror', lambda e: errors.append(str(e)))
pg.add_init_script('''window.__perfStop = true;
const raf = window.requestAnimationFrame.bind(window), pending = new Set();
@@ -169,7 +166,7 @@ def main():
}''')
if not result['machine']['crossOriginIsolated']:
raise RuntimeError('Local server did not provide cross-origin isolation')
if re.search('swiftshader|llvmpipe|softpipe|software', result['machine']['renderer'], re.I):
if args.gpu == 'real' and re.search('swiftshader|llvmpipe|softpipe|software', result['machine']['renderer'], re.I):
raise RuntimeError('Refusing a software-rendered baseline')
result['machine']['browser_version'] = browser.version
def setup(scene):
+3 -5
View File
@@ -2,16 +2,14 @@
import sys, time, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright
from browser import launch, consume_gpu_option
consume_gpu_option()
root = pathlib.Path(__file__).resolve().parent.parent
out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else root / 'shots' / 'playthrough')
out.mkdir(parents=True, exist_ok=True)
url = (root / 'dist' / 'index.html').as_uri()
with sync_playwright() as p:
b = p.chromium.launch(args=['--autoplay-policy=no-user-gesture-required'])
pg = b.new_page(viewport={'width': 1440, 'height': 860})
with launch(viewport={'width': 1440, 'height': 860}, args=['--autoplay-policy=no-user-gesture-required']) as (b, pg, url):
logs = []
pg.on('console', lambda m: logs.append(f'{m.type}: {m.text}'))
pg.on('pageerror', lambda e: logs.append(f'PAGEERROR: {e}'))
+5 -4
View File
@@ -1,5 +1,6 @@
"""Serve dist/ with isolation headers; isolated_server() owns a free-port background server."""
from contextlib import contextmanager
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from threading import Thread
@@ -8,8 +9,8 @@ DIST = Path(__file__).resolve().parent.parent / 'dist'
class Handler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=str(DIST), **kwargs)
def __init__(self, *args, directory=DIST, **kwargs):
super().__init__(*args, directory=str(directory), **kwargs)
def end_headers(self):
self.send_header('Cache-Control', 'no-store')
@@ -22,8 +23,8 @@ class Handler(SimpleHTTPRequestHandler):
@contextmanager
def isolated_server():
with ThreadingHTTPServer(('127.0.0.1', 0), Handler) as server:
def isolated_server(directory=DIST):
with ThreadingHTTPServer(('127.0.0.1', 0), partial(Handler, directory=directory)) as server:
thread = Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
+5 -10
View File
@@ -40,13 +40,9 @@ import argparse, pathlib, sys
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from where import translate
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright
from browser import launch, gpu_option
ROOT = pathlib.Path(__file__).resolve().parent.parent
# Headless Chromium has no GPU. These flags give it WebGL2 through SwiftShader (software), and
# window.__allowSoftwareGL tells the game to use the smooth look anyway (players on software
# rendering get the pixel look instead).
GL_ARGS = ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader']
FREEZE = """(() => { const real = performance.now.bind(performance); window.__frozenNow = null;
performance.now = () => window.__frozenNow === null ? real() : window.__frozenNow; })();"""
@@ -68,17 +64,16 @@ def main():
ap.add_argument('--freeze', action='store_true')
ap.add_argument('--no-light', action='store_true')
ap.add_argument('--page', default=str(ROOT / 'dist' / 'index.html'))
gpu_option(ap)
a = ap.parse_args()
ww, wh = (int(v) for v in a.size.split('x'))
vw, vh = (int(v) for v in a.viewport.split('x'))
errors = []
with sync_playwright() as p:
b = p.chromium.launch(args=GL_ARGS)
pg = b.new_page(viewport={'width': vw, 'height': vh})
with launch(gpu=a.gpu, viewport={'width': vw, 'height': vh}, page_path=a.page) as (b, pg, url):
pg.on('pageerror', lambda e: errors.append(translate(e.stack or str(e))))
pg.on('console', lambda m: errors.append('console: ' + m.text) if m.type == 'error' else None)
pg.add_init_script('window.__allowSoftwareGL = true;' + (FREEZE if a.freeze else ''))
pg.goto(pathlib.Path(a.page).resolve().as_uri())
pg.add_init_script(FREEZE if a.freeze else '')
pg.goto(url)
pg.evaluate("SET.fog = false")
pg.wait_for_timeout(300)
# pause the moment the world exists, so the shot depends only on the seed, the setup
+4 -6
View File
@@ -6,8 +6,8 @@ import math
import copy
from urllib.request import urlopen
from perf import ROOT, GPU_ARGS, quantiles, measurements, report
from server import isolated_server
from perf import ROOT, quantiles, measurements, report
from browser import launch, consume_gpu_option
from use_venv import ensure_playwright
@@ -28,14 +28,12 @@ def main():
assert '/factory/sim/total_ms: +20.0% REGRESSION' in out.getvalue()
assert 'n/a (baseline zero)' in out.getvalue()
ensure_playwright()
from playwright.sync_api import sync_playwright
with isolated_server() as url, sync_playwright() as p:
consume_gpu_option()
with launch() as (browser, pg, url):
with urlopen(url) as response:
assert response.headers['Cross-Origin-Opener-Policy'] == 'same-origin'
assert response.headers['Cross-Origin-Embedder-Policy'] == 'require-corp'
response.read()
browser = p.chromium.launch(args=GPU_ARGS)
pg = browser.new_page(viewport={'width': 1280, 'height': 760})
errors = []
pg.on('pageerror', lambda e: errors.append(str(e)))
pg.add_init_script('window.requestAnimationFrame = () => 0;')