publish
This commit is contained in:
parent
01204dfa2f
commit
f5fc9b76ce
73 changed files with 32628 additions and 376 deletions
83
docs/final-structure-cleanup/IMPLEMENTATION.md
Normal file
83
docs/final-structure-cleanup/IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# Final structure cleanup — 2026-09-12
|
||||
|
||||
## Intent
|
||||
|
||||
Finish the structural cleanup after the component pruning pass without changing Mandelbrot numerical behavior. The production HTML is now the single source of truth for both release and regression.
|
||||
|
||||
## Changes
|
||||
|
||||
### Single source of truth
|
||||
|
||||
- Removed the persistent `tests/index.test.html` copy.
|
||||
- `tests/regression.mjs` now parses and executes production `index.html` directly.
|
||||
- `tests/browser_smoke.mjs` generates a temporary instrumented page from production `index.html`, then removes it even when browser connection fails.
|
||||
- Added a regression assertion that a persistent test HTML copy must not reappear.
|
||||
|
||||
### Correctness fixes found during cleanup
|
||||
|
||||
- Fixed `cancelRender()` export cancellation/generation update. The production-only typo `truestate.token++` is gone; export cancellation and `state.token++` are separate operations.
|
||||
- Fixed the render cancellation/error path that called the nonexistent `returnfinishRenderClock(...)` token. Cancelled generations now close the render clock correctly and return.
|
||||
|
||||
### Removed write-only state and UI churn
|
||||
|
||||
Removed production state that had no readers:
|
||||
|
||||
- `lastEngine`
|
||||
- `frontierRounds`
|
||||
- `frontierPixels`
|
||||
- `frontierEscaped`
|
||||
- `frontierIter`
|
||||
- `frontierMinimumIter`
|
||||
- `frontierConverged`
|
||||
- `gpuUnavailable`
|
||||
- `qualityIter`
|
||||
- `refineMs`
|
||||
- `lastRender`
|
||||
- `gpuStage`
|
||||
|
||||
Progress-only `lastEngine` assignments and their no-op `updateStats()` calls were removed. The user-visible status continues to derive from `drawState`, unresolved counts, temporal fill, render clock, and GPU error state.
|
||||
|
||||
### Renderer diagnostics
|
||||
|
||||
Removed retained diagnostics that were never consumed:
|
||||
|
||||
- `this.adapter`
|
||||
- `this.adapterInfo`
|
||||
- `this.compilation`
|
||||
- `this.uncapturedErrors`
|
||||
|
||||
Shader compilation errors are still checked and thrown immediately. The latest uncaptured GPU error is still copied to `state.gpuError`. Device-loss stage tracking stays in renderer-local `activeGpuStage` / `lastCompletedGpuStage`.
|
||||
|
||||
### Fixed-point / WGSL dead definitions
|
||||
|
||||
- Removed production-only unused `fromDec()` and `decimalRequiredBits()` helpers. Test decimal conversion now lives only in the test harness.
|
||||
- Removed unused WGSL `FIELD_INTERIOR_LIKELY`.
|
||||
- All 19 surviving kernels are byte-identical to the pre-cleanup kernels after deleting that single unused constant line.
|
||||
- Simplified stale reference-selector comments and redundant return logic.
|
||||
|
||||
### Export metadata
|
||||
|
||||
Both export paths now call one `buildExportMetadata()` function. Common fields now have one definition for:
|
||||
|
||||
- finite-budget completion
|
||||
- membership certification
|
||||
- precision policy
|
||||
- iteration policy
|
||||
- view coordinates
|
||||
- palette description
|
||||
- renderer/shader version
|
||||
- pixel contract
|
||||
|
||||
Path-specific tile/ring fields are added only when applicable.
|
||||
|
||||
### Small dead-code cleanup
|
||||
|
||||
- Removed dead `dispatchWork` local after correction accounting was deleted.
|
||||
- Removed dead `sampleCount`, `lastUi`, and final-frame `elapsed` locals.
|
||||
- Renamed stale `reusePreview` to `reusePriorField` and the no-frame `PREVIEW` draw-state label to `PENDING`.
|
||||
|
||||
## Verification
|
||||
|
||||
`node tests/regression.mjs` passes and directly executes production JS. It covers routing, finite-budget semantics, staged operation-limit scoring, exact interior proof, integer-grid history reuse, nearby reference reuse, Worker lifecycle, correction pipeline count, export cancellation/generation behavior, common export metadata, pinned WGSL hashes, and CPU worker smoke tests.
|
||||
|
||||
The optional browser smoke script passes syntax validation. A real WebGPU browser run is not claimed in this environment.
|
||||
44
docs/final-structure-cleanup/results.json
Normal file
44
docs/final-structure-cleanup/results.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"date": "2026-09-12",
|
||||
"baseline": {
|
||||
"artifact": "mandelbrot-cleaned-all/index.html",
|
||||
"bytes": 212483,
|
||||
"gzip9_bytes_shell": 53779
|
||||
},
|
||||
"final": {
|
||||
"index_bytes": 208576,
|
||||
"gzip9_bytes_shell": 52563,
|
||||
"sha256": "8355da57e2f546c4e1cbeb2237e9dbfb65168b6d4c254929a2b02078c086a935",
|
||||
"wgsl_kernels": 19,
|
||||
"persistent_test_html": false,
|
||||
"definition_only_locals_or_functions": 0
|
||||
},
|
||||
"change": {
|
||||
"index_bytes": -3907,
|
||||
"index_percent": -1.8387,
|
||||
"gzip9_bytes_shell": -1216,
|
||||
"gzip9_percent": -2.2616
|
||||
},
|
||||
"kernel_equivalence": {
|
||||
"kernel_object_keys_including_version": 20,
|
||||
"byte_identical_without_normalization": 9,
|
||||
"byte_identical_after_removing_old_unused_FIELD_INTERIOR_LIKELY_line": 20,
|
||||
"behavioral_kernel_changes": 0
|
||||
},
|
||||
"regression": {
|
||||
"status": "PASS",
|
||||
"runs_after_warmup": 12,
|
||||
"median_ms": 54.8059,
|
||||
"min_ms": 47.6742,
|
||||
"max_ms": 71.0208
|
||||
},
|
||||
"browser_smoke": {
|
||||
"script_syntax": "PASS",
|
||||
"temporary_file_cleanup_on_connection_failure": "PASS",
|
||||
"real_webgpu_run": "not available in this environment"
|
||||
},
|
||||
"fixed_bugs": [
|
||||
"cancelRender production typo truestate.token++",
|
||||
"render error/cancellation typo returnfinishRenderClock(...)"
|
||||
]
|
||||
}
|
||||
153
docs/integrated-improvements/IMPLEMENTATION.md
Normal file
153
docs/integrated-improvements/IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# 5改善案の本体統合と再評価
|
||||
|
||||
## 対象
|
||||
|
||||
前回評価表の「Numeric tile cache」より上にあった次の5案を `index.html` に統合した。
|
||||
|
||||
1. 一般周期検出
|
||||
2. perturbation series approximation
|
||||
3. sparse active state
|
||||
4. dynamic multi-reference
|
||||
5. CPU Worker fallback
|
||||
|
||||
`Numeric tile cache` 以下は今回の対象外。基準は統合前の `index.html`(SHA-256 `8355da57...086a935`)。
|
||||
|
||||
## 実装
|
||||
|
||||
### 1. 周期検出
|
||||
|
||||
- direct f32 kernel に Brent 型 O(1) candidate search を追加。
|
||||
- 単純な「近いから内部」判定は再評価で危険と判明したため、候補後に **2周期分の再一致**と **周期乗数 `|μ| < 0.95`** を確認してから `FIELD_INTERIOR_HEURISTIC=2` にする。
|
||||
- `FIELD_INTERIOR_PROVEN=3` とは分離し、heuristic が1画素でもあれば `membershipCertified=false`。
|
||||
- strict/export では無効。
|
||||
- FAST perturbation 上の周期早期停止は撤回。forced-direct 診断で deep seahorse に false candidate が残ったため、deep path は series / continuation に任せる。
|
||||
|
||||
### 2. Series approximation
|
||||
|
||||
- reference worker で 1〜6次係数を更新。
|
||||
- GPU に送るのは 1〜4次係数。5・6次は truncation error の安全側判定に使用。
|
||||
- validity 条件を満たす最大 jump を reference ごとに計算し、FAST perturbation の初期反復を4次多項式評価へ置換。
|
||||
- strict/export では series jump を無効化し、従来 recurrence を使う。
|
||||
|
||||
### 3. Sparse active state
|
||||
|
||||
旧 continuation は state/queues/mark をほぼ全画素サイズで持っていた。新構成は以下。
|
||||
|
||||
- `DeepState`: active slot ごと 32 B
|
||||
- queue A/B: active slot ID
|
||||
- `pixelMap`: active slot → pixel index
|
||||
- membership: 1 bit/pixel bitset
|
||||
- 初期 capacity: `active * 1.25 + 256`(全画素上限)
|
||||
- correction により新しい `OPERATION_LIMIT` が生じた場合は、slot allocator を複雑化せず metadata から compact session を再構築する。
|
||||
|
||||
### 4. Dynamic multi-reference
|
||||
|
||||
- failure-guided reference recovery を最大3 passまで反復。
|
||||
- 既に試した reference identity は再試行しない。
|
||||
- 1 pass の改善が `max(8 pixels, 2%)` 未満なら打ち切る。
|
||||
- 固定 2x2 / 3x3 配置ではなく、既存 failure sample + scorer を使って必要箇所だけ reference を追加する。
|
||||
|
||||
### 5. CPU Worker fallback
|
||||
|
||||
- WebGPU 非対応/初期化失敗時に最大4 Workerで direct Mandelbrot を描画。
|
||||
- cardioid / period-2 bulb、有限 iteration、既存 palette の主要表示をサポート。
|
||||
- JS `Number` で adjacent pixel を区別できない deep zoom では停止し、誤った deep image を出さない。
|
||||
- CPU fallback の `OPERATION_LIMIT` と周期 heuristic は未確定扱い。WebGPU の high-precision path の代替ではなく互換経路。
|
||||
|
||||
## 再評価結果
|
||||
|
||||
### 周期検出
|
||||
|
||||
96×64、maxIter=8192、candidate は 65,536 iteration まで escape を追跡。
|
||||
|
||||
| view | production backend | 反復仕事量削減 | false candidate |
|
||||
|---|---|---:|---:|
|
||||
| overview | direct | 84.9% | 0 |
|
||||
| period3-bulb | direct | 99.09% | 0 |
|
||||
| period3-core | direct | 99.12% | 0 |
|
||||
| seahorse-boundary | direct | 0% | 0 |
|
||||
| exterior | direct | 80.0% | 0 |
|
||||
| seahorse-deep | fast-extended | **周期停止を使用しない** | — |
|
||||
|
||||
forced-direct では seahorse-deep に false candidate が残るため、FAST 側を無効化した判断は維持する。
|
||||
|
||||
### Series approximation
|
||||
|
||||
production fast-reference worker の実出力を使用し、4次 series と直接 perturbation の delta を8点で比較。
|
||||
|
||||
| span | jump | 最大絶対誤差 |
|
||||
|---:|---:|---:|
|
||||
| 1e-5 | 53 | 7.11e-9 |
|
||||
| 1e-7 | 446 | 7.74e-9 |
|
||||
| 1e-9 | 930 | 7.46e-9 |
|
||||
| 1e-11 | 1037 | 2.28e-9 |
|
||||
|
||||
reference build の一部として係数を作るため CPU 側の追加仕事はあるが、deep zoom ほど jump が大きい。実 GPU frame time はこの環境では測れていない。
|
||||
|
||||
### Sparse active state
|
||||
|
||||
standard 1,048,576 px の continuation workspace モデル。
|
||||
|
||||
| active ratio | 旧 | 新 | 削減 |
|
||||
|---:|---:|---:|---:|
|
||||
| 100% | 44.0 MiB | 44.13 MiB | -0.28% |
|
||||
| 25% | 44.0 MiB | 13.89 MiB | 68.4% |
|
||||
| 10% | 44.0 MiB | 5.64 MiB | 87.2% |
|
||||
| 1% | 44.0 MiB | 0.686 MiB | 98.4% |
|
||||
|
||||
active がほぼ全画面なら pixel map + bitset の分だけわずかに悪化する。狙いは sparse frontier であり、その条件では効果が大きい。
|
||||
|
||||
### Dynamic multi-reference
|
||||
|
||||
production は failure-guided 最大3追加 reference。GPU end-to-end の `A_num` は実 GPU 不在で測れないため、前回と同じ f32 perturbation locality surrogate を再実行した。
|
||||
|
||||
seahorse span=1e-7:
|
||||
|
||||
- center 1 ref: class mismatch 2.578%
|
||||
- center + 2x2 (5 refs): **1.445%**
|
||||
- center + 3x3 (9 refs): 1.953%
|
||||
|
||||
reference を無制限に増やすのは逆効果になり得るため、「失敗箇所から選ぶ + 3 pass cap」は妥当。
|
||||
|
||||
### CPU fallback
|
||||
|
||||
production `cpuFallbackWorkerSource()` 自体を Node worker shim で実行。640×360、350 iter、7 run median。
|
||||
|
||||
- 1 Worker: 約 97.2 ms
|
||||
- 4 Workers: 約 36.0 ms
|
||||
- speedup: **2.70×**
|
||||
- escaped / OP_LIMIT / heuristic / work count は 1 Worker と4 Workerで一致。
|
||||
|
||||
これはブラウザ 2D canvas compositing を含まない worker compute 値。
|
||||
|
||||
## 回帰・サイズ
|
||||
|
||||
- JS syntax: PASS
|
||||
- production regression: **5/5 PASS**
|
||||
- regression median: 約 0.08 s
|
||||
- WGSL kernels: 19本、hash 更新済み
|
||||
- `index.html`: 208,576 → 223,672 bytes(+7.24%)
|
||||
- gzip -9: 52,563 → 57,198 bytes(+8.82%)
|
||||
|
||||
## 実 GPU 検証の限界
|
||||
|
||||
コンテナの Headless Chromium + SwiftShader は EGL 初期化に失敗し、WebGPU smoke を開始できなかった。そのため以下は未測定。
|
||||
|
||||
- stable frame p50/p95
|
||||
- GPU series jump の実時間短縮
|
||||
- sparse scatter/gather の実 GPU overhead
|
||||
- dynamic multi-reference 導入後の production `A_num`
|
||||
|
||||
したがって **CPU/数値 surrogate を GPU frame-time として扱わない**。
|
||||
|
||||
## 結論
|
||||
|
||||
| 改善 | 統合判断 | 再評価 |
|
||||
|---|---|---|
|
||||
| 周期検出 | 採用(directのみ) | 高周期内部で非常に有効。FASTでは安全性不足のため無効 |
|
||||
| Series | 採用(interactive) | deepほど jump が増え、有望 |
|
||||
| Sparse active state | 採用 | active≪100%ならメモリ効果が明確 |
|
||||
| Dynamic multi-reference | 採用(最大3 pass) | reference増加は非単調。failure-guided capが妥当 |
|
||||
| CPU fallback | 採用 | 互換性改善。4 Workerで約2.7×、deep precision代替ではない |
|
||||
|
||||
次に実機で確認すべき優先順位は **series GPU frame time → sparse continuation latency → multi-reference の `A_num`**。
|
||||
6
docs/integrated-improvements/README.md
Normal file
6
docs/integrated-improvements/README.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Integrated improvements
|
||||
|
||||
- `IMPLEMENTATION.md`: 実装内容、設計判断、再評価、限界
|
||||
- `results.json`: 再現用の生データ
|
||||
- benchmark: `../../experiments/integrated_bench.mjs`
|
||||
- regression: `../../tests/regression.mjs`
|
||||
1202
docs/integrated-improvements/cost-audit-results.json
Normal file
1202
docs/integrated-improvements/cost-audit-results.json
Normal file
File diff suppressed because it is too large
Load diff
426
docs/integrated-improvements/results.json
Normal file
426
docs/integrated-improvements/results.json
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
{
|
||||
"date": "2026-09-29",
|
||||
"scope": [
|
||||
"direct periodicity heuristic",
|
||||
"perturbation series approximation",
|
||||
"sparse active continuation state",
|
||||
"dynamic failure-guided multi-reference",
|
||||
"CPU Worker fallback"
|
||||
],
|
||||
"baseline": {
|
||||
"path": "../mandelbrot_work/index.html",
|
||||
"sha256": "8355da57e2f546c4e1cbeb2237e9dbfb65168b6d4c254929a2b02078c086a935",
|
||||
"bytes": 208576,
|
||||
"gzip9Bytes": 52702
|
||||
},
|
||||
"integrated": {
|
||||
"sha256": "544820c5024cf6d56e012db6d1bec3ddfcef5d833691b890baa64b26c5c2ee5a",
|
||||
"bytes": 223672,
|
||||
"gzip9Bytes": 57375
|
||||
},
|
||||
"regression": {
|
||||
"passes": 5,
|
||||
"timesSeconds": [
|
||||
0.08,
|
||||
0.09,
|
||||
0.07,
|
||||
0.07,
|
||||
0.09
|
||||
],
|
||||
"medianSeconds": 0.08,
|
||||
"status": "PASS",
|
||||
"kernelCount": 19
|
||||
},
|
||||
"webGpuBrowserSmoke": {
|
||||
"status": "UNAVAILABLE_IN_CONTAINER",
|
||||
"reason": "Headless Chromium SwiftShader/EGL initialization failed before WebGPU could run; no real GPU frame-latency claim is made."
|
||||
},
|
||||
"benchmarks": {
|
||||
"date": "2026-09-29T08:28:46.101Z",
|
||||
"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": 68.786555
|
||||
},
|
||||
{
|
||||
"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": 3092.6467549999998
|
||||
},
|
||||
{
|
||||
"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": 3061.9720069999994
|
||||
},
|
||||
{
|
||||
"view": "seahorse-boundary",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 410111,
|
||||
"heuristicWork": 410111,
|
||||
"workReduction": 0,
|
||||
"candidates": 0,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 0,
|
||||
"heuristicLimit": 0,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 17.153881000000183
|
||||
},
|
||||
{
|
||||
"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": 2289.795112
|
||||
},
|
||||
{
|
||||
"view": "exterior",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 686432,
|
||||
"heuristicWork": 137063,
|
||||
"workReduction": 0.8003254510279241,
|
||||
"candidates": 72,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 79,
|
||||
"heuristicLimit": 7,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 37.95325000000048
|
||||
}
|
||||
],
|
||||
"note": "Production DIRECT_F32 rule with two attracting-cycle confirmations. FAST/perturbation periodic early-stop is intentionally disabled."
|
||||
},
|
||||
"series": {
|
||||
"rows": [
|
||||
{
|
||||
"span": 1e-05,
|
||||
"jump": 53,
|
||||
"errorLog2": -22.11596292311006,
|
||||
"maxAbsError": 7.112067409017996e-09,
|
||||
"maxRelativeError": 9.415623690713784e-05,
|
||||
"refLen": 1200,
|
||||
"buildMs": 32.31590200000028
|
||||
},
|
||||
{
|
||||
"span": 1e-07,
|
||||
"jump": 446,
|
||||
"errorLog2": -22.302471602690176,
|
||||
"maxAbsError": 7.736805567567409e-09,
|
||||
"maxRelativeError": 0.0031991190750368034,
|
||||
"refLen": 1200,
|
||||
"buildMs": 33.50016399999913
|
||||
},
|
||||
{
|
||||
"span": 1e-09,
|
||||
"jump": 930,
|
||||
"errorLog2": -22.038152635655024,
|
||||
"maxAbsError": 7.459933177532973e-09,
|
||||
"maxRelativeError": 1.015603159369818e-05,
|
||||
"refLen": 1200,
|
||||
"buildMs": 29.815043000000514
|
||||
},
|
||||
{
|
||||
"span": 1e-11,
|
||||
"jump": 1037,
|
||||
"errorLog2": -23.96377927072575,
|
||||
"maxAbsError": 2.2791360188558774e-09,
|
||||
"maxRelativeError": 4.948137247650717e-06,
|
||||
"refLen": 1200,
|
||||
"buildMs": 37.88702799999919
|
||||
}
|
||||
],
|
||||
"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": 97.22207600000002,
|
||||
"times": [
|
||||
115.15600200000154,
|
||||
86.49102299999868,
|
||||
97.22207600000002,
|
||||
97.75022499999977,
|
||||
95.21002200000112,
|
||||
105.08147800000006,
|
||||
81.41742200000044
|
||||
],
|
||||
"work": 2658084,
|
||||
"escaped": 176648,
|
||||
"operationLimit": 3394,
|
||||
"heuristicInterior": 1654
|
||||
},
|
||||
"multi": {
|
||||
"medianMs": 36.02884199999971,
|
||||
"times": [
|
||||
100.63864199999989,
|
||||
61.91339699999844,
|
||||
50.152170999999726,
|
||||
34.4066870000006,
|
||||
34.23579900000004,
|
||||
36.02884199999971,
|
||||
34.373131000000285
|
||||
],
|
||||
"work": 2658084,
|
||||
"escaped": 176648,
|
||||
"operationLimit": 3394,
|
||||
"heuristicInterior": 1654
|
||||
},
|
||||
"speedup": 2.698451312978663,
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"assessment": {
|
||||
"periodicity": "ADOPT_DIRECT_ONLY",
|
||||
"seriesApproximation": "ADOPT_EXPERIMENTAL_INTERACTIVE",
|
||||
"sparseActiveState": "ADOPT",
|
||||
"dynamicMultiReference": "ADOPT_CAPPED_FAILURE_GUIDED",
|
||||
"cpuFallback": "ADOPT_COMPATIBILITY_PATH"
|
||||
}
|
||||
}
|
||||
137
docs/performance-cost-audit/README.md
Normal file
137
docs/performance-cost-audit/README.md
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
# Performance / Memory Cost Audit
|
||||
|
||||
## Purpose
|
||||
|
||||
Audit the integrated Mandelbrot viewer for processing that is low-value, redundant, or harmful from a speed / memory perspective. This audit intentionally **does not remove or alter any production behavior** in `index.html`.
|
||||
|
||||
Production source audited:
|
||||
|
||||
- `index.html`
|
||||
- SHA-256: `544820c5024cf6d56e012db6d1bec3ddfcef5d833691b890baa64b26c5c2ee5a`
|
||||
- Regression: PASS, 19 WGSL kernels
|
||||
|
||||
Measurements are CPU/static surrogates unless explicitly stated. Real WebGPU frame timing is not available in this container because the browser WebGPU backend cannot initialize successfully here.
|
||||
|
||||
## Conclusions
|
||||
|
||||
### A — High-confidence waste / harmful cost
|
||||
|
||||
| ID | Finding | Cost | Benefit observed | Confidence |
|
||||
|---|---|---|---|---|
|
||||
| A1 | Numeric history is recopied after every accepted recolor | 8 B/px copy per recolor | Numeric data did not change | Very high |
|
||||
| A2 | `cpuFrameRgba` retains the assembled CPU fallback frame but is never read | 4 B/px JS heap retained | None | Very high |
|
||||
| A3 | `certifyInterior()` runs a full-frame unknown-statistics pass and waits for GPU completion before primary compute | Full meta scan + queue synchronization | Partial proof/history-seed caller does not consume these stats | Very high |
|
||||
| A4 | Strict direct rendering still updates Brent-cycle state even though periodicity classification is disabled | Per-iteration ALU/register state | None in strict mode | Very high |
|
||||
| A5 | Export workspace survives export completion | Up to ~24 MiB GPU memory with 512 tiles / 3 slots | None while not exporting | Very high |
|
||||
| A6 | Deep active/correction workspaces survive the hard frame that required them | active workspace up to ~44.1 MiB standard / ~176.5 MiB high; dense correction queue 4/16 MiB | None on later easy frames until reuse/resize | High |
|
||||
|
||||
### B — Strong optimization candidates, but keep until GPU A/B validation
|
||||
|
||||
| ID | Finding | Evidence | Assessment |
|
||||
|---|---|---|---|
|
||||
| B1 | Numeric history is always resident although exact-grid pan reuse is restrictive | 8 B/px resident. Under common budget-scaled resolutions, integer CSS pans 1–100 px produced 0/100 exact-grid reuse hits in the static alignment test | Likely over-allocated; make lazy/telemetry-driven rather than delete immediately |
|
||||
| B2 | CPU exact cardioid/bulb tile proof runs for every numerical frame | 1024×768: ~4.8–6.1 ms median; 2048²: ~25–28 ms median. Four of six fixed corpus views certified 0 pixels | Add cheap spatial gate; direct backend especially needs A/B comparison |
|
||||
| B3 | `fast-coverage-repair` runs a full-screen FAST pass after tiled coverage | Tile loops cover the full compute domain; mode 2 only repairs reason=0 untouched metadata | Likely redundant defensive pass; instrument reason=0 count before removal |
|
||||
| B4 | `captureColorSource()` snapshots `meta+smooth` during rendering | 8 B/px copy per snapshot; provisional snapshots can occur every 125 ms. Snapshot allocation is 12 B/px | Useful only for live recoloring while render is in flight; gate more aggressively |
|
||||
| B5 | Color-auto idle mode retains a color snapshot | standard ~12 MiB, high ~48 MiB at preset caps | Idle color-auto recolor path uses current field, not `recolorPublished()` | Snapshot should be render-only/on-demand |
|
||||
| B6 | Adaptive initial-iteration probe uses high-precision scoring before every adaptive frame | Fixed corpus: ~1.3–12.5 ms and all six chose 350. Exploratory views can choose 512, so feature is not useless | Cache/gate/replace with cheaper trigger; do not delete blindly |
|
||||
| B7 | Active continuation can replay already-performed iterations | initial OP_LIMIT session starts state at n=0; reference change resets progress to 0; recovered pixels can rebuild whole active session | Potentially large on the hardest views; add replay counters before redesign |
|
||||
|
||||
### C — Expensive but currently justified / not a deletion target
|
||||
|
||||
- General periodicity detection: CPU surrogate showed ~98% iteration reduction in period-3 interiors. It can add overhead on boundary-only views, but removal is not justified.
|
||||
- Sparse numerical correction and reason bucketing: it adds full scans/queue construction, but isolates expensive DS/BigInt work. Needs GPU A/B before structural change.
|
||||
- Stable texture reprojection: consumes one history texture but directly improves interaction latency.
|
||||
- Reference cache / guided multi-reference: memory cost is bounded and prior evaluation showed reduced perturbation mismatch.
|
||||
- Series approximation: small reference metadata cost with large deep-zoom iteration skip in prior evaluation.
|
||||
|
||||
## Key measurements
|
||||
|
||||
### Recolor numeric-history copy bandwidth
|
||||
|
||||
`commitHistory()` always calls `commitNumericHistory()`. Therefore a color-only update copies unchanged `meta` and `smooth` buffers (8 B/px). Color auto can request a pass every 50 ms while idle.
|
||||
|
||||
| preset cap | unchanged numeric copy / recolor | at 20 Hz |
|
||||
|---|---:|---:|
|
||||
| fast, 524,288 px | 4 MiB | 80 MiB/s |
|
||||
| standard, 1,048,576 px | 8 MiB | 160 MiB/s |
|
||||
| high, 4,194,304 px | 32 MiB | 640 MiB/s |
|
||||
|
||||
This is in addition to the actual color pass bandwidth.
|
||||
|
||||
### Color snapshot
|
||||
|
||||
`captureColorSource()` allocates/copies:
|
||||
|
||||
- meta: 4 B/px
|
||||
- smooth: 4 B/px
|
||||
- texture: 4 B/px
|
||||
|
||||
Resident cost = 12 B/px: ~6 MiB fast, 12 MiB standard, 48 MiB high at preset caps. A provisional snapshot copies 8 B/px and can be refreshed at up to 8 Hz, equivalent to ~32 / 64 / 256 MiB/s at the preset caps.
|
||||
|
||||
### Exact interior tile proof
|
||||
|
||||
Seven-run median on this host:
|
||||
|
||||
| view | 1024×768 | certified | 2048×2048 | certified |
|
||||
|---|---:|---:|---:|---:|
|
||||
| overview | ~6.1 ms | 7.3% | ~25.5 ms | 8.6% |
|
||||
| period3 bulb | ~5.1 ms | 0% | ~26.5 ms | 0% |
|
||||
| period3 core | ~4.8 ms | 0% | ~28.1 ms | 0% |
|
||||
| seahorse boundary | ~6.0 ms | 0% | ~25.1 ms | 0% |
|
||||
| seahorse deep | ~5.6 ms | 0% | ~26.1 ms | 0% |
|
||||
|
||||
The exact proof is still useful in views that overlap the main cardioid / period-2 bulb, especially on the perturbation path. The issue is unconditional invocation, not the proof mechanism itself.
|
||||
|
||||
### Exact-grid numeric-history reuse
|
||||
|
||||
The history seed requires identical span and an approximately integer render-pixel shift. When the resolution is pixel-budget limited, render pixels often do not align with CSS pixels.
|
||||
|
||||
Examples from the current resize rule, testing integer CSS horizontal pans of 1–100 px:
|
||||
|
||||
| screen / quality | render size | reuse hits |
|
||||
|---|---:|---:|
|
||||
| 1920×1080 / fast | 965×543 | 0/100 |
|
||||
| 1920×1080 / standard | 1365×768 | 0/100 |
|
||||
| 1920×1080 / high | 2731×1536 | 0/100 |
|
||||
| 1024×768@1× / standard | 1024×768 | 100/100 |
|
||||
|
||||
This is a static alignment test, not real interaction telemetry. It supports making numeric history conditional rather than proving that it has no value.
|
||||
|
||||
### Persistent workspaces
|
||||
|
||||
- Sparse active workspace, worst full-active capacity: ~44.1 MiB standard, ~176.5 MiB high.
|
||||
- At 10% active: ~5.64 MiB standard, ~22.51 MiB high.
|
||||
- Dense deep correction queue: 4 MiB standard, 16 MiB high.
|
||||
- Export workspace at 512 tile, depth 3: ~24 MiB retained after export.
|
||||
- Non-AA export currently allocates four 512² sample textures, ~4 MiB per slot, although 1× rendering does not need the four AA sample textures.
|
||||
|
||||
## Static code locations
|
||||
|
||||
- `index.html:1299-1300`: pre-primary unknown stats + synchronization in `certifyInterior()`.
|
||||
- `index.html:1383-1384`: full numeric-history copy.
|
||||
- `index.html:1416`: `commitHistory()` always commits numeric history.
|
||||
- `index.html:1822`: color-only recolor promotes history and therefore triggers numeric copy.
|
||||
- `index.html:1718`: `state.cpuFrameRgba=rgba`; the state value has no reader.
|
||||
- `index.html:162-163`: cycle state still updated in strict direct mode.
|
||||
- `index.html:1349-1350`: tiled FAST coverage followed by full-screen coverage-repair pass.
|
||||
- `index.html:1751-1756`: provisional color snapshots at 125 ms throttle.
|
||||
- `index.html:1774`: old completed field snapshot at every numerical render start.
|
||||
- `index.html:1866`: idle color-auto snapshot allocation.
|
||||
- `index.html:1218`: active workspace persists unless another active allocation request causes a >4× shrink.
|
||||
- `index.html:1217`: full-pixel dense correction queue persists once allocated.
|
||||
- `index.html:1222-1226, 1898-1944`: export workspace lifetime extends beyond export job.
|
||||
- `index.html:1306-1307, 1648, 1654`: active continuation reinitialization/rebuild can reset progress.
|
||||
|
||||
## Recommended instrumentation before deleting anything
|
||||
|
||||
1. Add counters for numeric-history `attempt / hit / reused pixels / bytes copied`.
|
||||
2. Count reason-0 pixels immediately before `fast-coverage-repair`; if consistently zero, remove/disable that pass in an A/B branch.
|
||||
3. Record `certifiedInteriorTiles` CPU ms and certified ratio per frame; gate when hit rate stays zero.
|
||||
4. Record active-continuation `reinitCount` and `replayedPixelIterations`.
|
||||
5. Record peak/resident bytes for active, correction, export, color snapshot and release-on-idle experiments.
|
||||
6. Separate color-history promotion from numeric-history promotion and A/B color-auto frame time.
|
||||
|
||||
## Production integrity
|
||||
|
||||
No production deletion or behavioral modification was made by this audit. Regression was rerun after inspection and passed.
|
||||
234
docs/performance-cost-audit/ab/README.md
Normal file
234
docs/performance-cost-audit/ab/README.md
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
# Mandelbrot viewer performance-cost A/B test
|
||||
|
||||
Date: 2026-09-29
|
||||
|
||||
## Scope
|
||||
|
||||
This A/B test follows the performance-cost audit. Production `index.html` was **not changed**. A is the current production implementation; B variants live under `experiments/ab/variants/` and disable, defer, or gate one candidate operation at a time.
|
||||
|
||||
Production SHA-256 before/after testing:
|
||||
|
||||
`544820c5024cf6d56e012db6d1bec3ddfcef5d833691b890baa64b26c5c2ee5a`
|
||||
|
||||
The existing regression suite passes on production. The combined high-confidence B variant and the individual structural B variants used for gating/lazy allocation also pass the same regression suite; all retain 19 WGSL kernels.
|
||||
|
||||
## Measurement limitation
|
||||
|
||||
A usable WebGPU backend could not be started in the container (Chromium/SwiftShader/EGL). Therefore this report deliberately separates:
|
||||
|
||||
1. **CPU measured time/work**: exact-interior BigInt proof, adaptive-iteration probe, CPU surrogates for strict-cycle and continuation replay.
|
||||
2. **Exact traffic/residency counts from the production buffer sizes and dispatch extents**: copies, full-frame scans, allocations.
|
||||
3. **Structural/regression validation**: B variants parse and pass the production regression suite.
|
||||
|
||||
No result below is presented as measured GPU frame time unless explicitly stated. Real-device WebGPU p50/p95 remains a follow-up requirement.
|
||||
|
||||
## Summary
|
||||
|
||||
| Candidate | A/B result | Decision from this test |
|
||||
|---|---|---|
|
||||
| Recolor numeric-history copy | B removes 8 B/pixel copy with unchanged numeric field | **Strongly supported** |
|
||||
| Unread `cpuFrameRgba` retention | B removes 4 B/pixel retained JS data; no reader exists | **Strongly supported** |
|
||||
| Pre-primary unknown stats after exact interior | B removes one full-frame `fieldMeta` scan; primary path computes stats later | **Strongly supported** |
|
||||
| Strict direct periodicity-state updates | CPU surrogate median B change **+2.82% slower** | **Do not prioritize for speed** |
|
||||
| FAST full-screen coverage repair | 36 layout cases + 200 semantic randomized cases: **0 holes** | **Strongly supported**, real GPU A/B still desirable |
|
||||
| Idle color-auto `colorSource` snapshot | B avoids 12 B/pixel idle snapshot; idle recolor does not consume it | **Strongly supported** |
|
||||
| Export workspace lifetime | B releases ~24 MiB max workspace after export | **Memory win**, trades for reallocation on next export |
|
||||
| Exact-interior spatial gate | Fixed corpus proof CPU time reduced ~40% at 1024×768 and ~34% at 2048²; 240 random views: 0 gate false-negatives | **Strongly supported** |
|
||||
| Conditional numeric-history residency | Budget-scaled 1920×1080 cases had 0–0.6% exact-grid hit rate; native-grid cases 100% | **Supported conditionally**, not blanket removal |
|
||||
| Lazy export AA samples | 1× export avoids **12 MiB** of four unused AA sample textures across a 3-slot ring | **Strongly supported** |
|
||||
| Adaptive initial-iteration probe removal | 3/48 survey views selected >350; forcing 350 raised predicted work by **+7.2%, +25.3%, +33.1%** | **Do not remove**; cache/gate instead |
|
||||
| Continuation restart from n=0 | Resume-state surrogate reduced work **7.6–9.8%**, identical classifications | **Real waste**, but requires state-preserving redesign |
|
||||
| Deep workspace release after easy frame | Up to ~44.1 MiB standard / ~176.5 MiB high can remain resident | **Memory-lifetime candidate**; real GPU allocation cost unmeasured |
|
||||
|
||||
## Results in detail
|
||||
|
||||
### 1. Recolor numeric-history copy
|
||||
|
||||
Current recolor promotes color history and also copies unchanged `meta/smooth` numeric history. B separates the two responsibilities.
|
||||
|
||||
Exact avoided copy per accepted recolor:
|
||||
|
||||
- fast cap: 4 MiB
|
||||
- standard cap: 8 MiB
|
||||
- high cap: 32 MiB
|
||||
|
||||
At 20 Hz color cycling, that corresponds to 80 / 160 / 640 MiB/s of avoidable numeric-history copying at the respective caps. This is a traffic count, not a GPU-time measurement.
|
||||
|
||||
Variant: `b01-recolor-no-numeric-copy.html`
|
||||
|
||||
### 2. Unread CPU RGBA retention
|
||||
|
||||
`cpuFrameRgba` is assigned after CPU fallback rendering but has no subsequent reader in production. B drops the assignment.
|
||||
|
||||
Avoided retained JS memory at cap:
|
||||
|
||||
- fast: 2 MiB
|
||||
- standard: 4 MiB
|
||||
- high: 16 MiB
|
||||
|
||||
Variant: `b02-cpu-no-rgba-retention.html`
|
||||
|
||||
### 3. Exact-interior pre-primary unknown-stat pass
|
||||
|
||||
When exact tile certification only partially covers the frame, production immediately runs a full-frame unknown-stat scan and synchronization before the primary numerical path. The partial-path result is not consumed; the primary path computes the required statistics later.
|
||||
|
||||
B removes this pre-primary stats scan while preserving the fully-certified shortcut.
|
||||
|
||||
Minimum avoided `fieldMeta` read per pass:
|
||||
|
||||
- fast: 2 MiB
|
||||
- standard: 4 MiB
|
||||
- high: 16 MiB
|
||||
|
||||
This excludes atomics, dispatch overhead, and synchronization cost, so it is a lower bound on removed work.
|
||||
|
||||
Variant: `b03-certify-no-preprimary-stats.html`
|
||||
|
||||
### 4. Strict direct periodicity-state updates
|
||||
|
||||
The strict path does not accept heuristic periodicity, yet production still maintains Brent state. A CPU surrogate compared identical numerical output with and without those state updates.
|
||||
|
||||
Across six fixed views, B was not consistently faster: median elapsed change was **+2.82%** (slower), mean +5.47%. One deep case improved by 1.48%, but the result is not robust.
|
||||
|
||||
Conclusion: semantically redundant state exists, but this A/B test does **not** justify touching it for performance. GPU register pressure could behave differently; real WebGPU measurement would be needed.
|
||||
|
||||
Variant: `b04-strict-no-cycle-state.html`
|
||||
|
||||
### 5. FAST coverage repair
|
||||
|
||||
Production performs a second full-frame FAST pass intended to fill `reason=0` holes after tiled FAST rendering.
|
||||
|
||||
Coverage simulation tested 36 combinations of dimensions, iteration budgets and symmetry modes; all produced **0 holes**. A second semantic randomized test seeded mixed proven/unknown metadata over 200 cases and also found **0 cases with holes**.
|
||||
|
||||
Representative work avoided if the repair pass is unnecessary:
|
||||
|
||||
- 1365×768: 1,050,624 shader invocations; at least 4,193,280 bytes of metadata reads
|
||||
- 2731×1536: 4,202,496 invocations; at least 16,779,264 bytes of metadata reads
|
||||
|
||||
Variant: `b05-fast-no-coverage-repair.html`
|
||||
|
||||
### 6. Idle color-auto snapshot
|
||||
|
||||
Starting color cycling while no render is in progress allocates/copies `colorSource`, but idle recolor reads the current numerical field and does not consume that snapshot. Render-time snapshot behavior is preserved in B.
|
||||
|
||||
Avoided idle snapshot residency:
|
||||
|
||||
- fast: 6 MiB
|
||||
- standard: 12 MiB
|
||||
- high: 48 MiB
|
||||
|
||||
Variant: `b06-idle-colorauto-no-snapshot.html`
|
||||
|
||||
### 7. Export workspace lifetime
|
||||
|
||||
The export ring remains allocated after export. With 512-sized slots and ring depth 3, the tested maximum workspace is approximately **24 MiB**. B destroys it in `finally`, reducing post-export residency to zero. The cost is allocation of three slots on a subsequent export.
|
||||
|
||||
This is a clear memory trade-off, not a proven speed win.
|
||||
|
||||
Variant: `b07-export-release-workspace.html`
|
||||
|
||||
### 8. Exact-interior spatial gate
|
||||
|
||||
The expensive BigInt tile proof only needs to run when the viewport can intersect the main cardioid or period-2 bulb. B uses a conservative bounding-box gate before tile proof.
|
||||
|
||||
The cardioid gate was corrected during the A/B test: the main cardioid reaches `x = 3/8`, so the conservative box is `x ∈ [-3/4, 3/8]`, `y ∈ [-2/3, 2/3]`. The period-2 bulb box is `x ∈ [-5/4, -3/4]`, `y ∈ [-1/4, 1/4]`.
|
||||
|
||||
Random validation: 240 views, 160 gate-negative, **0 false negatives** versus the current exact tile proof.
|
||||
|
||||
Fixed-corpus aggregate CPU proof time:
|
||||
|
||||
- 1024×768: A 41.64 ms → B 25.16 ms, about **39.6% reduction**
|
||||
- 2048²: A 177.70 ms → B 117.08 ms, about **34.1% reduction**
|
||||
|
||||
The largest wins were period-3 views that cannot intersect either analytically certifiable component: about 7–10 ms saved at 1024×768 and 25–30 ms at 2048².
|
||||
|
||||
Variant: `b08-exact-interior-spatial-gate.html`
|
||||
|
||||
### 9. Conditional numeric-history residency
|
||||
|
||||
Exact-grid history reuse requires render-pixel translation to line up exactly with CSS/device motion. A deterministic horizontal-pan survey over offsets -500…500 showed:
|
||||
|
||||
| Configuration | Reuse hits | A residency | B policy |
|
||||
|---|---:|---:|---:|
|
||||
| 1920×1080 fast → 965×543 | 0.2% | ~4.0 MiB | 0 |
|
||||
| 1920×1080 standard → 1365×768 | 0.6% | ~8.0 MiB | 0 |
|
||||
| 1920×1080 high → 2731×1536 | 0% | ~32.0 MiB | 0 |
|
||||
| 1024×768 native standard | 100% | 6 MiB | keep |
|
||||
| 1024×768 DPR2 native ratio | 100% | 24 MiB | keep |
|
||||
|
||||
B keeps numeric history only for simple reduced render/CSS scale ratios (denominator ≤ 8). This preserves the clearly useful native-grid cases while avoiding permanent allocation where reuse is nearly impossible.
|
||||
|
||||
Variant: `b09-conditional-numeric-history.html`
|
||||
|
||||
### 10. Lazy export AA sample textures
|
||||
|
||||
Production allocates four 512² RGBA8 AA sample textures per export slot even for 1× export, although only 2× AA uses them. Four textures are 4 MiB per slot; with ring depth 3 this is **12 MiB** of unnecessary 1× export allocation.
|
||||
|
||||
B creates AA sample textures lazily only for 2× AA. Regression passes.
|
||||
|
||||
Variant: `b10-export-aa-samples-lazy.html`
|
||||
|
||||
### 11. Adaptive initial-iteration probe
|
||||
|
||||
The fixed six-view corpus always selected the base 350 iterations, at a measured probe cost of roughly 1.4–17.4 ms per view. That initially suggested deletion.
|
||||
|
||||
A broader deterministic survey of 48 views disproved the blanket-removal hypothesis. Three views (6.25%) selected more than 350. Forcing 350 increased the surrogate work metric by:
|
||||
|
||||
- +7.21%
|
||||
- +33.14%
|
||||
- +25.25%
|
||||
|
||||
Conclusion: **do not delete this probe globally**. Better candidates are caching by nearby view, a cheaper trigger before the full probe, or reducing sample count where confidence is high.
|
||||
|
||||
### 12. Continuation replay
|
||||
|
||||
Current continuation reconstructs active state from `n=0`; a state-resume surrogate instead kept the base-350 state and continued to 4096. The classifications matched in every tested view.
|
||||
|
||||
Work reduction by view: **7.63% to 9.79%**, median about **7.87%**.
|
||||
|
||||
This is genuine redundant work, but production perturbation/reference changes complicate state validity. It is a redesign candidate, not a safe line deletion.
|
||||
|
||||
## Recommended next actions
|
||||
|
||||
### High-confidence changes to consider integrating
|
||||
|
||||
1. Separate recolor history promotion from numeric-history copy.
|
||||
2. Stop retaining unused CPU RGBA output.
|
||||
3. Remove the partial exact-interior pre-primary stats pass.
|
||||
4. Remove/disable FAST coverage repair after adding a diagnostic hole counter for one release cycle.
|
||||
5. Avoid idle color-auto snapshots.
|
||||
6. Release export workspace after export, subject to acceptable next-export allocation latency.
|
||||
7. Add the conservative exact-interior spatial gate.
|
||||
8. Make export AA sample textures lazy.
|
||||
|
||||
### Conditional changes
|
||||
|
||||
- Allocate numeric history only when exact-grid reuse has realistic geometry.
|
||||
- Release large deep workspaces after returning to an easy frame or after a memory-pressure/idle policy; do not churn them every frame.
|
||||
|
||||
### Do not remove based on this test
|
||||
|
||||
- Adaptive initial-iteration probe.
|
||||
- Strict periodicity state solely for speed; no measurable CPU benefit was established.
|
||||
|
||||
### Requires structural work
|
||||
|
||||
- Preserve valid continuation state so OPERATION_LIMIT pixels do not replay their first ~350 iterations.
|
||||
|
||||
## Reproduction
|
||||
|
||||
From project root:
|
||||
|
||||
```bash
|
||||
node tests/regression.mjs
|
||||
node experiments/ab/ab_test.mjs
|
||||
node experiments/ab/supplement.mjs
|
||||
node experiments/ab/variant_check.mjs
|
||||
```
|
||||
|
||||
Raw data:
|
||||
|
||||
- `docs/performance-cost-audit/ab/results.json`
|
||||
- `docs/performance-cost-audit/ab/supplement.json`
|
||||
|
||||
All B variants are experimental. Production was not modified by this A/B test.
|
||||
1
docs/performance-cost-audit/ab/production.sha256
Normal file
1
docs/performance-cost-audit/ab/production.sha256
Normal file
|
|
@ -0,0 +1 @@
|
|||
544820c5024cf6d56e012db6d1bec3ddfcef5d833691b890baa64b26c5c2ee5a index.html
|
||||
2283
docs/performance-cost-audit/ab/results.json
Normal file
2283
docs/performance-cost-audit/ab/results.json
Normal file
File diff suppressed because it is too large
Load diff
12
docs/performance-cost-audit/ab/supplement.json
Normal file
12
docs/performance-cost-audit/ab/supplement.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"interiorGateRandom": {
|
||||
"cases": 240,
|
||||
"gateFalse": 160,
|
||||
"falseNeg": 0,
|
||||
"totalProofInFalseNeg": 0
|
||||
},
|
||||
"fastCoverageSemantic": {
|
||||
"cases": 200,
|
||||
"casesWithHoles": 0
|
||||
}
|
||||
}
|
||||
1587
docs/performance-cost-audit/results.json
Normal file
1587
docs/performance-cost-audit/results.json
Normal file
File diff suppressed because it is too large
Load diff
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."
|
||||
]
|
||||
}
|
||||
123
docs/reported-view-bugfix/IMPLEMENTATION.md
Normal file
123
docs/reported-view-bugfix/IMPLEMENTATION.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# Reported deep-view artifact investigation and fix
|
||||
|
||||
Date: 2026-09-29
|
||||
|
||||
## Purpose
|
||||
|
||||
Investigate the reported URL state that displayed large yellow/black geometric regions and sometimes an almost entirely black frame. The production source before this pass is preserved as `index.before.html`. The fix is intentionally narrow: do not restore previously removed expensive work unless evidence points to it.
|
||||
|
||||
Reported state:
|
||||
|
||||
- bits: 273
|
||||
- re: `-15958919693715511752069626580195447581643016686947107473855747766272418724092005269`
|
||||
- im: `3884297345146583343999140717464554120497735725078805729405733239132420430333375719`
|
||||
- span: `35679807704506851074208413824041445358585910697267232113437882789120793`
|
||||
- palette: 0
|
||||
- cycle: 0.008
|
||||
- shift: 0.18
|
||||
- base iteration: 350, adaptive enabled, quality high
|
||||
|
||||
At 1108×1582, the adaptive initial target for this region is approximately 1536 iterations and the renderer takes the `fast-extended` path.
|
||||
|
||||
## Root cause 1: unsafe series jump
|
||||
|
||||
The integrated fourth-order series approximation was enabled for the reported view. The fast-reference worker selected:
|
||||
|
||||
- reference pixel ≈ `(69.5, 922.5)`
|
||||
- reference length: 1285
|
||||
- series jump: 773
|
||||
- fourth-order coefficient components up to approximately `2.35e38`, close to the maximum finite f32 magnitude
|
||||
|
||||
The series validity test checked truncation terms in unscaled orbit space, but did not bound the *intermediate scaled f32 polynomial products* used by WGSL. At screen-domain deltas away from the selected reference, `a4 * d^4` can overflow before the later `ldexp` rescales the term.
|
||||
|
||||
The reproducible probe in `experiments/reported-view/analyze.mjs` evaluates the exact worker output from the preserved pre-fix source. On a 32×32 viewport sample grid:
|
||||
|
||||
- pre-fix: 290 / 1024 series starts produce a non-finite fourth-order intermediate
|
||||
- fixed: 0 / 1024, because series acceleration is disabled
|
||||
|
||||
This is sufficient to generate `RANGE` failures and black unknown pixels. Separate CPU surrogate comparison also showed large escape-iteration deviations in this state, so merely adding an overflow clamp would not make the current jump trustworthy enough for publication.
|
||||
|
||||
### Fix
|
||||
|
||||
`SERIES_APPROXIMATION_ENABLED=false` now disables production series construction. The code remains present for future work, but the fast-reference worker emits `jump=0` and zero coefficients.
|
||||
|
||||
Re-enabling series should require at minimum:
|
||||
|
||||
1. viewport/reference-relative bounds on all scaled polynomial intermediates,
|
||||
2. a propagated truncation/rounding error bound carried into the perturbation path,
|
||||
3. a corpus test that checks false escapes, not only absolute approximation error,
|
||||
4. strict handling when the selected reference is off-center.
|
||||
|
||||
## Root cause 2: publishing numerical failures before repair
|
||||
|
||||
The FAST primary frame was colored and presented immediately after the primary perturbation pass, before `recoverNumericalCostOrdered()` repaired `REFERENCE_END`, `RANGE`, and other numerical failures.
|
||||
|
||||
`COLOR_WGSL` writes alpha zero for `FIELD_UNKNOWN`. Therefore an early provisional frame can appear:
|
||||
|
||||
- black when there is no usable history,
|
||||
- mixed with reprojected old colors when stable history exists,
|
||||
- as large geometric regions reflecting reference validity / numerical-failure domains rather than Mandelbrot geometry.
|
||||
|
||||
### Fix
|
||||
|
||||
For `fast-extended`:
|
||||
|
||||
- primary colorization is deferred (`deferPrimaryColor = deferColor || fastExtended`),
|
||||
- unresolved statistics are read,
|
||||
- numerical failures are repaired,
|
||||
- only then is the first provisional recolor/presentation allowed.
|
||||
|
||||
Direct rendering keeps its existing provisional behavior.
|
||||
|
||||
## What was *not* reverted
|
||||
|
||||
The previous FAST coverage-repair dispatch was not restored. The existing full-frame unknown reduction and final invariant still detect untouched reason-0 pixels, and prior A/B coverage tests found no holes. The reported state produced a concrete series failure mechanism, so restoring an O(pixels) repair pass would treat the symptom without addressing the cause.
|
||||
|
||||
Other removal-pass optimizations are unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
### Regression
|
||||
|
||||
`node tests/regression.mjs`
|
||||
|
||||
Result: PASS, 19 WGSL kernels.
|
||||
|
||||
Additional regression pins were added for:
|
||||
|
||||
- `SERIES_APPROXIMATION_ENABLED=false`
|
||||
- FAST primary-color deferral via `deferPrimaryColor=deferColor||fastExtended`
|
||||
|
||||
### Reported-view worker probe
|
||||
|
||||
`node experiments/reported-view/analyze.mjs`
|
||||
|
||||
See `results.json`.
|
||||
|
||||
Key result:
|
||||
|
||||
| Metric | Before | After |
|
||||
|---|---:|---:|
|
||||
| reference length | 1285 | 1285 |
|
||||
| series jump | 773 | 0 |
|
||||
| non-finite series-start samples | 290 / 1024 | 0 / 1024 |
|
||||
|
||||
### Existing integrated CPU benchmark
|
||||
|
||||
`node experiments/integrated_bench.mjs`
|
||||
|
||||
Completed successfully. The series section now reports jump 0 at all tested spans, while periodicity, sparse active state, multi-reference, and CPU fallback tests continue to run.
|
||||
|
||||
### Browser/WebGPU smoke
|
||||
|
||||
`tests/browser_smoke.mjs` could not run because its expected external Chromium remote-debug endpoint at `127.0.0.1:9333` was not available in this environment. This is an environment limitation, not a test assertion failure. Actual WebGPU visual confirmation of the supplied URL remains the most important local-device check.
|
||||
|
||||
## Build summary
|
||||
|
||||
See `build-summary.json`.
|
||||
|
||||
- before SHA-256: `e69b91ead027927d977a065d27d9e27629110122df20aa470eab877e23f77ac7`
|
||||
- fixed SHA-256: `c53c0dc4cb9fa2d2046aaa12ef03cc79c61a710b0c8db36dea1a87be2d9aedd8`
|
||||
- HTML size delta: +396 bytes (comments/guard and publication-order logic)
|
||||
|
||||
The WGSL kernels themselves were not changed in this pass, so kernel hashes remain pinned to the prior 19-kernel set.
|
||||
8
docs/reported-view-bugfix/README.md
Normal file
8
docs/reported-view-bugfix/README.md
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Reported-view bugfix artifacts
|
||||
|
||||
- `IMPLEMENTATION.md` — diagnosis, changes, verification, and limitations.
|
||||
- `results.json` — reproducible before/after fast-reference worker analysis for the reported view.
|
||||
- `integrated-bench.json` — existing CPU-side integrated benchmark after the fix.
|
||||
- `build-summary.json` — file sizes and SHA-256 hashes.
|
||||
- `index.diff` — exact production source diff.
|
||||
- `index.before.html` — preserved pre-fix production source.
|
||||
9
docs/reported-view-bugfix/build-summary.json
Normal file
9
docs/reported-view-bugfix/build-summary.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"beforeBytes": 224559,
|
||||
"afterBytes": 224955,
|
||||
"deltaBytes": 396,
|
||||
"beforeGzip": 57744,
|
||||
"afterGzip": 57958,
|
||||
"beforeSha256": "e69b91ead027927d977a065d27d9e27629110122df20aa470eab877e23f77ac7",
|
||||
"afterSha256": "c53c0dc4cb9fa2d2046aaa12ef03cc79c61a710b0c8db36dea1a87be2d9aedd8"
|
||||
}
|
||||
1970
docs/reported-view-bugfix/index.before.html
Normal file
1970
docs/reported-view-bugfix/index.before.html
Normal file
File diff suppressed because it is too large
Load diff
28
docs/reported-view-bugfix/index.diff
Normal file
28
docs/reported-view-bugfix/index.diff
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
--- /mnt/data/mandelbrot_bugfix_reported_view/docs/reported-view-bugfix/index.before.html 2026-09-29 10:14:53.060514133 +0000
|
||||
+++ /mnt/data/mandelbrot_bugfix_reported_view/index.html 2026-09-29 10:15:44.235560513 +0000
|
||||
@@ -973,8 +973,13 @@
|
||||
function cmuln(a,b){return[a[0]*b[0]-a[1]*b[1],a[0]*b[1]+a[1]*b[0]]}
|
||||
function cscale(a,k){return[a[0]*k,a[1]*k]}
|
||||
function clog2(a){const m=Math.hypot(a[0],a[1]);return m>0?Math.log2(m):-Infinity}
|
||||
+const SERIES_APPROXIMATION_ENABLED=false;
|
||||
function seriesForOrbit(rr,ri,refLen,span,bits,w,h){
|
||||
- if(!span||refLen<16)return{jump:0,coeffs:Array(8).fill(0),errorLog2:0};
|
||||
+ // Disabled after a reported deep-view failure: the current 4th-order jump
|
||||
+ // has no per-pixel propagated error bound and can overflow scaled f32
|
||||
+ // polynomial intermediates near viewport edges. Keep the implementation
|
||||
+ // available for future guarded reintroduction, but never publish it now.
|
||||
+ if(!SERIES_APPROXIMATION_ENABLED||!span||refLen<16)return{jump:0,coeffs:Array(8).fill(0),errorLog2:0};
|
||||
const aspect=Math.max(1e-9,h/Math.max(1,w)),logDelta=fixedLog2Abs(BigInt(span),bits)+Math.log2(Math.hypot(1,aspect));
|
||||
let a=Array.from({length:7},()=>[0,0]),best={jump:0,coeffs:Array(8).fill(0),errorLog2:0},limit=Math.min(refLen,4096);
|
||||
for(let n=0;n<limit;n++){
|
||||
@@ -1776,8 +1781,8 @@
|
||||
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={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}
|
||||
+ const deferColor=!!(r.historyReady&&canUseStableReprojection()),deferPrimaryColor=deferColor||fastExtended;state.deferNumericPublish=deferColor;const ok=await r.computeFrame(snap,iter,token,referencePixel,fastExtended,fastCtx,preserveInterior,!!historySeed,deferPrimaryColor);if(!ok||token!==state.token){state.deferNumericPublish=false;return null}if(!fastExtended&&!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(!deferColor&&!await presentProvisional(r,snap,iter,token,true)){state.deferNumericPublish=false;return null}}else{state.unresolved=stats.total;state.unknownReasons=stats.reasons}
|
||||
const frontier=await refinePixelFrontier(r,snap,iter,token,{total:state.unresolved,reasons:{...(state.unknownReasons||{})}},certified);if(!frontier||token!==state.token){state.deferNumericPublish=false;return null}const effectiveIter=frontier.iter;if(!frontier.converged)throw new Error('全画素反復が安全上限 '+PIXEL_FRONTIER_MAX_ITER+' までに収束しなかったためframeを公開しません');if(frontier.applied&&!deferColor){const painted=await r.recolor(token,effectiveIter);if(!painted||token!==state.token){state.deferNumericPublish=false;return null}}if(deferColor){const painted=await r.recolor(token,effectiveIter);if(!painted||token!==state.token){state.deferNumericPublish=false;return null}}
|
||||
const remaining=numericalFailureCount({reasons:state.unknownReasons||{}}),unprocessed=Math.max(0,state.unresolved-remaining-(state.unknownReasons?.operationLimit||0));if(unprocessed)throw new Error('未計算画素が '+unprocessed+' px 残っています');if(remaining>0)throw new Error('画素精度補修後も数値未確定が '+remaining+' px 残ったためframeを公開しません');state.frameView=snap;state.fieldView={...snap,iter:effectiveIter,frontier,w:canvas.width,h:canvas.height,fastExtended,backend:frontier.applied?'pixel-frontier-'+decision.backend:decision.backend,referenceKey:fastCtx?.key||'',referenceReused,complete:true,membershipCertified:frontier.membershipCertified};r.captureColorSource(snap,state.colorAuto||state.recolorPending);r.presentFrame({scaleX:1,scaleY:1,offsetX:0,offsetY:0});state.deferNumericPublish=false;return{decision,referenceReused,referencePixel,certified}
|
||||
}
|
||||
381
docs/reported-view-bugfix/integrated-bench.json
Normal file
381
docs/reported-view-bugfix/integrated-bench.json
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
{
|
||||
"date": "2026-09-29T10:16:09.784Z",
|
||||
"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": 73.54993999999999
|
||||
},
|
||||
{
|
||||
"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": 2914.8620730000002
|
||||
},
|
||||
{
|
||||
"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": 2906.390313
|
||||
},
|
||||
{
|
||||
"view": "seahorse-boundary",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 410111,
|
||||
"heuristicWork": 410111,
|
||||
"workReduction": 0,
|
||||
"candidates": 0,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 0,
|
||||
"heuristicLimit": 0,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 18.368617999999515
|
||||
},
|
||||
{
|
||||
"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": 2205.721136
|
||||
},
|
||||
{
|
||||
"view": "exterior",
|
||||
"productionBackend": "direct",
|
||||
"pixels": 6144,
|
||||
"baseWork": 686432,
|
||||
"heuristicWork": 137063,
|
||||
"workReduction": 0.8003254510279241,
|
||||
"candidates": 72,
|
||||
"falseCandidates": 0,
|
||||
"baseLimit": 79,
|
||||
"heuristicLimit": 7,
|
||||
"diagnosticIfForcedDirect": null,
|
||||
"ms": 38.21316800000022
|
||||
}
|
||||
],
|
||||
"note": "Production DIRECT_F32 rule with two attracting-cycle confirmations. FAST/perturbation periodic early-stop is intentionally disabled."
|
||||
},
|
||||
"series": {
|
||||
"rows": [
|
||||
{
|
||||
"span": 0.00001,
|
||||
"jump": 0,
|
||||
"errorLog2": 0,
|
||||
"maxAbsError": 0,
|
||||
"maxRelativeError": 0,
|
||||
"refLen": 1200,
|
||||
"buildMs": 9.239160999999513
|
||||
},
|
||||
{
|
||||
"span": 1e-7,
|
||||
"jump": 0,
|
||||
"errorLog2": 0,
|
||||
"maxAbsError": 0,
|
||||
"maxRelativeError": 0,
|
||||
"refLen": 1200,
|
||||
"buildMs": 6.685975000000326
|
||||
},
|
||||
{
|
||||
"span": 1e-9,
|
||||
"jump": 0,
|
||||
"errorLog2": 0,
|
||||
"maxAbsError": 0,
|
||||
"maxRelativeError": 0,
|
||||
"refLen": 1200,
|
||||
"buildMs": 7.990567000000738
|
||||
},
|
||||
{
|
||||
"span": 1e-11,
|
||||
"jump": 0,
|
||||
"errorLog2": 0,
|
||||
"maxAbsError": 0,
|
||||
"maxRelativeError": 0,
|
||||
"refLen": 1200,
|
||||
"buildMs": 7.234717999999702
|
||||
}
|
||||
],
|
||||
"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": 92.0384610000001,
|
||||
"times": [
|
||||
92.0384610000001,
|
||||
92.44781700000021,
|
||||
93.7905269999992,
|
||||
88.7301699999989,
|
||||
99.79122599999937,
|
||||
82.51707699999861,
|
||||
82.31785099999979
|
||||
],
|
||||
"work": 2658084,
|
||||
"escaped": 176648,
|
||||
"operationLimit": 3394,
|
||||
"heuristicInterior": 1654
|
||||
},
|
||||
"multi": {
|
||||
"medianMs": 35.13209400000051,
|
||||
"times": [
|
||||
90.21825200000058,
|
||||
47.29191700000047,
|
||||
35.13209400000051,
|
||||
34.57788200000141,
|
||||
32.72723099999894,
|
||||
35.70634799999971,
|
||||
32.441507999999885
|
||||
],
|
||||
"work": 2658084,
|
||||
"escaped": 176648,
|
||||
"operationLimit": 3394,
|
||||
"heuristicInterior": 1654
|
||||
},
|
||||
"speedup": 2.6197829540134663,
|
||||
"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"
|
||||
}
|
||||
}
|
||||
68
docs/reported-view-bugfix/results.json
Normal file
68
docs/reported-view-bugfix/results.json
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"view": {
|
||||
"bits": 273,
|
||||
"re": "-15958919693715511752069626580195447581643016686947107473855747766272418724092005269",
|
||||
"im": "3884297345146583343999140717464554120497735725078805729405733239132420430333375719",
|
||||
"span": "35679807704506851074208413824041445358585910697267232113437882789120793",
|
||||
"width": 1108,
|
||||
"height": 1582,
|
||||
"iter": 1536
|
||||
},
|
||||
"before": {
|
||||
"sha256": "e69b91ead027927d977a065d27d9e27629110122df20aa470eab877e23f77ac7",
|
||||
"targetBits": 96,
|
||||
"refLen": 1285,
|
||||
"selectionEscape": 1285,
|
||||
"referencePixel": {
|
||||
"x": 69.5,
|
||||
"y": 922.4999999999999
|
||||
},
|
||||
"series": {
|
||||
"jump": 773,
|
||||
"coeffs": [
|
||||
40452732,
|
||||
196035184,
|
||||
-3191889952015122400,
|
||||
2277982484077478000,
|
||||
-7.321407650201077e+27,
|
||||
3.7598990810493897e+28,
|
||||
-2.345861669341488e+38,
|
||||
2.300172499347822e+38
|
||||
],
|
||||
"errorLog2": -27.69705218182358
|
||||
},
|
||||
"nonFiniteSeriesStartSamples": 290,
|
||||
"seriesSampleCount": 1024
|
||||
},
|
||||
"after": {
|
||||
"sha256": "c53c0dc4cb9fa2d2046aaa12ef03cc79c61a710b0c8db36dea1a87be2d9aedd8",
|
||||
"targetBits": 96,
|
||||
"refLen": 1285,
|
||||
"selectionEscape": 1285,
|
||||
"referencePixel": {
|
||||
"x": 69.5,
|
||||
"y": 922.4999999999999
|
||||
},
|
||||
"series": {
|
||||
"jump": 0,
|
||||
"coeffs": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"errorLog2": 0
|
||||
},
|
||||
"nonFiniteSeriesStartSamples": 0,
|
||||
"seriesSampleCount": 1024
|
||||
},
|
||||
"checks": {
|
||||
"baselineSeriesActive": true,
|
||||
"baselineHasNonFiniteSeriesStarts": true,
|
||||
"patchedSeriesDisabled": true
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue