Finished up most documentation

This commit is contained in:
2026-09-10 21:11:07 -07:00
parent 2cfa0ca690
commit 8355743ada
14 changed files with 1871 additions and 868 deletions

View File

@@ -0,0 +1,126 @@
// Run: node scripts/shader-pixel-regression.mjs
// Optional: SHADER_BROWSER=/path/to/firefox SHADER_TIMEOUT_MS=180000
// Chromium: SHADER_BROWSER=/path/to/chrome-headless-shell SHADER_BROWSER_KIND=chromium
// Hosts without user namespaces: SHADER_NO_SANDBOX=1 (local test content only).
// No automation dependency: the browser POSTs WebGL readback results to a loopback Vite server.
// Also checks that prepared packs create no new programs through reveal and restart.
import { spawn } from 'node:child_process'
import { once } from 'node:events'
import { readFile, mkdir, writeFile, rm } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createServer } from 'vite'
import { originalFragment, baselineFragment, referenceCommit, referenceHash } from './shader-wear-reference.mjs'
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const profile = resolve(root, 'scripts', `.shader-browser-${process.pid}`)
const timeoutMs = Number(process.env.SHADER_TIMEOUT_MS ?? 180000)
if (!Number.isFinite(timeoutMs) || timeoutMs < 1000 || timeoutMs > 600000) {
throw new Error('SHADER_TIMEOUT_MS must be between 1000 and 600000')
}
const fragmentShader = originalFragment(await readFile(resolve(root, 'src/cardMaterial.ts'), 'utf8'))
const baselineShader = baselineFragment(root)
let resolveResult
let rejectResult
const result = new Promise((resolve, reject) => {
resolveResult = resolve
rejectResult = reject
})
// Attach a handler immediately; startup errors can precede awaiting the result.
result.catch(() => {})
let server
let browser
let timer
try {
await mkdir(profile)
await writeFile(resolve(profile, 'user.js'), [
'user_pref("webgl.force-enabled", true);',
'user_pref("webgl.disabled", false);',
'user_pref("browser.shell.checkDefaultBrowser", false);',
'user_pref("browser.startup.homepage_override.mstone", "ignore");',
'user_pref("datareporting.policy.dataSubmissionEnabled", false);',
].join('\n'))
server = await createServer({
root,
configFile: false,
cacheDir: resolve(profile, 'vite'),
logLevel: 'error',
server: { host: '127.0.0.1', port: 0, watch: null },
plugins: [{
name: 'shader-pixel-regression',
configureServer(vite) {
vite.middlewares.use((req, res, next) => {
if (req.url === '/__shader_reference') {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ fragmentShader, baselineShader }))
} else if (req.url === '/__shader_pixels' && req.method === 'POST') {
let body = ''
req.on('data', (chunk) => {
body += chunk
if (body.length > 2_000_000) req.destroy()
})
req.on('end', () => {
try {
resolveResult(JSON.parse(body))
res.end('ok')
} catch (error) {
rejectResult(error)
res.statusCode = 400
res.end('Invalid JSON')
}
})
} else if (req.url === '/__shader_test') {
res.setHeader('Content-Type', 'text/html')
res.end('<!doctype html><title>Shader pixel regression</title><script type="module" src="/scripts/shader-pixel-client.mjs"></script>')
} else {
next()
}
})
},
}],
})
await server.listen()
const address = server.httpServer.address()
const url = `http://127.0.0.1:${address.port}/__shader_test`
const probe = await fetch(url)
if (!probe.ok) throw new Error(`Local test server failed: ${probe.status}`)
console.log(`Shader reference verified against ${referenceCommit} (${referenceHash})`)
console.log(`Serving ${url}; browser timeout ${timeoutMs}ms`)
timer = setTimeout(() => rejectResult(new Error('Browser pixel regression timed out')), timeoutMs)
const browserArgs = process.env.SHADER_BROWSER_KIND === 'chromium' ? [
'--headless', `--user-data-dir=${profile}`, '--no-first-run',
'--disable-background-networking', '--disable-dev-shm-usage',
'--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader', url,
] : ['--headless', '--no-remote', '--profile', profile, url]
if (process.env.SHADER_BROWSER_KIND === 'chromium' && process.env.SHADER_NO_SANDBOX === '1') {
browserArgs.unshift('--no-sandbox')
}
browser = spawn(process.env.SHADER_BROWSER ?? '/usr/bin/firefox', browserArgs, {
env: { ...process.env, TMPDIR: profile, MOZ_HEADLESS: '1' },
stdio: ['ignore', 'ignore', 'pipe'],
})
let browserLog = ''
browser.stderr.on('data', (chunk) => { browserLog = (browserLog + chunk).slice(-12000) })
browser.once('error', rejectResult)
browser.once('exit', (code) => rejectResult(new Error(`Browser exited with ${code}\n${browserLog}`)))
let report
try {
report = await result
} catch (error) {
console.error(browserLog)
throw error
}
console.log(JSON.stringify({ referenceCommit, referenceHash, ...report }, null, 2))
if (!report.passed) process.exitCode = 1
} finally {
clearTimeout(timer)
if (browser?.pid && browser.exitCode === null && browser.signalCode === null) {
const exited = once(browser, 'exit')
browser.kill('SIGTERM')
const forceKill = setTimeout(() => browser.kill('SIGKILL'), 3000)
await exited
clearTimeout(forceKill)
}
await server?.close()
await rm(profile, { recursive: true, force: true })
}