35 lines
5.9 KiB
JavaScript
35 lines
5.9 KiB
JavaScript
(()=>{'use strict';
|
|
const out=document.querySelector('#out'),frame=document.querySelector('#app');
|
|
const scenes=[
|
|
['z0','-0.5','0','3.4',120],
|
|
['period2-cusp-z14','-0.75','0','3.4e-14',1200],
|
|
['swirly-seahorses-z12','-0.7453983606667815','0.1125046349959942','3.52e-12',2000],
|
|
['period2-cusp-z20','-0.75','0','3.4e-20',1800],
|
|
['period2-cusp-z100','-0.75','0','3.4e-100',2400],
|
|
['period3-interior','-0.122561166876653619975245551820735654052','0.744861766619744236593170428604392367240','1e-8',1200],
|
|
['period2-cusp-e280','-0.75','0','1e-280',2400],
|
|
['period2-cusp-e400','-0.75','0','1e-400',1600]
|
|
];
|
|
function roundDiv(v,d){const neg=v<0n,a=neg?-v:v,q=(a+d/2n)/d;return neg?-q:q}
|
|
function align(v,a,b){const d=b-a;return d===0?v:d>0?v<<BigInt(d):v>>BigInt(-d)}
|
|
function roundShift(v,b){const neg=v<0n,a=neg?-v:v,q=(a+(1n<<(BigInt(b)-1n)))>>BigInt(b);return neg?-q:q}
|
|
function pixel(st,x,y,bits=st.bits){const re=align(BigInt(st.re),st.bits,bits),im=align(BigInt(st.im),st.bits,bits),span=align(BigInt(st.span),st.bits,bits),den=BigInt(2*st.width);return[re+roundDiv(span*BigInt(2*x+1-st.width),den),im+roundDiv(span*BigInt(st.height-2*y-1),den)]}
|
|
function orbit(cr,ci,bits,limit){const bail=4n<<BigInt(bits);let zr=0n,zi=0n;for(let n=0;n<limit;n++){const zr2=roundShift(zr*zr,bits),zi2=roundShift(zi*zi,bits);zi=roundShift(2n*zr*zi,bits)+ci;zr=zr2-zi2+cr;if(roundShift(zr*zr,bits)+roundShift(zi*zi,bits)>bail)return n+1}return limit}
|
|
function points(w,h){const xs=[0,.25,.5,.75,1].map(t=>Math.min(w-1,Math.max(0,Math.round(t*(w-1))))),ys=[0,.25,.5,.75,1].map(t=>Math.min(h-1,Math.max(0,Math.round(t*(h-1)))));return ys.flatMap(y=>xs.map(x=>[x,y]))}
|
|
async function waitApp(){if(frame.contentWindow&&frame.contentWindow.__MANDEL_TEST__)return;await new Promise(r=>frame.addEventListener('load',r,{once:true}));const t=performance.now();while(!frame.contentWindow.__MANDEL_TEST__&&performance.now()-t<15000)await new Promise(r=>setTimeout(r,50));if(!frame.contentWindow.__MANDEL_TEST__)throw new Error('test hook not available')}
|
|
async function checkScene(api,id,re,im,span,iter,mode){
|
|
const r=await api.setView({re,im,span,baseIter:iter,adaptive:false,processMode:mode}),diag=r.diag;
|
|
if(diag.backend!=='webgpu'||diag.webgpuError)throw new Error(id+'/'+mode+': WebGPU backend unavailable/error: '+(diag.webgpuError||diag.backend));
|
|
const st=api.state(),ps=points(st.width,st.height),meta=await api.sampleMeta(ps);let guardMismatch=0,falseEscaped=0,falseBounded=0,escapeIterationMismatch=0,unknown=0;
|
|
for(let i=0;i<ps.length;i++){const [x,y]=ps[i],p=pixel(st,x,y),pg=pixel(st,x,y,st.bits+64),a=orbit(p[0],p[1],st.bits,st.iter),g=orbit(pg[0],pg[1],st.bits+64,st.iter);if(a!==g){guardMismatch++;continue}const m=meta[i],cls=(m>>>28)&3,n=m&0x0fffffff;if(cls===0){unknown++;continue}if(cls===1&&a===st.iter)falseEscaped++;if(cls!==1&&a<st.iter)falseBounded++;if(cls===1&&a<st.iter&&n!==a)escapeIterationMismatch++}
|
|
const row={id,mode,width:st.width,height:st.height,iter:st.iter,deep:diag.deep,guardMismatch,falseEscaped,falseBounded,escapeIterationMismatch,unknown,known:ps.length-unknown,reference:diag.reference};
|
|
if(guardMismatch||falseEscaped||falseBounded)throw new Error(id+'/'+mode+' numeric gate failed: '+JSON.stringify(row));
|
|
if(id==='swirly-seahorses-z12'){if(row.known<12)throw new Error(id+'/'+mode+': excessive UNKNOWN rate: '+JSON.stringify(row));}
|
|
else if(row.unknown!==0)throw new Error(id+'/'+mode+': stable corpus scene produced UNKNOWN samples: '+JSON.stringify(row));
|
|
return row;
|
|
}
|
|
async function run(){await waitApp();const api=frame.contentWindow.__MANDEL_TEST__,report=[];for(const [id,re,im,span,iter] of scenes){for(const mode of (id==='z0'?['standard']:['standard','validate'])){out.textContent='running '+id+' / '+mode+'…\n'+JSON.stringify(report,null,2);report.push(await checkScene(api,id,re,im,span,iter,mode));}}
|
|
await api.setView({re:'-0.7453983606667815',im:'0.1125046349959942',span:'3.52e-12',baseIter:2000,adaptive:false,processMode:'validate'});const denseState={...api.state(),width:61,height:39},denseMeta=await api.probeMeta({w:61,h:39,strict:true}),densePoints=[[25,12],[26,15],[27,25]],denseRegression=[];for(const[x,y]of densePoints){const p=pixel(denseState,x,y),pg=pixel(denseState,x,y,denseState.bits+64),a=orbit(p[0],p[1],denseState.bits,denseState.iter),g=orbit(pg[0],pg[1],denseState.bits+64,denseState.iter);if(a!==g)throw new Error('dense guard mismatch '+x+','+y);const m=denseMeta[y*61+x],cls=(m>>>28)&3,n=m&0x0fffffff;if(cls===1&&a===denseState.iter)throw new Error('dense false escape '+JSON.stringify({x,y,a,cls,n}));if(cls!==0&&cls!==1&&a<denseState.iter)throw new Error('dense false bounded '+JSON.stringify({x,y,a,cls,n}));denseRegression.push({x,y,oracle:a,cls,n})}
|
|
await api.setView({re:'-0.75',im:'0',span:'3.4e-20',baseIter:1000,adaptive:false,processMode:'validate'});const exportSmoke=await api.smokeExportTile({w:48,h:32,strict:true,ss:1}),exportSmokeAA=await api.smokeExportTile({w:48,h:32,strict:true,ss:2});if(exportSmoke.length!==exportSmoke.expected||exportSmokeAA.length!==exportSmokeAA.expected)throw new Error('GPU export tile readback length mismatch');if(exportSmoke.unresolved!==0||exportSmokeAA.unresolved!==0)throw new Error('GPU export smoke left unresolved samples: '+JSON.stringify({exportSmoke,exportSmokeAA}));const diag=frame.contentWindow.__MANDEL_DIAG__.snapshot();if(diag.uncapturedErrors&&diag.uncapturedErrors.length)throw new Error('uncaptured WebGPU errors: '+JSON.stringify(diag.uncapturedErrors));const result={status:'pass',kind:'real-webgpu-acceptance',date:new Date().toISOString(),shaderVersion:diag.shaderVersion,adapter:diag.adapter,limits:diag.limits,exportSmoke,exportSmokeAA,denseRegression,report};out.textContent=JSON.stringify(result,null,2);document.title='PASS v24.1 WebGPU acceptance';globalThis.__WEBGPU_ACCEPTANCE__=result}
|
|
run().catch(e=>{const result={status:'fail',error:String(e&&e.stack||e)};out.textContent=JSON.stringify(result,null,2);document.title='FAIL v24.1 WebGPU acceptance';globalThis.__WEBGPU_ACCEPTANCE__=result});
|
|
})();
|