Lighting on the GPU in the WebGPU path (t88 phase 2b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 20:01:15 -04:00
co-authored by Claude Opus 5.5
parent fc05146887
commit 31e3005816
11 changed files with 247 additions and 16 deletions
+32
View File
@@ -72,6 +72,38 @@ with launch(viewport={'width': 960, 'height': 600}) as (browser, pg, url):
assert pg.evaluate('!GPU.queries || GPU.gpuMs>=0')
assert pg.evaluate('perfMarks.override>=0 && perfMarks.upload>=0')
print('PASS separate override/upload clocks and optional GPU timestamps')
pg.evaluate('() => {' + (root / 'tools/scenes/t88-light.js').read_text() + '}')
# Draw GPU first with no CPU grid and forbid accidental CPU lighting calls.
pg.evaluate("""() => {
window.savedComputeLight=computeLight;
computeLight=()=>{throw Error('CPU light called in WebGPU');};
R.lr=R.lg=R.lb=R.latt=R.lightPx=null; R.lw=R.lh=0;
SET.renderer='webgpu';SET.look='pixel';SET.zoom=6;SET.lighting='full';SET.lightEvery=1;
resizeRender();bot.x=CAM[0]-bot.w/2;bot.y=CAM[1]-bot.h/2;snapCamera();drawWorld();
computeLight=savedComputeLight;
}""")
for mode in ['full', 'fast']:
for case in ['lamp', 'opening', 'magma', 'fire']:
parity = pg.evaluate("""([mode,kind]) => {
lightScene(kind);SET.lighting=mode;SET.lightEvery=1;R.frame=1000;R.rain=0;
SET.renderer='legacy';drawWorld();const a=worldPixelContext().getImageData(0,0,960,600).data;
R.rain=0;SET.renderer='webgpu';drawWorld();const b=worldPixelContext().getImageData(0,0,960,600).data;
let max=0,bad=0,sum=0;
for(let i=0;i<a.length;i+=4){let d=0;for(let j=0;j<3;j++){const e=Math.abs(a[i+j]-b[i+j]);sum+=e;d=Math.max(d,e);}max=Math.max(max,d);bad+=d>8;}
return {mode,kind,max,bad,mean:sum/(960*600*3)};
}""", [mode,case])
assert parity['bad'] == 0, parity
print('PASS light scene', parity)
cadence = pg.evaluate("""() => {
SET.lightEvery=3;R.frame=1002;drawWorld();const old=GPU.lightFrame,origin=R.lx0;
bot.lamp=!bot.lamp;R.camX+=4;R.frame=1003;drawWorld();
const held=GPU.lightFrame===old && R.lx0===origin;
R.frame=1005;drawWorld();const updated=GPU.lightFrame===1005 && R.lx0!==origin;
SET.lightEvery=1;return {held,updated};
}""")
assert all(cadence.values()), cadence
print('PASS GPU-only lighting, both resolutions and lightEvery cadence')
assert pg.evaluate('!GPU.queries || GPU.lightMs>=0')
pg.evaluate('GPU.device.destroy()')
pg.wait_for_function('!GPU.ok')
assert pg.evaluate('SET.renderer==="legacy"')