Smooth look on WebGPU: WGSL port fed by GPU inputs and CPU overrides (t88 phase 2c)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+50
-4
@@ -65,10 +65,42 @@ with launch(viewport={'width': 960, 'height': 600}) as (browser, pg, url):
|
||||
assert parity['max'] <= 8 and parity['nonblack'] > 1000, parity
|
||||
print('PASS world replacement and zoom reallocate textures and draw the new world', parity)
|
||||
pg.evaluate('SET.look="smooth"; drawWorld();')
|
||||
assert pg.evaluate('!R.gpuOn && R.smoothOn && GPU.canvas.hidden')
|
||||
print('PASS smooth keeps the legacy path')
|
||||
assert pg.evaluate('R.gpuOn && R.smoothOn && !GPU.canvas.hidden')
|
||||
print('PASS smooth uses WebGPU')
|
||||
pg.evaluate('() => {' + (root / 'tools/scenes/t88-renderer.js').read_text() + '}')
|
||||
pg.add_script_tag(path=str(root / 'test/gpu-readback.js'))
|
||||
# Compare all four smooth inputs, including valid zero tags and class-zero overrides.
|
||||
smooth_inputs = pg.evaluate("""async () => {
|
||||
// Every material also gets a plain, unowned cell (including part materials).
|
||||
for(let m=0;m<NMAT;m++) {
|
||||
const x=CAM[0]-16+(m%11)*3,y=CAM[1]-40+Math.floor(m/11)*2,i=y*W+x;
|
||||
setCell(i,m,20,0);part[i]=mach[i]=occ[i]=bgm[i]=0;blockP[(y>>2)*BW+(x>>2)]=0;
|
||||
}
|
||||
const results=[];
|
||||
const now=performance.now.bind(performance); performance.now=()=>5000; SM.t0=0;
|
||||
try {
|
||||
for(const zoom of [12,6,2])for(const fogOn of [false,true]) {
|
||||
SET.zoom=zoom;SET.fog=fogOn;SET.look='smooth';R.lens=false;SET.lighting='off';SET.glow='off';
|
||||
resizeRender();bot.x=CAM[0]-bot.w/2;bot.y=CAM[1]-bot.h/2;snapCamera();R.frame=999;
|
||||
SET.renderer='legacy';SM.scale=1;drawWorld();
|
||||
const expected=[R.px.slice(),R.backPx.slice(),R.treePx.slice(),R.fieldPx.slice()];
|
||||
SET.renderer='webgpu';drawWorld();
|
||||
const actual=await testSmoothInputs(); let alpha=0,max=0,bad=0,examples=[];
|
||||
for(let k=0;k<R.px.length;k++)for(let t=0;t<4;t++) {
|
||||
const a=expected[t][k],b=actual[k*4+t];if((a>>>24)!==(b>>>24))alpha++;
|
||||
for(let c=0;c<3;c++){const diff=Math.abs((a>>>(c*8)&255)-(b>>>(c*8)&255));max=Math.max(max,diff);if(diff>1){bad++;if(examples.length<3)examples.push({k,t,a,b});}}
|
||||
}
|
||||
results.push({zoom,fogOn,alpha,max,bad,examples,share:GPU.overrideCount/R.px.length});
|
||||
}
|
||||
return results;
|
||||
} finally { performance.now=now; }
|
||||
}""")
|
||||
for r in smooth_inputs:
|
||||
assert r['alpha'] == 0 and r['bad'] == 0, r
|
||||
print('PASS all four smooth inputs match legacy: classes and flags exact, RGB within 1', smooth_inputs)
|
||||
pg.evaluate('SET.look="pixel"; SET.renderer="webgpu"; PERF_MARKS=true; drawWorld();')
|
||||
pg.wait_for_function('!GPU.timingBusy')
|
||||
# This fixture stops requestAnimationFrame; poll asynchronous GPU events by timer.
|
||||
pg.wait_for_function('!GPU.timingBusy', polling=10)
|
||||
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')
|
||||
@@ -103,9 +135,23 @@ with launch(viewport={'width': 960, 'height': 600}) as (browser, pg, url):
|
||||
}""")
|
||||
assert all(cadence.values()), cadence
|
||||
print('PASS GPU-only lighting, both resolutions and lightEvery cadence')
|
||||
switches = pg.evaluate("""() => {
|
||||
const original=computeLight,frames=[];
|
||||
computeLight=()=>{throw Error('CPU light called in WebGPU smooth');};
|
||||
try {
|
||||
SET.lightEvery=100;
|
||||
for(const look of ['smooth','pixel','smooth']) {
|
||||
SET.look=look;R.frame++;drawWorld();
|
||||
frames.push(R.gpuOn && GPU.lightFrame===R.frame && R.smoothOn===(look==='smooth'));
|
||||
}
|
||||
return frames;
|
||||
} finally {computeLight=original;SET.lightEvery=1;}
|
||||
}""")
|
||||
assert all(switches), switches
|
||||
print('PASS switching either look refreshes new light textures without CPU lighting')
|
||||
assert pg.evaluate('!GPU.queries || GPU.lightMs>=0')
|
||||
pg.evaluate('GPU.device.destroy()')
|
||||
pg.wait_for_function('!GPU.ok')
|
||||
pg.wait_for_function('!GPU.ok', polling=10)
|
||||
assert pg.evaluate('SET.renderer==="legacy"')
|
||||
pg.evaluate('drawWorld()')
|
||||
print('PASS device loss restores the legacy renderer')
|
||||
|
||||
Reference in New Issue
Block a user