publish
This commit is contained in:
parent
01204dfa2f
commit
f5fc9b76ce
73 changed files with 32628 additions and 376 deletions
148
docs/removal-pass/IMPLEMENTATION.md
Normal file
148
docs/removal-pass/IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# Runtime cost removal pass
|
||||
|
||||
Date: 2026-09-29
|
||||
|
||||
## Purpose
|
||||
|
||||
This build applies every removal/reduction that the preceding A/B pass supported strongly enough to change without redesigning numerical state. The original integrated build remains separately available at `/mnt/data/mandelbrot_integrated`; this directory is the experimental production-equivalent removal build.
|
||||
|
||||
Production source of truth in this build: `index.html`.
|
||||
|
||||
## Applied changes
|
||||
|
||||
### 1. Recolor no longer copies numeric history
|
||||
|
||||
Color-only recolor now promotes only color history. `meta/smooth` numeric history is not recopied when the numerical field did not change.
|
||||
|
||||
A/B traffic avoided per accepted recolor at the preset caps:
|
||||
|
||||
- fast: 4 MiB
|
||||
- standard: 8 MiB
|
||||
- high: 32 MiB
|
||||
|
||||
At 20 Hz color cycling this removes 80 / 160 / 640 MiB/s of avoidable copy traffic respectively. These are byte counts, not measured GPU frame-time gains.
|
||||
|
||||
### 2. Removed unread CPU fallback RGBA retention
|
||||
|
||||
The CPU fallback still creates the RGBA array needed for `ImageData`, but no longer stores a second long-lived reference in `state.cpuFrameRgba`. The state property itself was removed.
|
||||
|
||||
Avoided retained JS memory at the pixel caps:
|
||||
|
||||
- fast: 2 MiB
|
||||
- standard: 4 MiB
|
||||
- high: 16 MiB
|
||||
|
||||
### 3. Removed partial exact-interior pre-primary unknown scan
|
||||
|
||||
`certifyInterior()` no longer runs a full-frame unknown-stat pass immediately before the primary numerical pass. The primary path computes the required statistics later. A fully certified frame now uses known-zero stats directly instead of a readback.
|
||||
|
||||
Minimum avoided `fieldMeta` read per partial certification:
|
||||
|
||||
- fast: 2 MiB
|
||||
- standard: 4 MiB
|
||||
- high: 16 MiB
|
||||
|
||||
### 4. Removed FAST coverage-repair pass and its dead remnants
|
||||
|
||||
The second full-screen FAST numerical pass that attempted to repair `reason=0` holes was removed. Validation before removal found zero holes in 36 layout cases plus 200 randomized semantic cases.
|
||||
|
||||
After integration, two dead remnants were also removed:
|
||||
|
||||
- the post-tile `numericParams` upload whose only purpose was coverage-repair mode 2;
|
||||
- the coverage-hole mode-2 branch in `FAST_PERTURB_WGSL`.
|
||||
|
||||
Representative eliminated shader invocations:
|
||||
|
||||
- 1365×768: 1,050,624 invocations
|
||||
- 2731×1536: 4,202,496 invocations
|
||||
|
||||
The FAST kernel hash changed only because this unreachable coverage-repair branch was deleted. `tests/kernel_hashes.json` was updated accordingly.
|
||||
|
||||
### 5. Removed idle color-auto snapshot allocation
|
||||
|
||||
Turning on automatic hue cycling while no render is running no longer captures `colorSource`. Idle recolor does not consume that snapshot. Render-time snapshot behavior remains because it is used to keep visual continuity while a new numerical frame is in flight.
|
||||
|
||||
Avoided idle snapshot residency at caps:
|
||||
|
||||
- fast: 6 MiB
|
||||
- standard: 12 MiB
|
||||
- high: 48 MiB
|
||||
|
||||
### 6. Export workspace is released after every export
|
||||
|
||||
`runExport()` now destroys the reusable export ring in `finally`. This removes roughly 24 MiB of post-export residency in the tested 512×512 / depth-3 configuration. The next export must allocate the ring again; real-device allocation latency was not measurable in this environment.
|
||||
|
||||
### 7. Exact-interior proof is spatially gated
|
||||
|
||||
Before the BigInt tile proof, the exact viewport is tested against conservative bounding boxes for the main cardioid and period-2 bulb. If neither can intersect the viewport, the proof is skipped.
|
||||
|
||||
A/B aggregate CPU proof time on the fixed corpus:
|
||||
|
||||
- 1024×768: 41.64 ms → 25.16 ms (~39.6% reduction)
|
||||
- 2048²: 177.70 ms → 117.08 ms (~34.1% reduction)
|
||||
|
||||
Random validation: 240 views, 160 gate-negative, 0 false negatives versus the existing exact tile proof.
|
||||
|
||||
### 8. Numeric history is no longer permanently resident when exact-grid reuse is unrealistic
|
||||
|
||||
`historyMeta/historySmooth` are allocated only when the render-to-CSS scale ratio has a reduced denominator ≤ 8. Native-grid cases retain the feature; common pixel-budget-scaled cases do not keep 8 B/pixel of history that almost never matches integer-grid pan reuse.
|
||||
|
||||
Measured deterministic pan survey:
|
||||
|
||||
- 1920×1080 → 965×543: 0.2% reuse, history removed (~4 MiB)
|
||||
- 1920×1080 → 1365×768: 0.6% reuse, history removed (~8 MiB)
|
||||
- 1920×1080 → 2731×1536: 0% reuse, history removed (~32 MiB)
|
||||
- 1024×768 native: 100% reuse, history retained
|
||||
- 1024×768 DPR2/native ratio: 100% reuse, history retained
|
||||
|
||||
This is intentionally conditional rather than blanket deletion.
|
||||
|
||||
### 9. Export AA sample textures are lazy
|
||||
|
||||
The four RGBA8 sample textures used only by 2× AA export are no longer allocated by 1× export. At 512² and ring depth 3, 1× export avoids 12 MiB of peak sample-texture allocation.
|
||||
|
||||
## Deliberately not removed
|
||||
|
||||
The following were investigated but are not part of this deletion pass:
|
||||
|
||||
- **Adaptive initial-iteration probe**: 3/48 survey views selected more than 350 iterations; forcing 350 increased the surrogate work metric by +7.2%, +25.3%, and +33.1%.
|
||||
- **Strict periodicity-state updates**: the CPU A/B did not establish a speed win; median B was slower.
|
||||
- **Continuation restart from n=0**: confirmed redundant work, but fixing it requires carrying valid continuation state across primary/reference transitions rather than deleting code.
|
||||
- **Deep/correction workspace lifetime**: potentially large memory saving, but unconditional release can cause GPU allocation churn; no real WebGPU allocation timing was available, so it remains unchanged in this pass.
|
||||
|
||||
## Validation
|
||||
|
||||
Commands:
|
||||
|
||||
```bash
|
||||
node tests/regression.mjs
|
||||
node experiments/ab/variant_check.mjs index.html
|
||||
node experiments/ab/supplement.mjs
|
||||
node experiments/integrated_bench.mjs docs/removal-pass/integrated-bench.json
|
||||
```
|
||||
|
||||
Results:
|
||||
|
||||
- regression: PASS
|
||||
- JavaScript syntax: PASS
|
||||
- WGSL kernels: 19 + bundle version
|
||||
- exact-interior gate randomized test: 0 false negatives / 240 views
|
||||
- FAST coverage semantic test: 0 hole cases / 200 randomized cases
|
||||
- integrated CPU benchmark: completed; existing periodicity/series/sparse/multi-reference/CPU-fallback paths remain operational
|
||||
|
||||
Real WebGPU frame-time p50/p95 remains unmeasured because the container cannot start a usable WebGPU adapter.
|
||||
|
||||
## Source-size impact
|
||||
|
||||
Compared with the preceding integrated build:
|
||||
|
||||
- `index.html`: 223,672 → 224,559 bytes (+887 bytes)
|
||||
- gzip-9: 57,375 → 57,744 bytes (+369 bytes)
|
||||
|
||||
The file is slightly larger because the spatial and allocation eligibility gates add code. The intended gains are runtime computation, transfer traffic, and memory residency rather than download size.
|
||||
|
||||
## Integrity
|
||||
|
||||
Removal-build `index.html` SHA-256:
|
||||
|
||||
`e69b91ead027927d977a065d27d9e27629110122df20aa470eab877e23f77ac7`
|
||||
163
docs/removal-pass/index.diff
Normal file
163
docs/removal-pass/index.diff
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
--- /mnt/data/mandelbrot_integrated/index.html 2026-09-29 09:15:05.708978122 +0000
|
||||
+++ index.html 2026-09-29 09:26:37.218893530 +0000
|
||||
@@ -268,9 +268,6 @@
|
||||
if(p.unknownOnly!=0u){
|
||||
let prior=fieldMeta[out];let cls=(prior>>28u)&3u;
|
||||
if(cls!=FIELD_UNKNOWN){return;}
|
||||
- let reason=(prior>>20u)&0xffu;
|
||||
- // mode 2 repairs only untouched coverage holes (zero-filled metadata).
|
||||
- if(p.unknownOnly==2u && reason!=0u){return;}
|
||||
}
|
||||
let gx=f32(p.tileX+gid.x)+p.sampleX;
|
||||
let gy=f32(p.tileY+gid.y)+p.sampleY;
|
||||
@@ -915,7 +912,7 @@
|
||||
};
|
||||
function normalizeQuality(value){return value==='fast'||value==='power'?'fast':value==='high'||value==='fine'||value==='validate'?'high':'standard'}
|
||||
function applyQuality(value){state.quality=normalizeQuality(value);state.continuationBudget=QUALITY_PRESETS[state.quality].iterations}
|
||||
-const state={bits:INITIAL_BITS,re:0n,im:0n,span:0n,baseIter:350,adaptive:true,continuationBudget:16384,quality:'standard',palette:0,cycle:.008,shift:.18,colorAuto:false,colorRevision:0,token:0,rendering:false,recoloring:false,recolorPending:false,dirty:true,renderClock:null,drawState:'REPROJECTED',frameView:null,fieldView:null,pointerActive:false,wheelActive:false,unresolved:0,unknownReasons:null,gpuError:'',gpuInitFailed:false,lastInteraction:performance.now(),uiHidden:false,temporalFill:false,deferNumericPublish:false,heuristicInterior:0,cpuFrameRgba:null};
|
||||
+const state={bits:INITIAL_BITS,re:0n,im:0n,span:0n,baseIter:350,adaptive:true,continuationBudget:16384,quality:'standard',palette:0,cycle:.008,shift:.18,colorAuto:false,colorRevision:0,token:0,rendering:false,recoloring:false,recolorPending:false,dirty:true,renderClock:null,drawState:'REPROJECTED',frameView:null,fieldView:null,pointerActive:false,wheelActive:false,unresolved:0,unknownReasons:null,gpuError:'',gpuInitFailed:false,lastInteraction:performance.now(),uiHidden:false,temporalFill:false,deferNumericPublish:false,heuristicInterior:0};
|
||||
let renderer=null,rendererInitPromise=null,raf=0,settleTimer=0,lastWrittenHash='',navigationHash='';
|
||||
|
||||
// ── exact fixed-point view state ─────────────────────────────────────────
|
||||
@@ -1171,6 +1168,12 @@
|
||||
function buf(device,size,usage,label){return device.createBuffer({label,size:Math.max(4,Math.ceil(size/4)*4),usage})}
|
||||
function destroy(x){if(x&&x.destroy)try{x.destroy()}catch{}}
|
||||
function writeU32F32(size,writer){const a=new ArrayBuffer(size),d=new DataView(a);writer(d);return a}
|
||||
+
|
||||
+function numericHistoryScaleEligible(w,h){
|
||||
+ const cw=Math.max(1,Math.round(canvas.clientWidth||w)),ch=Math.max(1,Math.round(canvas.clientHeight||h));
|
||||
+ const gcd=(a,b)=>{a=Math.abs(a);b=Math.abs(b);while(b){const t=a%b;a=b;b=t}return a||1};
|
||||
+ return cw/gcd(w,cw)<=8&&ch/gcd(h,ch)<=8;
|
||||
+}
|
||||
class WebGpuRenderer{
|
||||
constructor(adapter,device){
|
||||
this.device=device;
|
||||
@@ -1210,7 +1213,7 @@
|
||||
frameDestroy(){this.releaseColorSource();if(!this.frame)return;this.clearBindGroupCache();for(const ring of Object.values(this.frame.readbackRings||{}))for(const b of ring.buffers)destroy(b);for(const slot of this.frame.precisionUploadRing?.slots||[])destroy(slot.buffer);for(const k of ['meta','smooth','historyMeta','historySmooth','unresolved','deepQueueStats','deepBucketState','deepQueue','deepIndirect','deepActiveState','deepActiveQueueA','deepActiveQueueB','deepActiveCountA','deepActiveCountB','deepActiveIndirect','deepActiveRead','deepActiveMark','deepActivePixel','numericParams','statsParams','symmetryParams','colorParams','presentParams'])destroy(this.frame[k]);for(const t of new Set([this.frame.front,this.frame.back,this.frame.history,this.frame.spare]))destroy(t);this.frame=null;this.historyView=null;this.historyColorKey='';this.frontColorKey='';this.historyReady=false;this.numericHistoryView=null;this.numericHistoryIter=0;this.numericHistoryReady=false}
|
||||
ensureFrame(w,h){
|
||||
const n=w*h;if(this.frame&&this.frame.w===w&&this.frame.h===h)return this.frame;this.frameDestroy();const d=this.device,B=GPUBufferUsage,T=GPUTextureUsage,colorUsage=T.STORAGE_BINDING|T.TEXTURE_BINDING|T.COPY_SRC,front=d.createTexture({size:[w,h],format:'rgba8unorm',usage:colorUsage,label:'front-color'}),back=d.createTexture({size:[w,h],format:'rgba8unorm',usage:colorUsage,label:'back-color'}),history=d.createTexture({size:[w,h],format:'rgba8unorm',usage:colorUsage,label:'stable-history'});
|
||||
- this.frame={w,h,n,meta:buf(d,n*4,B.STORAGE|B.COPY_SRC|B.COPY_DST,'field-meta'),smooth:buf(d,n*4,B.STORAGE|B.COPY_SRC|B.COPY_DST,'field-smooth'),historyMeta:buf(d,n*4,B.COPY_SRC|B.COPY_DST,'stable-history-meta'),historySmooth:buf(d,n*4,B.COPY_SRC|B.COPY_DST,'stable-history-smooth'),unresolved:buf(d,UNRESOLVED_BYTES,B.STORAGE|B.COPY_SRC|B.COPY_DST,'unresolved-count'),deepQueueStats:null,deepBucketState:null,deepQueue:null,deepIndirect:null,deepActiveCapacity:0,deepActiveState:null,deepActiveQueueA:null,deepActiveQueueB:null,deepActiveCountA:null,deepActiveCountB:null,deepActiveIndirect:null,deepActiveRead:null,deepActiveMark:null,deepActivePixel:null,readbackRings:{},numericParams:buf(d,NUMERIC_PARAM_BYTES,B.UNIFORM|B.COPY_DST,'numeric-params'),statsParams:buf(d,16,B.UNIFORM|B.COPY_DST,'unknown-stats-params'),symmetryParams:buf(d,16,B.UNIFORM|B.COPY_DST,'symmetry-copy-params'),colorParams:buf(d,32,B.UNIFORM|B.COPY_DST,'color-params'),presentParams:buf(d,48,B.UNIFORM|B.COPY_DST,'present-params'),front,back,history,spare:history};return this.frame;
|
||||
+ const keepNumericHistory=numericHistoryScaleEligible(w,h);this.frame={w,h,n,meta:buf(d,n*4,B.STORAGE|B.COPY_SRC|B.COPY_DST,'field-meta'),smooth:buf(d,n*4,B.STORAGE|B.COPY_SRC|B.COPY_DST,'field-smooth'),historyMeta:keepNumericHistory?buf(d,n*4,B.COPY_SRC|B.COPY_DST,'stable-history-meta'):null,historySmooth:keepNumericHistory?buf(d,n*4,B.COPY_SRC|B.COPY_DST,'stable-history-smooth'):null,unresolved:buf(d,UNRESOLVED_BYTES,B.STORAGE|B.COPY_SRC|B.COPY_DST,'unresolved-count'),deepQueueStats:null,deepBucketState:null,deepQueue:null,deepIndirect:null,deepActiveCapacity:0,deepActiveState:null,deepActiveQueueA:null,deepActiveQueueB:null,deepActiveCountA:null,deepActiveCountB:null,deepActiveIndirect:null,deepActiveRead:null,deepActiveMark:null,deepActivePixel:null,readbackRings:{},numericParams:buf(d,NUMERIC_PARAM_BYTES,B.UNIFORM|B.COPY_DST,'numeric-params'),statsParams:buf(d,16,B.UNIFORM|B.COPY_DST,'unknown-stats-params'),symmetryParams:buf(d,16,B.UNIFORM|B.COPY_DST,'symmetry-copy-params'),colorParams:buf(d,32,B.UNIFORM|B.COPY_DST,'color-params'),presentParams:buf(d,48,B.UNIFORM|B.COPY_DST,'present-params'),front,back,history,spare:history};return this.frame;
|
||||
}
|
||||
frameReadback(kind,size){const f=this.frame;if(!f)throw new Error('frame readback requested without a frame');let ring=f.readbackRings[kind];if(!ring){const B=GPUBufferUsage;ring=f.readbackRings[kind]={cursor:0,buffers:Array.from({length:3},(_,i)=>buf(this.device,size,B.COPY_DST|B.MAP_READ,kind+'-readback-'+i))}}for(let tries=0;tries<ring.buffers.length;tries++){const i=ring.cursor++%ring.buffers.length,b=ring.buffers[i];if(!b.mapState||b.mapState==='unmapped')return b}throw new Error(kind+' readback ring is saturated')}
|
||||
ensureDeepBucketState(f=this.frame){if(!f)return null;if(!f.deepBucketState){const d=this.device,B=GPUBufferUsage;f.deepBucketState=buf(d,DEEP_BUCKET_STATE_BYTES,B.STORAGE|B.COPY_SRC|B.COPY_DST,'deep-unknown-bucket-state')}return f}
|
||||
@@ -1221,8 +1224,9 @@
|
||||
destroyExportWorkspace(ws){if(!ws)return;this.clearBindGroupCache();for(const k of ['meta','smooth','cbuf','tex','read'])destroy(ws[k]);for(const b of ws.unresolveds)destroy(b);for(const b of ws.pbufs)destroy(b);for(const t of ws.samples)destroy(t)}
|
||||
ensureExportWorkspace(slot=0,S=this.exportTileSize()){
|
||||
const old=this.exportWs[slot];if(old&&old.size===S)return old;if(old)this.destroyExportWorkspace(old);const d=this.device,B=GPUBufferUsage,T=GPUTextureUsage,n=S*S,bpr=S*4,pixelBytes=bpr*S,label='export-'+slot+'-';
|
||||
- const ws={slot,size:S,meta:buf(d,n*4,B.STORAGE|B.COPY_DST,label+'meta'),smooth:buf(d,n*4,B.STORAGE|B.COPY_DST,label+'smooth'),unresolveds:Array.from({length:4},(_,i)=>buf(d,UNRESOLVED_BYTES,B.STORAGE|B.COPY_SRC|B.COPY_DST,label+'unresolved-'+i)),pbufs:Array.from({length:4},(_,i)=>buf(d,NUMERIC_PARAM_BYTES,B.UNIFORM|B.COPY_DST,label+'numeric-'+i)),cbuf:buf(d,32,B.UNIFORM|B.COPY_DST,label+'color'),samples:Array.from({length:4},(_,i)=>d.createTexture({label:label+'sample-'+i,size:[S,S],format:'rgba8unorm',usage:T.STORAGE_BINDING|T.TEXTURE_BINDING})),tex:d.createTexture({label:label+'resolve',size:[S,S],format:'rgba8unorm',usage:T.STORAGE_BINDING|T.COPY_SRC}),read:buf(d,pixelBytes+4*UNRESOLVED_BYTES,B.COPY_DST|B.MAP_READ,label+'readback')};this.exportWs[slot]=ws;return ws;
|
||||
+ const ws={slot,size:S,meta:buf(d,n*4,B.STORAGE|B.COPY_DST,label+'meta'),smooth:buf(d,n*4,B.STORAGE|B.COPY_DST,label+'smooth'),unresolveds:Array.from({length:4},(_,i)=>buf(d,UNRESOLVED_BYTES,B.STORAGE|B.COPY_SRC|B.COPY_DST,label+'unresolved-'+i)),pbufs:Array.from({length:4},(_,i)=>buf(d,NUMERIC_PARAM_BYTES,B.UNIFORM|B.COPY_DST,label+'numeric-'+i)),cbuf:buf(d,32,B.UNIFORM|B.COPY_DST,label+'color'),samples:[],tex:d.createTexture({label:label+'resolve',size:[S,S],format:'rgba8unorm',usage:T.STORAGE_BINDING|T.COPY_SRC}),read:buf(d,pixelBytes+4*UNRESOLVED_BYTES,B.COPY_DST|B.MAP_READ,label+'readback')};this.exportWs[slot]=ws;return ws;
|
||||
}
|
||||
+ ensureExportAaSamples(ws){if(ws.samples.length===4)return ws.samples;const d=this.device,T=GPUTextureUsage;for(let i=0;i<4;i++)ws.samples.push(d.createTexture({label:'export-'+ws.slot+'-sample-'+i,size:[ws.size,ws.size],format:'rgba8unorm',usage:T.STORAGE_BINDING|T.TEXTURE_BINDING}));return ws.samples}
|
||||
exportWorkspaceDestroy(){for(const ws of this.exportWs)this.destroyExportWorkspace(ws);this.exportWs=[]}
|
||||
setDeepContext(ctx){
|
||||
if(this.deepCtx?.key===ctx.key&&this.deepCtx.refLen>=ctx.refLen)return;
|
||||
@@ -1296,7 +1300,6 @@
|
||||
if(input)d.queue.writeBuffer(input,0,mask);const e=d.createCommandEncoder({label:'history-seed-and-exact-interior'});if(historySeed)this.encodeNumericHistorySeed(e,historySeed);else if(clear){e.clearBuffer(f.meta);e.clearBuffer(f.smooth)}
|
||||
if(pixels){const bg=d.createBindGroup({layout:this.interiorMask.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:input}},{binding:1,resource:{buffer:f.meta}},{binding:2,resource:{buffer:f.smooth}}]});
|
||||
const pass=e.beginComputePass();pass.setPipeline(this.interiorMask);pass.setBindGroup(0,bg);pass.dispatchWorkgroups(Math.ceil(f.w/8),Math.ceil(f.h/8));pass.end()}
|
||||
- this.encodeUnknownStats(e,{meta:f.meta,unresolved:f.unresolved,n:f.n});
|
||||
return await this.submitStage(e,historySeed?'numeric history + exposed interior':'exact interior before primary',token,'correction')===null?0:pixels;
|
||||
}finally{destroy(input)}
|
||||
}
|
||||
@@ -1347,7 +1350,7 @@
|
||||
const tileWidth=this.numericTileShape(f.w,iter).width;let rows=this.initialNumericRows(tileWidth,iter),burstCount=0,burstStart=performance.now(),lastStage='',predictedStripMs=22,burstLimit=this.numericBurstLimit(predictedStripMs),burstPredictedMs=0,lastCompletedRows=rows;
|
||||
const flushBurst=async remaining=>{if(!burstCount)return true;const ms=await this.waitStageBurst(lastStage,token,burstStart,burstCount);if(ms===null)return false;rows=this.adaptNumericRows(lastCompletedRows,ms,remaining);predictedStripMs=.65*predictedStripMs+.35*ms;burstLimit=this.numericBurstLimit(predictedStripMs);burstCount=0;burstPredictedMs=0;burstStart=performance.now();return true};
|
||||
for(let y=0;y<computeH;){if(token!==state.token)return false;const th=Math.min(rows,computeH-y);lastCompletedRows=th;for(let x=0;x<f.w;x+=tileWidth){if(token!==state.token)return false;const tw=Math.min(tileWidth,f.w-x),base=y*f.w+x,params=this.fastParams(tw,th,f.w,f.h,x,y,iter,snap,.5,.5,refX,refY,f.w,base,reuseResolved?1:0);d.queue.writeBuffer(f.numericParams,0,params);const e=d.createCommandEncoder({label:'fast-numeric-strip-'+y});if(x===0&&y===0){if(historySeed)this.encodeNumericHistorySeed(e,historySeed);else if(!reusePriorField&&!preserveInterior){e.clearBuffer(f.meta);e.clearBuffer(f.smooth)}}this.encodeFastPostStatsNumeric(e,{pbuf:f.numericParams,meta:f.meta,smooth:f.smooth,w:tw,h:th});lastStage='fast primary rows '+y+'..'+(y+th-1);this.submitStageDeferred(e,lastStage,'numeric');burstCount++;burstPredictedMs+=predictedStripMs;const budgetMs=this.numericInFlightBudgetMs();if(burstCount>=burstLimit||burstPredictedMs>=budgetMs||performance.now()-burstStart>=budgetMs){if(!await flushBurst(computeH-(y+th)))return false}}y+=th}
|
||||
- if(!await flushBurst(0))return false;d.queue.writeBuffer(f.numericParams,0,this.fastParams(f.w,f.h,f.w,f.h,0,0,iter,snap,.5,.5,refX,refY,f.w,0,2));if(!deferColor)d.queue.writeBuffer(f.colorParams,0,this.colorParamsData(f.w,f.h,iter,colorStyle));const e=d.createCommandEncoder({label:deferColor?'fast-coverage-repair':'fast-coverage-repair-and-color'});if(mirror)this.encodeSymmetryCopy(e,{meta:f.meta,smooth:f.smooth,w:f.w,h:f.h});this.encodeFastNumeric(e,{pbuf:f.numericParams,meta:f.meta,smooth:f.smooth,unresolved:f.unresolved,w:f.w,h:f.h});this.encodeUnknownStats(e,{meta:f.meta,unresolved:f.unresolved,n:f.n});if(!deferColor){const cbg=this.cachedBindGroup(this.color,[{binding:0,resource:{buffer:f.colorParams}},{binding:1,resource:{buffer:f.meta}},{binding:2,resource:{buffer:f.smooth}},{binding:3,resource:this.cachedTextureView(this.colorTarget(f))}]),cp=e.beginComputePass();cp.setPipeline(this.color);cp.setBindGroup(0,cbg);cp.dispatchWorkgroups(Math.ceil(f.w/8),Math.ceil(f.h/8));cp.end()}const ms=await this.submitStage(e,deferColor?'fast coverage only':'fast coverage + color',token,'numeric');if(ms===null)return false;if(!deferColor){[f.front,f.back]=[f.back,f.front];this.frontColorKey=colorKey}return true
|
||||
+ if(!await flushBurst(0))return false;if(!deferColor)d.queue.writeBuffer(f.colorParams,0,this.colorParamsData(f.w,f.h,iter,colorStyle));const e=d.createCommandEncoder({label:deferColor?'fast-post-stats':'fast-post-stats-and-color'});if(mirror)this.encodeSymmetryCopy(e,{meta:f.meta,smooth:f.smooth,w:f.w,h:f.h});this.encodeUnknownStats(e,{meta:f.meta,unresolved:f.unresolved,n:f.n});if(!deferColor){const cbg=this.cachedBindGroup(this.color,[{binding:0,resource:{buffer:f.colorParams}},{binding:1,resource:{buffer:f.meta}},{binding:2,resource:{buffer:f.smooth}},{binding:3,resource:this.cachedTextureView(this.colorTarget(f))}]),cp=e.beginComputePass();cp.setPipeline(this.color);cp.setBindGroup(0,cbg);cp.dispatchWorkgroups(Math.ceil(f.w/8),Math.ceil(f.h/8));cp.end()}const ms=await this.submitStage(e,deferColor?'fast post-stats only':'fast post-stats + color',token,'numeric');if(ms===null)return false;if(!deferColor){[f.front,f.back]=[f.back,f.front];this.frontColorKey=colorKey}return true
|
||||
}
|
||||
async computeFrame(snap,iter,token,referencePixel=null,fastExtended=false,fastContext=null,preserveInterior=false,preseededHistory=false,deferColor=false){
|
||||
await this.ready;if(fastExtended)await this.ensureFastPipelines();if(token!==state.token)return false;const f=this.ensureFrame(canvas.width,canvas.height),d=this.device,colorStyle=colorStyleSnapshot(),colorKey=colorStyleKey(colorStyle);
|
||||
@@ -1381,7 +1384,7 @@
|
||||
return true;
|
||||
}
|
||||
commitNumericHistory(view=state.frameView,iter=state.fieldView?.iter??maxIter()){
|
||||
- const f=this.frame;if(!f||!view||state.fieldView?.complete===false)return false;const e=this.device.createCommandEncoder({label:'stable-numeric-history'});e.copyBufferToBuffer(f.meta,0,f.historyMeta,0,f.n*4);e.copyBufferToBuffer(f.smooth,0,f.historySmooth,0,f.n*4);this.device.queue.submit([e.finish()]);this.numericHistoryView={...view,w:f.w,h:f.h};this.numericHistoryIter=iter;this.numericHistoryReady=true;return true;
|
||||
+ const f=this.frame;if(!f||!f.historyMeta||!f.historySmooth||!view||state.fieldView?.complete===false){this.numericHistoryReady=false;return false}const e=this.device.createCommandEncoder({label:'stable-numeric-history'});e.copyBufferToBuffer(f.meta,0,f.historyMeta,0,f.n*4);e.copyBufferToBuffer(f.smooth,0,f.historySmooth,0,f.n*4);this.device.queue.submit([e.finish()]);this.numericHistoryView={...view,w:f.w,h:f.h};this.numericHistoryIter=iter;this.numericHistoryReady=true;return true;
|
||||
}
|
||||
captureColorSource(view=state.frameView,needed=state.colorAuto||state.rendering){
|
||||
this.colorSourceVisible=false;
|
||||
@@ -1413,7 +1416,7 @@
|
||||
}
|
||||
async recolor(token,iter=state.fieldView?.iter??maxIter()){await this.ready;if(token!==state.token||!this.frame)return false;const f=this.frame,d=this.device,colorStyle=colorStyleSnapshot(),colorKey=colorStyleKey(colorStyle);d.queue.writeBuffer(f.colorParams,0,this.colorParamsData(f.w,f.h,iter,colorStyle));const e=d.createCommandEncoder(),bg=this.cachedBindGroup(this.color,[{binding:0,resource:{buffer:f.colorParams}},{binding:1,resource:{buffer:f.meta}},{binding:2,resource:{buffer:f.smooth}},{binding:3,resource:this.cachedTextureView(this.colorTarget(f))}]),p=e.beginComputePass();p.setPipeline(this.color);p.setBindGroup(0,bg);p.dispatchWorkgroups(Math.ceil(f.w/8),Math.ceil(f.h/8));p.end();d.queue.submit([e.finish()]);await d.queue.onSubmittedWorkDone();if(token!==state.token)return false;[f.front,f.back]=[f.back,f.front];this.frontColorKey=colorKey;return true}
|
||||
presentTransform(view=state.frameView){if(!this.frame||!view)return{scaleX:1,scaleY:1,offsetX:0,offsetY:0};const cur=snapshot(),b=Math.max(cur.bits,view.bits),cs=align(cur.span,cur.bits,b),ps=align(view.span,view.bits,b),dr=align(cur.re,cur.bits,b)-align(view.re,view.bits,b),di=align(cur.im,cur.bits,b)-align(view.im,view.bits,b),scale=fixedRatio(cs,ps);return{scaleX:scale,scaleY:scale,offsetX:fixedRatio(dr,ps),offsetY:-fixedRatio(di,ps)*this.frame.w/Math.max(1,this.frame.h)}}
|
||||
- commitHistory(view=state.frameView){if(!this.frame||!view||state.fieldView?.complete===false)return false;const f=this.frame;if(f.history!==f.front){f.history=f.front;this.clearBindGroupCache()}this.historyView=view;this.historyColorKey=this.frontColorKey;this.historyReady=true;this.commitNumericHistory(view,state.fieldView?.iter??maxIter());return true}
|
||||
+ commitHistory(view=state.frameView,commitNumeric=true){if(!this.frame||!view||state.fieldView?.complete===false)return false;const f=this.frame;if(f.history!==f.front){f.history=f.front;this.clearBindGroupCache()}this.historyView=view;this.historyColorKey=this.frontColorKey;this.historyReady=true;if(commitNumeric)this.commitNumericHistory(view,state.fieldView?.iter??maxIter());return true}
|
||||
presentFrame(transform=this.presentTransform()){if(!this.frame)return;const colored=this.colorSourceVisible&&this.colorSource;if(colored)transform=this.presentTransform(colored.view);const f=this.frame,d=this.device,pb=f.presentParams,interactive=state.pointerActive||state.wheelActive,finite=Number.isFinite(transform?.scaleX)&&Number.isFinite(transform?.scaleY)&&Number.isFinite(transform?.offsetX)&&Number.isFinite(transform?.offsetY)&&transform.scaleX>0&&transform.scaleY>0,ft=finite?transform:{scaleX:1,scaleY:1,offsetX:0,offsetY:0},historyCandidate=!!(!colored&&state.temporalFill&&this.historyReady&&this.historyView&&this.historyColorKey===colorStyleKey()),rawHistory=historyCandidate?this.presentTransform(this.historyView):{scaleX:1,scaleY:1,offsetX:0,offsetY:0},useHistory=!!(historyCandidate&&reprojectionSafe(rawHistory,2.25,.45)),ht=useHistory?rawHistory:{scaleX:1,scaleY:1,offsetX:0,offsetY:0};d.queue.writeBuffer(pb,0,new Float32Array([ft.scaleX,ft.scaleY,ft.offsetX,ft.offsetY,ht.scaleX,ht.scaleY,ht.offsetX,ht.offsetY,useHistory?1:0,0,0,0]));const bg=this.cachedBindGroup(this.present,[{binding:0,resource:interactive?this.reprojectSampler:this.sampler},{binding:1,resource:this.cachedTextureView(colored?colored.texture:f.front)},{binding:2,resource:this.cachedTextureView(f.history)},{binding:3,resource:{buffer:pb}}]),e=d.createCommandEncoder(),pass=e.beginRenderPass({colorAttachments:[{view:this.context.getCurrentTexture().createView(),clearValue:{r:0,g:0,b:0,a:1},loadOp:'clear',storeOp:'store'}]});pass.setPipeline(this.present);pass.setBindGroup(0,bg);pass.draw(3);pass.end();d.queue.submit([e.finish()])}
|
||||
async readMeta(indices){if(!this.frame||!indices.length)return new Uint32Array();const d=this.device,B=GPUBufferUsage,r=buf(d,indices.length*4,B.COPY_DST|B.MAP_READ),e=d.createCommandEncoder();for(let i=0;i<indices.length;i++)e.copyBufferToBuffer(this.frame.meta,indices[i]*4,r,i*4,4);d.queue.submit([e.finish()]);await r.mapAsync(GPUMapMode.READ);const out=new Uint32Array(r.getMappedRange().slice(0));r.unmap();destroy(r);return out}
|
||||
async readNumericalIndices(token){
|
||||
@@ -1504,7 +1507,7 @@
|
||||
}
|
||||
async renderTileRGBA2x({snap,iter,fastContext=null,fullW,fullH,tileX,tileY,w,h,strict=false,workspaceSlot=0,workspaceSize=this.exportTileSize()}){
|
||||
if(w*h*Math.max(1,iter)>48000000){const shape=this.numericTileShape(w,iter),out=new Uint8ClampedArray(w*h*4),generation=state.token;let unresolved=0;for(let y=0;y<h;y+=shape.rows)for(let x=0;x<w;x+=shape.width){if(generation!==state.token||exportJob.cancelled&&exportJob.active)throw new Error('cancelled');const tw=Math.min(shape.width,w-x),th=Math.min(shape.rows,h-y),part=await this.renderTileRGBA2x({snap,iter,fastContext,fullW,fullH,strict,workspaceSlot,workspaceSize,tileX:tileX+x,tileY:tileY+y,w:tw,h:th});for(let row=0;row<th;row++)out.set(part.rgba.subarray(row*tw*4,(row+1)*tw*4),((y+row)*w+x)*4);unresolved+=part.unresolved}return{rgba:out,unresolved}}
|
||||
- await this.ready;await this.ensureAaPipeline();const ws=this.ensureExportWorkspace(workspaceSlot,workspaceSize);if(w>ws.size||h>ws.size)throw new Error('export tile exceeds reusable workspace');const fastExtended=!!fastContext;if(fastExtended){await this.ensureFastPipelines();this.setFastContext(fastContext)}const d=this.device,meta=ws.meta,smooth=ws.smooth,encoder=d.createCommandEncoder({label:'export-aa2x'}),offsets=[[.25,.25],[.75,.25],[.25,.75],[.75,.75]],ca=this.colorParamsData(w,h,iter);new DataView(ca).setUint32(12,0,true);d.queue.writeBuffer(ws.cbuf,0,ca);for(let si=0;si<4;si++){const [sampleX,sampleY]=offsets[si],pbuf=ws.pbufs[si],unresolved=ws.unresolveds[si];d.queue.writeBuffer(unresolved,0,new Uint32Array(UNRESOLVED_BYTES/4));if(fastExtended){const ref=referencePixelForSource(fastContext?.source,snap,fullW,fullH);d.queue.writeBuffer(pbuf,0,this.fastParams(w,h,fullW,fullH,tileX,tileY,iter,snap,sampleX,sampleY,ref.x,ref.y,w,0,0,strict?1:0));this.encodeFastNumeric(encoder,{pbuf,meta,smooth,unresolved,w,h})}else{d.queue.writeBuffer(pbuf,0,this.directParams(w,h,fullW,fullH,tileX,tileY,iter,snap,sampleX,sampleY,strict?1:0));this.encodeDirectNumeric(encoder,{pbuf,meta,smooth,w,h})}const cbg=d.createBindGroup({layout:this.color.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:ws.cbuf}},{binding:1,resource:{buffer:meta}},{binding:2,resource:{buffer:smooth}},{binding:3,resource:ws.samples[si].createView()}]}),cp=encoder.beginComputePass();cp.setPipeline(this.color);cp.setBindGroup(0,cbg);cp.dispatchWorkgroups(Math.ceil(w/8),Math.ceil(h/8));cp.end()}const abg=d.createBindGroup({layout:this.aaResolve.getBindGroupLayout(0),entries:[{binding:0,resource:ws.samples[0].createView()},{binding:1,resource:ws.samples[1].createView()},{binding:2,resource:ws.samples[2].createView()},{binding:3,resource:ws.samples[3].createView()},{binding:4,resource:ws.tex.createView()}]}),ap=encoder.beginComputePass();ap.setPipeline(this.aaResolve);ap.setBindGroup(0,abg);ap.dispatchWorkgroups(Math.ceil(w/8),Math.ceil(h/8));ap.end();const bpr=Math.ceil(w*4/256)*256,pixelBytes=bpr*h,statsBytes=4*UNRESOLVED_BYTES;encoder.copyTextureToBuffer({texture:ws.tex},{buffer:ws.read,bytesPerRow:bpr,rowsPerImage:h},{width:w,height:h});for(let si=0;si<4;si++)encoder.copyBufferToBuffer(ws.unresolveds[si],0,ws.read,pixelBytes+si*UNRESOLVED_BYTES,UNRESOLVED_BYTES);d.queue.submit([encoder.finish()]);await ws.read.mapAsync(GPUMapMode.READ,0,pixelBytes+statsBytes);const raw=new Uint8Array(ws.read.getMappedRange(0,pixelBytes+statsBytes)),out=new Uint8ClampedArray(w*h*4);for(let y=0;y<h;y++)out.set(raw.subarray(y*bpr,y*bpr+w*4),y*w*4);let unresolved=0;for(let si=0;si<4;si++)unresolved+=new Uint32Array(raw.buffer,raw.byteOffset+pixelBytes+si*UNRESOLVED_BYTES,UNRESOLVED_BYTES/4)[0]||0;ws.read.unmap();return{rgba:out,unresolved}
|
||||
+ await this.ready;await this.ensureAaPipeline();const ws=this.ensureExportWorkspace(workspaceSlot,workspaceSize);this.ensureExportAaSamples(ws);if(w>ws.size||h>ws.size)throw new Error('export tile exceeds reusable workspace');const fastExtended=!!fastContext;if(fastExtended){await this.ensureFastPipelines();this.setFastContext(fastContext)}const d=this.device,meta=ws.meta,smooth=ws.smooth,encoder=d.createCommandEncoder({label:'export-aa2x'}),offsets=[[.25,.25],[.75,.25],[.25,.75],[.75,.75]],ca=this.colorParamsData(w,h,iter);new DataView(ca).setUint32(12,0,true);d.queue.writeBuffer(ws.cbuf,0,ca);for(let si=0;si<4;si++){const [sampleX,sampleY]=offsets[si],pbuf=ws.pbufs[si],unresolved=ws.unresolveds[si];d.queue.writeBuffer(unresolved,0,new Uint32Array(UNRESOLVED_BYTES/4));if(fastExtended){const ref=referencePixelForSource(fastContext?.source,snap,fullW,fullH);d.queue.writeBuffer(pbuf,0,this.fastParams(w,h,fullW,fullH,tileX,tileY,iter,snap,sampleX,sampleY,ref.x,ref.y,w,0,0,strict?1:0));this.encodeFastNumeric(encoder,{pbuf,meta,smooth,unresolved,w,h})}else{d.queue.writeBuffer(pbuf,0,this.directParams(w,h,fullW,fullH,tileX,tileY,iter,snap,sampleX,sampleY,strict?1:0));this.encodeDirectNumeric(encoder,{pbuf,meta,smooth,w,h})}const cbg=d.createBindGroup({layout:this.color.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:ws.cbuf}},{binding:1,resource:{buffer:meta}},{binding:2,resource:{buffer:smooth}},{binding:3,resource:ws.samples[si].createView()}]}),cp=encoder.beginComputePass();cp.setPipeline(this.color);cp.setBindGroup(0,cbg);cp.dispatchWorkgroups(Math.ceil(w/8),Math.ceil(h/8));cp.end()}const abg=d.createBindGroup({layout:this.aaResolve.getBindGroupLayout(0),entries:[{binding:0,resource:ws.samples[0].createView()},{binding:1,resource:ws.samples[1].createView()},{binding:2,resource:ws.samples[2].createView()},{binding:3,resource:ws.samples[3].createView()},{binding:4,resource:ws.tex.createView()}]}),ap=encoder.beginComputePass();ap.setPipeline(this.aaResolve);ap.setBindGroup(0,abg);ap.dispatchWorkgroups(Math.ceil(w/8),Math.ceil(h/8));ap.end();const bpr=Math.ceil(w*4/256)*256,pixelBytes=bpr*h,statsBytes=4*UNRESOLVED_BYTES;encoder.copyTextureToBuffer({texture:ws.tex},{buffer:ws.read,bytesPerRow:bpr,rowsPerImage:h},{width:w,height:h});for(let si=0;si<4;si++)encoder.copyBufferToBuffer(ws.unresolveds[si],0,ws.read,pixelBytes+si*UNRESOLVED_BYTES,UNRESOLVED_BYTES);d.queue.submit([encoder.finish()]);await ws.read.mapAsync(GPUMapMode.READ,0,pixelBytes+statsBytes);const raw=new Uint8Array(ws.read.getMappedRange(0,pixelBytes+statsBytes)),out=new Uint8ClampedArray(w*h*4);for(let y=0;y<h;y++)out.set(raw.subarray(y*bpr,y*bpr+w*4),y*w*4);let unresolved=0;for(let si=0;si<4;si++)unresolved+=new Uint32Array(raw.buffer,raw.byteOffset+pixelBytes+si*UNRESOLVED_BYTES,UNRESOLVED_BYTES/4)[0]||0;ws.read.unmap();return{rgba:out,unresolved}
|
||||
}
|
||||
destroy(){this.frameDestroy();this.exportWorkspaceDestroy();this.destroyDeepContext();this.destroyFastContext()}
|
||||
}
|
||||
@@ -1514,6 +1517,16 @@
|
||||
// sparse/moderate failures go directly to local references.
|
||||
// Integer interval tests over exact rational pixel coordinates. No f32
|
||||
// rounding, periodic-orbit guess, or agreement of two precisions is a proof.
|
||||
+
|
||||
+function knownInteriorViewportMayOverlap(snap,width,height){
|
||||
+ const unit=1n<<BigInt(snap.bits),bw=BigInt(Math.max(1,width)),bh=BigInt(Math.max(1,height));
|
||||
+ const xLo=2n*snap.re-snap.span,xHi=2n*snap.re+snap.span,xDen=2n*unit;
|
||||
+ const yLo=2n*bw*snap.im-snap.span*bh,yHi=2n*bw*snap.im+snap.span*bh,yDen=2n*bw*unit;
|
||||
+ const axis=(lo,hi,den,lp,lq,hp,hq)=>hi*BigInt(lq)>=BigInt(lp)*den&&lo*BigInt(hq)<=BigInt(hp)*den;
|
||||
+ const cardioid=axis(xLo,xHi,xDen,-3,4,3,8)&&axis(yLo,yHi,yDen,-2,3,2,3);
|
||||
+ const bulb=axis(xLo,xHi,xDen,-5,4,-3,4)&&axis(yLo,yHi,yDen,-1,4,1,4);
|
||||
+ return cardioid||bulb;
|
||||
+}
|
||||
function certifiedInteriorTiles(snap,width,height,tile=32,historySeed=null){
|
||||
const columns=Math.ceil(width/tile),rows=Math.ceil(height/tile),mask=new Uint32Array(4+columns*rows);
|
||||
mask.set([width,height,tile,columns]);let pixels=0;
|
||||
@@ -1715,7 +1728,7 @@
|
||||
constructor(){this.workers=[];this.serial=0;this.pending=new Map();this.failed=false;this.ctx=null}
|
||||
ensure(){if(this.workers.length)return true;if(this.failed||typeof Worker==='undefined'||typeof Blob==='undefined')return false;try{this.ctx=canvas.getContext('2d');if(!this.ctx)throw new Error('2D canvas unavailable');const count=Math.max(1,Math.min(4,(Number(navigator.hardwareConcurrency)||4)-1));for(let i=0;i<count;i++){const url=URL.createObjectURL(new Blob([cpuFallbackWorkerSource()],{type:'text/javascript'})),w=new Worker(url);w.__url=url;w.onmessage=e=>{const d=e.data,p=this.pending.get(d.id);if(!p)return;this.pending.delete(d.id);d.type==='error'?p.reject(new Error(d.error)):p.resolve(d)};w.onerror=e=>{this.failed=true;for(const p of this.pending.values())p.reject(new Error(e.message||'CPU fallback worker error'));this.pending.clear()};this.workers.push(w)}return true}catch(e){this.failed=true;return false}}
|
||||
request(worker,payload){const id=++this.serial;return new Promise((resolve,reject)=>{this.pending.set(id,{resolve,reject});worker.postMessage({...payload,id})})}
|
||||
- async render(snap,iter,token){if(!this.ensure())throw new Error('CPU fallback workerを作成できません');const w=canvas.width,h=canvas.height,re=fixedNum(snap.re,snap.bits),im=fixedNum(snap.im,snap.bits),span=fixedNum(snap.span,snap.bits),step=span/Math.max(1,w);if(!Number.isFinite(re)||!Number.isFinite(im)||!Number.isFinite(span)||span<=0||step===0||(re!==0&&re+step===re)||(im!==0&&im+step===im))throw new Error('CPU fallback の倍精度範囲を超えています');const style=colorStyleSnapshot(),jobs=[],rows=Math.ceil(h/this.workers.length);for(let i=0;i<this.workers.length;i++){const y0=i*rows,y1=Math.min(h,y0+rows);if(y0<y1)jobs.push(this.request(this.workers[i],{type:'render',w,h,y0,y1,maxIter:iter,re,im,span,style}))}const parts=await Promise.all(jobs);if(token!==state.token)throw new Error('cancelled');const rgba=new Uint8ClampedArray(w*h*4);let operationLimit=0,heuristicInterior=0,work=0,escaped=0;for(const part of parts){rgba.set(new Uint8ClampedArray(part.rgba),part.y0*w*4);operationLimit+=part.operationLimit||0;heuristicInterior+=part.heuristicInterior||0;work+=part.work||0;escaped+=part.escaped||0}this.ctx.putImageData(new ImageData(rgba,w,h),0,0);state.cpuFrameRgba=rgba;return{operationLimit,heuristicInterior,work,escaped,workers:this.workers.length}}
|
||||
+ async render(snap,iter,token){if(!this.ensure())throw new Error('CPU fallback workerを作成できません');const w=canvas.width,h=canvas.height,re=fixedNum(snap.re,snap.bits),im=fixedNum(snap.im,snap.bits),span=fixedNum(snap.span,snap.bits),step=span/Math.max(1,w);if(!Number.isFinite(re)||!Number.isFinite(im)||!Number.isFinite(span)||span<=0||step===0||(re!==0&&re+step===re)||(im!==0&&im+step===im))throw new Error('CPU fallback の倍精度範囲を超えています');const style=colorStyleSnapshot(),jobs=[],rows=Math.ceil(h/this.workers.length);for(let i=0;i<this.workers.length;i++){const y0=i*rows,y1=Math.min(h,y0+rows);if(y0<y1)jobs.push(this.request(this.workers[i],{type:'render',w,h,y0,y1,maxIter:iter,re,im,span,style}))}const parts=await Promise.all(jobs);if(token!==state.token)throw new Error('cancelled');const rgba=new Uint8ClampedArray(w*h*4);let operationLimit=0,heuristicInterior=0,work=0,escaped=0;for(const part of parts){rgba.set(new Uint8ClampedArray(part.rgba),part.y0*w*4);operationLimit+=part.operationLimit||0;heuristicInterior+=part.heuristicInterior||0;work+=part.work||0;escaped+=part.escaped||0}this.ctx.putImageData(new ImageData(rgba,w,h),0,0);return{operationLimit,heuristicInterior,work,escaped,workers:this.workers.length}}
|
||||
cancelPending(reason='cancelled'){for(const p of this.pending.values())p.reject(new Error(reason));this.pending.clear();this.destroyWorkers()}
|
||||
destroyWorkers(){for(const w of this.workers){try{w.terminate()}catch{}try{URL.revokeObjectURL(w.__url)}catch{}}this.workers.length=0}
|
||||
destroy(){this.cancelPending('destroyed');this.ctx=null}
|
||||
@@ -1759,9 +1772,9 @@
|
||||
}
|
||||
async function renderFinalFrame(r,snap,iter,token){
|
||||
let decision=chooseBackend(snap,canvas.width),fastExtended=decision.fastExtended;
|
||||
- const historySeed=fastExtended?r.numericHistorySeed(snap,iter,false,true):null,proof=certifiedInteriorTiles(snap,canvas.width,canvas.height,32,historySeed);let certified=0,preserveInterior=false;
|
||||
+ const historySeed=fastExtended?r.numericHistorySeed(snap,iter,false,true):null,proof=knownInteriorViewportMayOverlap(snap,canvas.width,canvas.height)?certifiedInteriorTiles(snap,canvas.width,canvas.height,32,historySeed):{mask:null,pixels:0};let certified=0,preserveInterior=false;
|
||||
if(proof.pixels||historySeed){certified=await r.certifyInterior(snap,token,true,proof,historySeed);if(token!==state.token)return null;preserveInterior=certified>0||!!historySeed}
|
||||
- if(certified===canvas.width*canvas.height){decision={backend:'exact-interior',fastExtended:false,reason:'exact-full-interior'};const stats=await r.readUnresolvedStats();if(token!==state.token)return null;state.unresolved=stats.total;state.unknownReasons=stats.reasons;state.heuristicInterior=0;const painted=await r.recolor(token,iter);if(!painted||token!==state.token)return null;const frontier={stats,iter,minimumIter:iter,rounds:0,processed:0,escaped:0,converged:true,finiteBudgetComplete:true,policy:'exact-interior-proof',membershipCertified:true,applied:true};state.frameView=snap;state.fieldView={...snap,iter,frontier,w:canvas.width,h:canvas.height,fastExtended:false,backend:'exact-interior',referenceKey:'',referenceReused:false,complete:true,membershipCertified:true};r.captureColorSource(snap,state.colorAuto||state.recolorPending);r.presentFrame({scaleX:1,scaleY:1,offsetX:0,offsetY:0});return{decision,referenceReused:false,referencePixel:null,certified}}
|
||||
+ if(certified===canvas.width*canvas.height){decision={backend:'exact-interior',fastExtended:false,reason:'exact-full-interior'};const stats={total:0,heuristicInterior:0,reasons:{errorBound:0,escapeUncertain:0,referenceEnd:0,rebaseGap:0,range:0,operationLimit:0}};state.unresolved=0;state.unknownReasons=stats.reasons;state.heuristicInterior=0;const painted=await r.recolor(token,iter);if(!painted||token!==state.token)return null;const frontier={stats,iter,minimumIter:iter,rounds:0,processed:0,escaped:0,converged:true,finiteBudgetComplete:true,policy:'exact-interior-proof',membershipCertified:true,applied:true};state.frameView=snap;state.fieldView={...snap,iter,frontier,w:canvas.width,h:canvas.height,fastExtended:false,backend:'exact-interior',referenceKey:'',referenceReused:false,complete:true,membershipCertified:true};r.captureColorSource(snap,state.colorAuto||state.recolorPending);r.presentFrame({scaleX:1,scaleY:1,offsetX:0,offsetY:0});return{decision,referenceReused:false,referencePixel:null,certified}}
|
||||
let fastCtx=null,referencePixel=null,referenceReused=false;if(fastExtended){const reuse=reusableFastReference(snap,iter,canvas.width,canvas.height);if(reuse){fastCtx=reuse.ctx;referencePixel=reuse.pixel;referenceReused=true}else{fastCtx=await fastRefs.request(snap,iter,canvas.width,canvas.height);if(token!==state.token)return null;referencePixel=referencePixelForSource(fastCtx.source,snap,canvas.width,canvas.height)}}
|
||||
const deferColor=!!(r.historyReady&&canUseStableReprojection());state.deferNumericPublish=deferColor;const ok=await r.computeFrame(snap,iter,token,referencePixel,fastExtended,fastCtx,preserveInterior,!!historySeed,deferColor);if(!ok||token!==state.token){state.deferNumericPublish=false;return null}if(!deferColor&&!await presentProvisional(r,snap,iter,token,true)){state.deferNumericPublish=false;return null}
|
||||
let stats=await r.readUnresolvedStats();if(token!==state.token)return null;if(fastExtended){const initialFailed=numericalFailureCount(stats);if(initialFailed>0){const ordered=await recoverNumericalCostOrdered(r,snap,iter,token,stats);if(!ordered||token!==state.token){state.deferNumericPublish=false;return null}stats=ordered.stats}state.unresolved=stats.total;state.unknownReasons=stats.reasons;state.heuristicInterior=stats.heuristicInterior||0;if(initialFailed>0&&!deferColor){const painted=await r.recolor(token,iter);if(!painted||token!==state.token){state.deferNumericPublish=false;return null}}}else{state.unresolved=stats.total;state.unknownReasons=stats.reasons}
|
||||
@@ -1819,7 +1832,7 @@
|
||||
// Only promote a colour frame to stable history if it still represents
|
||||
// the newest slider value. Rapid dragging therefore collapses to the
|
||||
// latest value instead of replaying stale colour states.
|
||||
- if(revision===state.colorRevision&&numericFrameComplete()){renderer.commitHistory(state.frameView);state.temporalFill=false}
|
||||
+ if(revision===state.colorRevision&&numericFrameComplete()){renderer.commitHistory(state.frameView,false);state.temporalFill=false}
|
||||
updateStats();
|
||||
// Yield ownership after one pass: a stream of hue changes must not keep
|
||||
// recoloring=true forever and prevent a pending pan from computing.
|
||||
@@ -1863,7 +1876,7 @@
|
||||
function syncColorAutoButton(){const b=$('colorAuto');b.textContent=state.colorAuto?'色相変化停止':'色相変化';b.classList.toggle('on',state.colorAuto);b.setAttribute('aria-pressed',state.colorAuto?'true':'false')}
|
||||
function stopColorAuto(repaint=true){state.colorAuto=false;colorAutoLast=0;if(colorAutoRaf){cancelAnimationFrame(colorAutoRaf);colorAutoRaf=0}syncColorAutoButton();if(repaint)requestRecolor()}
|
||||
function colorAutoStep(now){if(!state.colorAuto){colorAutoRaf=0;return}if(!colorAutoLast)colorAutoLast=now;const dt=Math.min(.1,Math.max(0,(now-colorAutoLast)/1000));colorAutoLast=now;/* Hue animation advances shift directly. No cycle or phase compensation is applied. */state.shift=fract01(state.shift+.09*dt);$('shift').value=String(state.shift);$('shiftO').textContent=state.shift.toFixed(2);if(!state.pointerActive&&!state.wheelActive&&now-colorAutoPaint>=(state.rendering?100:50)){colorAutoPaint=now;requestRecolor()}colorAutoRaf=requestAnimationFrame(colorAutoStep)}
|
||||
-$('colorAuto').onclick=()=>{state.colorAuto=!state.colorAuto;syncColorAutoButton();if(state.colorAuto&&!colorAutoRaf){if(!state.rendering)renderer?.captureColorSource();colorAutoRaf=requestAnimationFrame(colorAutoStep);}else if(!state.colorAuto)stopColorAuto()};
|
||||
+$('colorAuto').onclick=()=>{state.colorAuto=!state.colorAuto;syncColorAutoButton();if(state.colorAuto&&!colorAutoRaf){colorAutoRaf=requestAnimationFrame(colorAutoStep);}else if(!state.colorAuto)stopColorAuto()};
|
||||
$('palette').addEventListener('change',e=>{const pal=Math.max(0,Math.min(PALETTE_MAX,Number(e.target.value)|0));state.palette=VALID_PALETTE_IDS.has(pal)?pal:0;requestRecolor();saveHash()});
|
||||
function applyCycleControl(e){state.cycle=sliderToCycle(e.target.value);$('cycleO').textContent=state.cycle.toFixed(4);requestRecolor()}
|
||||
function applyShiftControl(e){if(state.colorAuto)stopColorAuto();state.shift=Math.max(0,Math.min(1,Number(e.target.value)||0));$('shiftO').textContent=state.shift.toFixed(2);requestRecolor()}
|
||||
@@ -1940,7 +1953,7 @@
|
||||
downloadBlob(blob,base+'.png');downloadBlob(new Blob([JSON.stringify(meta,null,2)],{type:'application/json'}),base+'.json');
|
||||
$('exportStatus').textContent=unresolvedSamples?'保存しました · 未確定sample '+unresolvedSamples+'(sidecar参照)':'PNGと座標メタデータを保存しました。';
|
||||
}catch(e){await png.abort(e);$('exportStatus').textContent=String(e.message)==='cancelled'?'出力を中止しました。':'出力失敗: '+String(e&&e.message||e)}
|
||||
- finally{exportJob.active=false;$('exportStart').disabled=false}
|
||||
+ finally{r.exportWorkspaceDestroy();exportJob.active=false;$('exportStart').disabled=false}
|
||||
}
|
||||
$('png').onclick=()=>{const d=$('exportDialog');$('exportWidth').value=String(canvas.width);$('exportScale').value='1';$('exportProgress').hidden=true;$('exportStatus').textContent='';d.showModal?d.showModal():d.setAttribute('open','')};$('exportScale').onchange=e=>{const s=Number(e.target.value);if(s)$('exportWidth').value=String(exportDimensions().w)};$('exportStart').onclick=runExport;$('exportCancel').onclick=()=>{if(exportJob.active){exportJob.cancelled=true;$('exportStatus').textContent='中止しています…'}else $('exportDialog').close()};$('exportQuick').onclick=()=>canvas.toBlob(blob=>{if(blob)downloadBlob(blob,'mandelbrot-'+Date.now()+(state.fieldView?.complete===false?'-preview':'')+'.png')},'image/png');
|
||||
|
||||
381
docs/removal-pass/integrated-bench.json
Normal file
381
docs/removal-pass/integrated-bench.json
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
{
|
||||
"date": "2026-09-29T09:27:34.132Z",
|
||||
"periodicity": {
|
||||
"w": 96,
|
||||
"h": 64,
|
||||
"maxIter": 8192,
|
||||
"oracleIter": 65536,
|
||||
"rows": [
|
||||
{
|
||||
"view": "overview",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 884420,
|
||||
"heuristicWork": 133404,
|
||||
"workReduction": 0.849162162773343,
|
||||
"candidates": 96,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 104,
|
||||
"heuristicLimit": 8,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 107.84037399999998
|
||||
},
|
||||
{
|
||||
"view": "period3-bulb",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 50331648,
|
||||
"heuristicWork": 460156,
|
||||
"workReduction": 0.9908575216929117,
|
||||
"candidates": 6144,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 6144,
|
||||
"heuristicLimit": 0,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 4596.538675
|
||||
},
|
||||
{
|
||||
"view": "period3-core",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 50331648,
|
||||
"heuristicWork": 442395,
|
||||
"workReduction": 0.991210401058197,
|
||||
"candidates": 6144,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 6144,
|
||||
"heuristicLimit": 0,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 3849.7766140000003
|
||||
},
|
||||
{
|
||||
"view": "seahorse-boundary",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 410111,
|
||||
"heuristicWork": 410111,
|
||||
"workReduction": 0,
|
||||
"candidates": 0,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 0,
|
||||
"heuristicLimit": 0,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 28.11920800000007
|
||||
},
|
||||
{
|
||||
"view": "seahorse-deep",
|
||||
"productionBackend": "fast-extended",
|
||||
"pixels": 6144,
|
||||
"baseWork": 36579905,
|
||||
"heuristicWork": 6167354,
|
||||
"workReduction": null,
|
||||
"candidates": 0,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 4304,
|
||||
"heuristicLimit": 4304,
|
||||
"diagnosticIfForcedDirect": {
|
||||
"workReduction": 0.8314004916087125,
|
||||
"candidates": 4125,
|
||||
"falseCandidates": 12
|
||||
},
|
||||
"ms": 2697.9715419999993
|
||||
},
|
||||
{
|
||||
"view": "exterior",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 686432,
|
||||
"heuristicWork": 137063,
|
||||
"workReduction": 0.8003254510279241,
|
||||
"candidates": 72,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 79,
|
||||
"heuristicLimit": 7,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 44.91841799999929
|
||||
}
|
||||
],
|
||||
"note": "Production DIRECT_F32 rule with two attracting-cycle confirmations. FAST/perturbation periodic early-stop is intentionally disabled."
|
||||
},
|
||||
"series": {
|
||||
"rows": [
|
||||
{
|
||||
"span": 0.00001,
|
||||
"jump": 53,
|
||||
"errorLog2": -22.11596292311006,
|
||||
"maxAbsError": 7.112067409017996e-9,
|
||||
"maxRelativeError": 0.00009415623690713784,
|
||||
"refLen": 1200,
|
||||
"buildMs": 38.390750999999
|
||||
},
|
||||
{
|
||||
"span": 1e-7,
|
||||
"jump": 446,
|
||||
"errorLog2": -22.302471602690176,
|
||||
"maxAbsError": 7.736805567567409e-9,
|
||||
"maxRelativeError": 0.0031991190750368034,
|
||||
"refLen": 1200,
|
||||
"buildMs": 35.28093799999988
|
||||
},
|
||||
{
|
||||
"span": 1e-9,
|
||||
"jump": 930,
|
||||
"errorLog2": -22.038152635655024,
|
||||
"maxAbsError": 7.459933177532973e-9,
|
||||
"maxRelativeError": 0.00001015603159369818,
|
||||
"refLen": 1200,
|
||||
"buildMs": 40.962877999998454
|
||||
},
|
||||
{
|
||||
"span": 1e-11,
|
||||
"jump": 1037,
|
||||
"errorLog2": -23.96377927072575,
|
||||
"maxAbsError": 2.2791360188558774e-9,
|
||||
"maxRelativeError": 0.000004948137247650717,
|
||||
"refLen": 1200,
|
||||
"buildMs": 42.88623700000062
|
||||
}
|
||||
],
|
||||
"note": "Production fast-reference worker output. Error compares packed 4th-order series against direct perturbation over 8 screen-domain deltas."
|
||||
},
|
||||
"sparseActiveState": {
|
||||
"rows": [
|
||||
{
|
||||
"preset": "fast",
|
||||
"pixels": 524288,
|
||||
"activeRatio": 1,
|
||||
"capacity": 524288,
|
||||
"oldBytes": 23068672,
|
||||
"newBytes": 23134208,
|
||||
"reduction": -0.0028409090909091717
|
||||
},
|
||||
{
|
||||
"preset": "fast",
|
||||
"pixels": 524288,
|
||||
"activeRatio": 0.25,
|
||||
"capacity": 164096,
|
||||
"oldBytes": 23068672,
|
||||
"newBytes": 7285760,
|
||||
"reduction": 0.6841708096590908
|
||||
},
|
||||
{
|
||||
"preset": "fast",
|
||||
"pixels": 524288,
|
||||
"activeRatio": 0.1,
|
||||
"capacity": 65792,
|
||||
"oldBytes": 23068672,
|
||||
"newBytes": 2960384,
|
||||
"reduction": 0.8716708096590909
|
||||
},
|
||||
{
|
||||
"preset": "fast",
|
||||
"pixels": 524288,
|
||||
"activeRatio": 0.01,
|
||||
"capacity": 6810,
|
||||
"oldBytes": 23068672,
|
||||
"newBytes": 365176,
|
||||
"reduction": 0.9841700467196378
|
||||
},
|
||||
{
|
||||
"preset": "standard",
|
||||
"pixels": 1048576,
|
||||
"activeRatio": 1,
|
||||
"capacity": 1048576,
|
||||
"oldBytes": 46137344,
|
||||
"newBytes": 46268416,
|
||||
"reduction": -0.0028409090909091717
|
||||
},
|
||||
{
|
||||
"preset": "standard",
|
||||
"pixels": 1048576,
|
||||
"activeRatio": 0.25,
|
||||
"capacity": 327936,
|
||||
"oldBytes": 46137344,
|
||||
"newBytes": 14560256,
|
||||
"reduction": 0.6844149502840908
|
||||
},
|
||||
{
|
||||
"preset": "standard",
|
||||
"pixels": 1048576,
|
||||
"activeRatio": 0.1,
|
||||
"capacity": 131328,
|
||||
"oldBytes": 46137344,
|
||||
"newBytes": 5909504,
|
||||
"reduction": 0.8719149502840909
|
||||
},
|
||||
{
|
||||
"preset": "standard",
|
||||
"pixels": 1048576,
|
||||
"activeRatio": 0.01,
|
||||
"capacity": 13364,
|
||||
"oldBytes": 46137344,
|
||||
"newBytes": 719088,
|
||||
"reduction": 0.9844141873446378
|
||||
},
|
||||
{
|
||||
"preset": "high",
|
||||
"pixels": 4194304,
|
||||
"activeRatio": 1,
|
||||
"capacity": 4194304,
|
||||
"oldBytes": 184549376,
|
||||
"newBytes": 185073664,
|
||||
"reduction": -0.0028409090909091717
|
||||
},
|
||||
{
|
||||
"preset": "high",
|
||||
"pixels": 4194304,
|
||||
"activeRatio": 0.25,
|
||||
"capacity": 1310976,
|
||||
"oldBytes": 184549376,
|
||||
"newBytes": 58207232,
|
||||
"reduction": 0.6845980557528408
|
||||
},
|
||||
{
|
||||
"preset": "high",
|
||||
"pixels": 4194304,
|
||||
"activeRatio": 0.1,
|
||||
"capacity": 524544,
|
||||
"oldBytes": 184549376,
|
||||
"newBytes": 23604224,
|
||||
"reduction": 0.8720980557528409
|
||||
},
|
||||
{
|
||||
"preset": "high",
|
||||
"pixels": 4194304,
|
||||
"activeRatio": 0.01,
|
||||
"capacity": 52685,
|
||||
"oldBytes": 184549376,
|
||||
"newBytes": 2842428,
|
||||
"reduction": 0.9845980080691251
|
||||
}
|
||||
],
|
||||
"note": "Production allocation: 32 B state + 2x4 B queues + 4 B pixel map per active slot, plus 1-bit membership bitmap. Capacity adds 25% + 256 headroom."
|
||||
},
|
||||
"multiReference": {
|
||||
"maxGuidedPasses": 3,
|
||||
"w": 64,
|
||||
"h": 40,
|
||||
"maxIter": 1200,
|
||||
"rows": [
|
||||
{
|
||||
"view": "seahorse-1e-5",
|
||||
"grid": 1,
|
||||
"references": 1,
|
||||
"total": 2560,
|
||||
"refEnd": 0,
|
||||
"mismatchClass": 3,
|
||||
"mismatchIter": 155,
|
||||
"mismatchRate": 0.001171875
|
||||
},
|
||||
{
|
||||
"view": "seahorse-1e-5",
|
||||
"grid": 2,
|
||||
"references": 5,
|
||||
"total": 2560,
|
||||
"refEnd": 0,
|
||||
"mismatchClass": 3,
|
||||
"mismatchIter": 155,
|
||||
"mismatchRate": 0.001171875
|
||||
},
|
||||
{
|
||||
"view": "seahorse-1e-5",
|
||||
"grid": 3,
|
||||
"references": 9,
|
||||
"total": 2560,
|
||||
"refEnd": 0,
|
||||
"mismatchClass": 3,
|
||||
"mismatchIter": 155,
|
||||
"mismatchRate": 0.001171875
|
||||
},
|
||||
{
|
||||
"view": "seahorse-1e-7",
|
||||
"grid": 1,
|
||||
"references": 1,
|
||||
"total": 2560,
|
||||
"refEnd": 0,
|
||||
"mismatchClass": 66,
|
||||
"mismatchIter": 74,
|
||||
"mismatchRate": 0.02578125
|
||||
},
|
||||
{
|
||||
"view": "seahorse-1e-7",
|
||||
"grid": 2,
|
||||
"references": 5,
|
||||
"total": 2560,
|
||||
"refEnd": 0,
|
||||
"mismatchClass": 37,
|
||||
"mismatchIter": 41,
|
||||
"mismatchRate": 0.014453125
|
||||
},
|
||||
{
|
||||
"view": "seahorse-1e-7",
|
||||
"grid": 3,
|
||||
"references": 9,
|
||||
"total": 2560,
|
||||
"refEnd": 0,
|
||||
"mismatchClass": 50,
|
||||
"mismatchIter": 67,
|
||||
"mismatchRate": 0.01953125
|
||||
}
|
||||
],
|
||||
"note": "CPU f32 perturbation locality surrogate. Production selects up to three additional references from failure clusters instead of fixed grids."
|
||||
},
|
||||
"cpuFallback": {
|
||||
"w": 640,
|
||||
"h": 360,
|
||||
"iter": 350,
|
||||
"threads": 4,
|
||||
"one": {
|
||||
"medianMs": 142.60004300000037,
|
||||
"times": [
|
||||
149.66868299999987,
|
||||
152.4433539999991,
|
||||
129.93585699999858,
|
||||
119.70592199999919,
|
||||
129.10428200000024,
|
||||
142.60004300000037,
|
||||
150.36682700000165
|
||||
],
|
||||
"work": 2658084,
|
||||
"escaped": 176648,
|
||||
"operationLimit": 3394,
|
||||
"heuristicInterior": 1654
|
||||
},
|
||||
"multi": {
|
||||
"medianMs": 58.8322790000002,
|
||||
"times": [
|
||||
132.8346849999998,
|
||||
62.70222600000125,
|
||||
52.98496299999897,
|
||||
54.89045199999964,
|
||||
60.89404900000045,
|
||||
58.8322790000002,
|
||||
43.25380600000062
|
||||
],
|
||||
"work": 2658084,
|
||||
"escaped": 176648,
|
||||
"operationLimit": 3394,
|
||||
"heuristicInterior": 1654
|
||||
},
|
||||
"speedup": 2.423840201736871,
|
||||
"workMatch": true,
|
||||
"escapedMatch": true,
|
||||
"operationLimitMatch": true,
|
||||
"heuristicMatch": true,
|
||||
"note": "Production CPU fallback Worker source executed under node:worker_threads shim."
|
||||
},
|
||||
"sourceAudit": {
|
||||
"heuristicClass": true,
|
||||
"strictDisablesPeriodicity": true,
|
||||
"seriesBinding": true,
|
||||
"compactPixelMap": true,
|
||||
"multiReferencePasses": true,
|
||||
"cpuFallback": true
|
||||
},
|
||||
"environment": {
|
||||
"node": "v22.16.0",
|
||||
"cpus": 5,
|
||||
"platform": "linux",
|
||||
"arch": "x64"
|
||||
}
|
||||
}
|
||||
574
docs/removal-pass/results.json
Normal file
574
docs/removal-pass/results.json
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
{
|
||||
"date": "2026-09-29",
|
||||
"base": {
|
||||
"sha256": "544820c5024cf6d56e012db6d1bec3ddfcef5d833691b890baa64b26c5c2ee5a",
|
||||
"bytes": 223672,
|
||||
"gzip9": 57375
|
||||
},
|
||||
"pruned": {
|
||||
"sha256": "e69b91ead027927d977a065d27d9e27629110122df20aa470eab877e23f77ac7",
|
||||
"bytes": 224559,
|
||||
"gzip9": 57744
|
||||
},
|
||||
"validation": {
|
||||
"regression": "PASS",
|
||||
"kernelCount": 19,
|
||||
"variantSyntax": "PASS",
|
||||
"interiorGateRandom": {
|
||||
"cases": 240,
|
||||
"gateFalse": 160,
|
||||
"falseNeg": 0,
|
||||
"totalProofInFalseNeg": 0
|
||||
},
|
||||
"fastCoverageSemantic": {
|
||||
"cases": 200,
|
||||
"casesWithHoles": 0
|
||||
}
|
||||
},
|
||||
"structural": {
|
||||
"cpuFrameRgbaRemoved": true,
|
||||
"fastCoverageRepairRemoved": true,
|
||||
"idleColorAutoSnapshotRemoved": true,
|
||||
"exactInteriorSpatialGate": true,
|
||||
"conditionalNumericHistory": true,
|
||||
"lazyExportAaSamples": true,
|
||||
"exportWorkspaceReleased": true,
|
||||
"recolorNumericHistorySeparated": true,
|
||||
"prePrimaryUnknownStatsRemoved": true,
|
||||
"deadFastMode2ParamWriteRemoved": true
|
||||
},
|
||||
"priorABEvidence": {
|
||||
"fastCoverageCases": [
|
||||
{
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"iter": 350,
|
||||
"mirror": false,
|
||||
"tileW": 965,
|
||||
"initialRows": 142,
|
||||
"holes": 0,
|
||||
"repairThreads": 526592,
|
||||
"minimumMetaReadBytes": 2095980
|
||||
},
|
||||
{
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"iter": 350,
|
||||
"mirror": true,
|
||||
"tileW": 965,
|
||||
"initialRows": 142,
|
||||
"holes": 0,
|
||||
"repairThreads": 526592,
|
||||
"minimumMetaReadBytes": 2095980
|
||||
},
|
||||
{
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"iter": 4096,
|
||||
"mirror": false,
|
||||
"tileW": 965,
|
||||
"initialRows": 12,
|
||||
"holes": 0,
|
||||
"repairThreads": 526592,
|
||||
"minimumMetaReadBytes": 2095980
|
||||
},
|
||||
{
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"iter": 4096,
|
||||
"mirror": true,
|
||||
"tileW": 965,
|
||||
"initialRows": 12,
|
||||
"holes": 0,
|
||||
"repairThreads": 526592,
|
||||
"minimumMetaReadBytes": 2095980
|
||||
},
|
||||
{
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"iter": 16384,
|
||||
"mirror": false,
|
||||
"tileW": 965,
|
||||
"initialRows": 3,
|
||||
"holes": 0,
|
||||
"repairThreads": 526592,
|
||||
"minimumMetaReadBytes": 2095980
|
||||
},
|
||||
{
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"iter": 16384,
|
||||
"mirror": true,
|
||||
"tileW": 965,
|
||||
"initialRows": 3,
|
||||
"holes": 0,
|
||||
"repairThreads": 526592,
|
||||
"minimumMetaReadBytes": 2095980
|
||||
},
|
||||
{
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"iter": 350,
|
||||
"mirror": false,
|
||||
"tileW": 1365,
|
||||
"initialRows": 100,
|
||||
"holes": 0,
|
||||
"repairThreads": 1050624,
|
||||
"minimumMetaReadBytes": 4193280
|
||||
},
|
||||
{
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"iter": 350,
|
||||
"mirror": true,
|
||||
"tileW": 1365,
|
||||
"initialRows": 100,
|
||||
"holes": 0,
|
||||
"repairThreads": 1050624,
|
||||
"minimumMetaReadBytes": 4193280
|
||||
},
|
||||
{
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"iter": 4096,
|
||||
"mirror": false,
|
||||
"tileW": 1365,
|
||||
"initialRows": 8,
|
||||
"holes": 0,
|
||||
"repairThreads": 1050624,
|
||||
"minimumMetaReadBytes": 4193280
|
||||
},
|
||||
{
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"iter": 4096,
|
||||
"mirror": true,
|
||||
"tileW": 1365,
|
||||
"initialRows": 8,
|
||||
"holes": 0,
|
||||
"repairThreads": 1050624,
|
||||
"minimumMetaReadBytes": 4193280
|
||||
},
|
||||
{
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"iter": 16384,
|
||||
"mirror": false,
|
||||
"tileW": 1365,
|
||||
"initialRows": 2,
|
||||
"holes": 0,
|
||||
"repairThreads": 1050624,
|
||||
"minimumMetaReadBytes": 4193280
|
||||
},
|
||||
{
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"iter": 16384,
|
||||
"mirror": true,
|
||||
"tileW": 1365,
|
||||
"initialRows": 2,
|
||||
"holes": 0,
|
||||
"repairThreads": 1050624,
|
||||
"minimumMetaReadBytes": 4193280
|
||||
},
|
||||
{
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"iter": 350,
|
||||
"mirror": false,
|
||||
"tileW": 1024,
|
||||
"initialRows": 133,
|
||||
"holes": 0,
|
||||
"repairThreads": 786432,
|
||||
"minimumMetaReadBytes": 3145728
|
||||
},
|
||||
{
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"iter": 350,
|
||||
"mirror": true,
|
||||
"tileW": 1024,
|
||||
"initialRows": 133,
|
||||
"holes": 0,
|
||||
"repairThreads": 786432,
|
||||
"minimumMetaReadBytes": 3145728
|
||||
},
|
||||
{
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"iter": 4096,
|
||||
"mirror": false,
|
||||
"tileW": 1024,
|
||||
"initialRows": 11,
|
||||
"holes": 0,
|
||||
"repairThreads": 786432,
|
||||
"minimumMetaReadBytes": 3145728
|
||||
},
|
||||
{
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"iter": 4096,
|
||||
"mirror": true,
|
||||
"tileW": 1024,
|
||||
"initialRows": 11,
|
||||
"holes": 0,
|
||||
"repairThreads": 786432,
|
||||
"minimumMetaReadBytes": 3145728
|
||||
},
|
||||
{
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"iter": 16384,
|
||||
"mirror": false,
|
||||
"tileW": 1024,
|
||||
"initialRows": 2,
|
||||
"holes": 0,
|
||||
"repairThreads": 786432,
|
||||
"minimumMetaReadBytes": 3145728
|
||||
},
|
||||
{
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"iter": 16384,
|
||||
"mirror": true,
|
||||
"tileW": 1024,
|
||||
"initialRows": 2,
|
||||
"holes": 0,
|
||||
"repairThreads": 786432,
|
||||
"minimumMetaReadBytes": 3145728
|
||||
},
|
||||
{
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"iter": 350,
|
||||
"mirror": false,
|
||||
"tileW": 2731,
|
||||
"initialRows": 50,
|
||||
"holes": 0,
|
||||
"repairThreads": 4202496,
|
||||
"minimumMetaReadBytes": 16779264
|
||||
},
|
||||
{
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"iter": 350,
|
||||
"mirror": true,
|
||||
"tileW": 2731,
|
||||
"initialRows": 50,
|
||||
"holes": 0,
|
||||
"repairThreads": 4202496,
|
||||
"minimumMetaReadBytes": 16779264
|
||||
},
|
||||
{
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"iter": 4096,
|
||||
"mirror": false,
|
||||
"tileW": 2731,
|
||||
"initialRows": 4,
|
||||
"holes": 0,
|
||||
"repairThreads": 4202496,
|
||||
"minimumMetaReadBytes": 16779264
|
||||
},
|
||||
{
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"iter": 4096,
|
||||
"mirror": true,
|
||||
"tileW": 2731,
|
||||
"initialRows": 4,
|
||||
"holes": 0,
|
||||
"repairThreads": 4202496,
|
||||
"minimumMetaReadBytes": 16779264
|
||||
},
|
||||
{
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"iter": 16384,
|
||||
"mirror": false,
|
||||
"tileW": 2731,
|
||||
"initialRows": 1,
|
||||
"holes": 0,
|
||||
"repairThreads": 4202496,
|
||||
"minimumMetaReadBytes": 16779264
|
||||
},
|
||||
{
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"iter": 16384,
|
||||
"mirror": true,
|
||||
"tileW": 2731,
|
||||
"initialRows": 1,
|
||||
"holes": 0,
|
||||
"repairThreads": 4202496,
|
||||
"minimumMetaReadBytes": 16779264
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 2048,
|
||||
"iter": 350,
|
||||
"mirror": false,
|
||||
"tileW": 2048,
|
||||
"initialRows": 66,
|
||||
"holes": 0,
|
||||
"repairThreads": 4194304,
|
||||
"minimumMetaReadBytes": 16777216
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 2048,
|
||||
"iter": 350,
|
||||
"mirror": true,
|
||||
"tileW": 2048,
|
||||
"initialRows": 66,
|
||||
"holes": 0,
|
||||
"repairThreads": 4194304,
|
||||
"minimumMetaReadBytes": 16777216
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 2048,
|
||||
"iter": 4096,
|
||||
"mirror": false,
|
||||
"tileW": 2048,
|
||||
"initialRows": 5,
|
||||
"holes": 0,
|
||||
"repairThreads": 4194304,
|
||||
"minimumMetaReadBytes": 16777216
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 2048,
|
||||
"iter": 4096,
|
||||
"mirror": true,
|
||||
"tileW": 2048,
|
||||
"initialRows": 5,
|
||||
"holes": 0,
|
||||
"repairThreads": 4194304,
|
||||
"minimumMetaReadBytes": 16777216
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 2048,
|
||||
"iter": 16384,
|
||||
"mirror": false,
|
||||
"tileW": 2048,
|
||||
"initialRows": 1,
|
||||
"holes": 0,
|
||||
"repairThreads": 4194304,
|
||||
"minimumMetaReadBytes": 16777216
|
||||
},
|
||||
{
|
||||
"w": 2048,
|
||||
"h": 2048,
|
||||
"iter": 16384,
|
||||
"mirror": true,
|
||||
"tileW": 2048,
|
||||
"initialRows": 1,
|
||||
"holes": 0,
|
||||
"repairThreads": 4194304,
|
||||
"minimumMetaReadBytes": 16777216
|
||||
},
|
||||
{
|
||||
"w": 513,
|
||||
"h": 511,
|
||||
"iter": 350,
|
||||
"mirror": false,
|
||||
"tileW": 513,
|
||||
"initialRows": 267,
|
||||
"holes": 0,
|
||||
"repairThreads": 266240,
|
||||
"minimumMetaReadBytes": 1048572
|
||||
},
|
||||
{
|
||||
"w": 513,
|
||||
"h": 511,
|
||||
"iter": 350,
|
||||
"mirror": true,
|
||||
"tileW": 513,
|
||||
"initialRows": 267,
|
||||
"holes": 0,
|
||||
"repairThreads": 266240,
|
||||
"minimumMetaReadBytes": 1048572
|
||||
},
|
||||
{
|
||||
"w": 513,
|
||||
"h": 511,
|
||||
"iter": 4096,
|
||||
"mirror": false,
|
||||
"tileW": 513,
|
||||
"initialRows": 22,
|
||||
"holes": 0,
|
||||
"repairThreads": 266240,
|
||||
"minimumMetaReadBytes": 1048572
|
||||
},
|
||||
{
|
||||
"w": 513,
|
||||
"h": 511,
|
||||
"iter": 4096,
|
||||
"mirror": true,
|
||||
"tileW": 513,
|
||||
"initialRows": 22,
|
||||
"holes": 0,
|
||||
"repairThreads": 266240,
|
||||
"minimumMetaReadBytes": 1048572
|
||||
},
|
||||
{
|
||||
"w": 513,
|
||||
"h": 511,
|
||||
"iter": 16384,
|
||||
"mirror": false,
|
||||
"tileW": 513,
|
||||
"initialRows": 5,
|
||||
"holes": 0,
|
||||
"repairThreads": 266240,
|
||||
"minimumMetaReadBytes": 1048572
|
||||
},
|
||||
{
|
||||
"w": 513,
|
||||
"h": 511,
|
||||
"iter": 16384,
|
||||
"mirror": true,
|
||||
"tileW": 513,
|
||||
"initialRows": 5,
|
||||
"holes": 0,
|
||||
"repairThreads": 266240,
|
||||
"minimumMetaReadBytes": 1048572
|
||||
}
|
||||
],
|
||||
"numericHistory": [
|
||||
{
|
||||
"name": "1920x1080-fast",
|
||||
"cssW": 1920,
|
||||
"cssH": 1080,
|
||||
"w": 965,
|
||||
"h": 543,
|
||||
"hits": 2,
|
||||
"total": 1000,
|
||||
"hitRate": 0.002,
|
||||
"meanReuseRatio": 0.8,
|
||||
"reducedScaleDenominator": 384,
|
||||
"policyEligible": false,
|
||||
"aResidentBytes": 4191960,
|
||||
"bResidentBytes": 0
|
||||
},
|
||||
{
|
||||
"name": "1920x1080-standard",
|
||||
"cssW": 1920,
|
||||
"cssH": 1080,
|
||||
"w": 1365,
|
||||
"h": 768,
|
||||
"hits": 6,
|
||||
"total": 1000,
|
||||
"hitRate": 0.006,
|
||||
"meanReuseRatio": 0.8666666666666667,
|
||||
"reducedScaleDenominator": 128,
|
||||
"policyEligible": false,
|
||||
"aResidentBytes": 8386560,
|
||||
"bResidentBytes": 0
|
||||
},
|
||||
{
|
||||
"name": "1920x1080-high",
|
||||
"cssW": 1920,
|
||||
"cssH": 1080,
|
||||
"w": 2731,
|
||||
"h": 1536,
|
||||
"hits": 0,
|
||||
"total": 1000,
|
||||
"hitRate": 0,
|
||||
"meanReuseRatio": 0,
|
||||
"reducedScaleDenominator": 1920,
|
||||
"policyEligible": false,
|
||||
"aResidentBytes": 33558528,
|
||||
"bResidentBytes": 0
|
||||
},
|
||||
{
|
||||
"name": "1024x768-standard",
|
||||
"cssW": 1024,
|
||||
"cssH": 768,
|
||||
"w": 1024,
|
||||
"h": 768,
|
||||
"hits": 1000,
|
||||
"total": 1000,
|
||||
"hitRate": 1,
|
||||
"meanReuseRatio": 0.75537109375,
|
||||
"reducedScaleDenominator": 1,
|
||||
"policyEligible": true,
|
||||
"aResidentBytes": 6291456,
|
||||
"bResidentBytes": 6291456
|
||||
},
|
||||
{
|
||||
"name": "1024x768-dpr2",
|
||||
"cssW": 1024,
|
||||
"cssH": 768,
|
||||
"w": 2048,
|
||||
"h": 1536,
|
||||
"hits": 1000,
|
||||
"total": 1000,
|
||||
"hitRate": 1,
|
||||
"meanReuseRatio": 0.75537109375,
|
||||
"reducedScaleDenominator": 1,
|
||||
"policyEligible": true,
|
||||
"aResidentBytes": 25165824,
|
||||
"bResidentBytes": 25165824
|
||||
}
|
||||
],
|
||||
"memoryTraffic": {
|
||||
"rows": [
|
||||
{
|
||||
"preset": "fast",
|
||||
"pixels": 524288,
|
||||
"recolorNumericCopyA": 4194304,
|
||||
"recolorNumericCopyB": 0,
|
||||
"cpuRgbaRetainedA": 2097152,
|
||||
"cpuRgbaRetainedB": 0,
|
||||
"idleColorSnapshotA": 6291456,
|
||||
"idleColorSnapshotB": 0,
|
||||
"unknownPrepassMetaReadA": 2097152,
|
||||
"unknownPrepassMetaReadB": 0,
|
||||
"fastRepairMetaReadA": 2097152,
|
||||
"fastRepairMetaReadB": 0
|
||||
},
|
||||
{
|
||||
"preset": "standard",
|
||||
"pixels": 1048576,
|
||||
"recolorNumericCopyA": 8388608,
|
||||
"recolorNumericCopyB": 0,
|
||||
"cpuRgbaRetainedA": 4194304,
|
||||
"cpuRgbaRetainedB": 0,
|
||||
"idleColorSnapshotA": 12582912,
|
||||
"idleColorSnapshotB": 0,
|
||||
"unknownPrepassMetaReadA": 4194304,
|
||||
"unknownPrepassMetaReadB": 0,
|
||||
"fastRepairMetaReadA": 4194304,
|
||||
"fastRepairMetaReadB": 0
|
||||
},
|
||||
{
|
||||
"preset": "high",
|
||||
"pixels": 4194304,
|
||||
"recolorNumericCopyA": 33554432,
|
||||
"recolorNumericCopyB": 0,
|
||||
"cpuRgbaRetainedA": 16777216,
|
||||
"cpuRgbaRetainedB": 0,
|
||||
"idleColorSnapshotA": 50331648,
|
||||
"idleColorSnapshotB": 0,
|
||||
"unknownPrepassMetaReadA": 16777216,
|
||||
"unknownPrepassMetaReadB": 0,
|
||||
"fastRepairMetaReadA": 16777216,
|
||||
"fastRepairMetaReadB": 0
|
||||
}
|
||||
],
|
||||
"export": {
|
||||
"retainedA": 25165824,
|
||||
"retainedB": 0,
|
||||
"reallocationSlotsB": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"notes": [
|
||||
"Real WebGPU frame-time p50/p95 remains unavailable in this container.",
|
||||
"Adaptive initial-iteration probe was retained because blanket removal regressed 3/48 survey views.",
|
||||
"Strict periodicity-state updates were retained because CPU A/B did not establish a speed win.",
|
||||
"Continuation replay and deep-workspace lifetime were not changed because they require redesign or real-GPU allocation measurements rather than safe deletion."
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue