M10 Continent Proof
Status: implemented from code truth on 2026-05-02; canonical browser baseline captured on 2026-05-06.
Artifact
- Run:
viewer/public/continent-lod6, served by the viewer artifact namespace as/mapv10/runs/continent-lod6/manifest.json. - Validation summary:
pass, 0 failed stages. - Scale:
continent, 2400 x 1800 km, 4,320,000 km2, 2048 x 1536 raster. - Political scale: 1250 provinces, 11000 Locations.
- Required raster contract: root
materialWeightsB.rgba8.bin, generic influence products, full-resolution influence/effective rasters, and matching influence/effective assets in every raster tile. - Physical size on disk: 1.3G.
Implemented Proof
- Same generator pipeline as smaller presets: stages
00-configthrough14-previews, plus run-local15-valenar-worlddataexport products. - The current generator contract uses the canonical continent LOD pyramid:
- z0
continent: 1 x 1, sample step 32. - z1
macro-region: 4 x 3, sample step 16. - z2
realm: 8 x 6, sample step 8. - z3
province-cluster: 16 x 12, sample step 4. - z4
province: 32 x 24, sample step 2. - z5
location-detail: 64 x 48, sample step 1. - z6
close-detail: 128 x 96, sample step 1, close detail scale 8. - z7
settlement-detail: 256 x 192, sample step 1, close detail scale 8. - Total: 65,533 tile slots per raster, vector, semantic, and mesh family.
- z0
- Zoom-aware vector density:
- continent and macro-region bands: realm-level overview, major water/routes, no province/Location/detail flood.
- realm and province-cluster bands: province-level payload, no Location/detail flood.
- province band: Location labels/polygons without full local crossing/map-feature flood.
- location-detail band: full local detail payload, labels, features, crossings, and routes.
- Auxiliary mesh streaming:
- water and route assets declare compact z/x/y LOD tile membership; borders render from the generated border-SDF raster in the terrain shader.
- continent mesh manifests are bounded products, not eager high-detail boot meshes.
- Viewer streaming:
- camera/zoom selected terrain, runtime, vector, and auxiliary mesh products.
- shared max in-flight scheduler limit: 24.
- telemetry exposes requested/loaded/failed/canceled/evicted counts, memory bytes, LOD counters, family fetch counts, non-terrain mesh count, label counts, frame p50/p95/p99, and long task count where available.
- Browser probe:
- Command:
cd examples/map/mapv10/viewer && npm run verify:browser:continent - Manifest URL:
/mapv10/runs/continent-lod6/manifest.json - JSON:
viewer/verification/m10-lod6-continuity-proof.json - Screenshots:
viewer/verification/m10-lod6-continuity-proof-overview.png,viewer/verification/m10-lod6-continuity-proof-province-cluster.png,viewer/verification/m10-lod6-continuity-proof-location-detail.png,viewer/verification/m10-lod6-continuity-proof-boundary-a.png,viewer/verification/m10-lod6-continuity-proof-boundary-b.png,viewer/verification/m10-lod6-continuity-proof-zoom-back.png - Probe result:
pass, 20 proof steps including overview, detail, slow-zoom, constant-distance move, every map mode at overview/detail, zoom-back, and required-failure UX. - Network proof: 3713 raw requests, 3696 unique requests; raster 2850, semantic 300, vector 150, terrain 157, water 8, borders 0, routes 77; preview requests 0; tile preview requests 0; forbidden root high-detail fetches
[]. - Browser readiness identity:
window.__mapv10Ready.loaded === true,errors === [], andcontentHashmatches the manifest-derivedmapv10-content-v1|mapv10-artifacts-v1|continent-lod6|20260502|continent|1778664235467|0,0,2400,1800|2048x1536|62:623440281|131083:57305509at load and final proof state. - Terrain shading probe:
__mapv10TerrainShadingProbereportshasDirectionalLight: true,normalsAreComputed: true, world-normal shader use, relief uniforms present, and normalized light direction[-0.591, 0.469, 0.656]. - Source-owned readback audit: before the viewer script runs, the verifier instruments
WebGLRenderingContext.readPixels,WebGL2RenderingContext.readPixels,CanvasRenderingContext2D.getImageData,CanvasRenderingContext2D.drawImage, andHTMLCanvasElement.toDataURL;totalCallsis0at load and after all proof steps. The verifier launches Chromium with GPU compositing enabled and promotes matching WebGL/THREE/GPU warning signatures intowindow.__mapv10Ready.errors, so browser-level GL failures fail fast instead of timing out. - Overview proof: z0-only terrain/runtime tile loading, visible water/route/label context, runtime raster sidecars for base and effective ecology fields, and no z1+ raster/semantic/vector/terrain tile fetches before the detail camera.
- Continuity proof: zero parent-dropped-before-child-ready events, zero auxiliary gaps, zero LOD oscillation events, and zero cache evictions.
- Location-detail marker proof: selected z5 tile
z5.23.21contains map feature/crossing evidence and the browser proof requires a visible marker pass.
- Command:
- Required-product failure probe:
- JSON: included in
viewer/verification/m10-lod6-continuity-proof.json. - Screenshot:
viewer/verification/m10-lod6-continuity-proof-missing-required-product.png. - Probe result:
pass; a temporary manifest missing required truth productmeshTileManifestleaveswindow.__mapv10Ready.loadedfalse, showsmanifest is missing required product meshTileManifest, fetches no generated products after the manifest, and makes no fallback request to any other generated run such ascontinent-lod6orsample-run.
- JSON: included in
Performance Foundation Pass
The structural proof above passed but recorded p99 frame spikes up to 770.6 ms and cumulative long-task counts up to 143 across the probe. A performance/LOD foundation pass landed on 2026-05-02 to address the main-thread cost without compromising the structural proof.
Architecture changes
- Decoder worker pool (
viewer/src/workers/): a fixed pool of dedicated workers (sized fromnavigator.hardwareConcurrency, capped at 6) handles HTTP fetch, byte-length checks, typed-array decode, binary mesh world-coordinate transform, per-vertex normal computation, worldKm sidecar, and vector-tile JSON parse. Results post back as transferable typed-array buffers (zero-copy on most browsers). The 24-tile main-threadTileRequestSchedulerstays in front to gate concurrent network requests. - Worker-prepared mesh buffers:
LoadedMeshAssetnow carries pre-transformed scene-local positions, optional normals, optional worldKm sidecar, and optional zero-init RGB color buffer. The renderer'sgeometryFromMeshAssetis a thin attribute wrap with no main-thread vertex loop and nocomputeVertexNormalscall. - Incremental auxiliary batching (
Mapv10ThreeRenderer.applyAuxiliaryMeshSet+rebuildAuxiliaryBatch): one mergedTHREE.BufferGeometryper material kind per family, with a per-batch dirty flag and a stable membership signature. When tile membership changes, only dirty batches re-merge. Re-merges enqueue intoFrameBudgetlaneauxiliaryand drain under a per-frame time budget so a transition does not produce one synchronous burst of GPU geometry uploads. FrameBudget(viewer/src/renderer/FrameBudget.ts): per-frame work-item dispatcher with four lanes (terrain, auxiliary, marker, label) and round-robin minimum + total-budget draining. Same-key enqueues coalesce so quick membership thrash does not multiply rebuild work.LabelTextureCache(viewer/src/renderer/LabelTextureCache.ts): text-keyed memoization of the canvas raster + GPUCanvasTexture. Reference-counted release frees the texture on the last consumer's release. Each label still owns a per-labelSpriteMaterialso per-label fade opacity uniforms cannot alias when two labels share text.- Footprint material cache: footprints with the same
kindshare a singleMeshBasicMaterial(the renderer used to allocate one per footprint). - Per-label fade-in/fade-out: label sprites fade over
LOD_FADE_MS = 320viacomputeFadeProgressin the animate loop. Terrain and auxiliary fade are deferred to a future pass that uses geomorph or a shared-shader uniform; their continuity is already structurally locked by the existing parent-retain logic (parentDroppedBeforeChildReady = 0).
New telemetry surfaced in the proof
frameBudgetDrainedMs,frameBudgetDrainedTotal,frameBudgetPending: prove the FrameBudget is alive.decoderWorkerCount: shows the worker pool size for the run.labelTextureCacheSize: shows the unique-text count cached so far.
Refreshed numbers (m10-lod6-continuity-proof.json, 2026-05-06)
| Step | Frame p50 / p95 / p99 ms | longTaskCount | route draws | budgetDrainedTotal | labelCacheSize |
|---|---|---|---|---|---|
| continent-overview | 1.1 / 16.8 / 194.4 | 4 | 1 | 5 | 1 |
| province-cluster | 1.2 / 6.0 / 120.0 | 4 | 1 | 9 | 109 |
| location-detail | 1.6 / 7.3 / 37.6 | 4 | 1 | 22 | 62 |
| detail-boundary-a | 1.9 / 7.1 / 37.6 | 4 | 1 | 23 | 62 |
| detail-boundary-b | 1.9 / 6.9 / 37.6 | 4 | 1 | 28 | 65 |
| zoom-back-out | 1.9 / 6.5 / 32.6 | 4 | 1 | 31 | 52 |
Current canonical proof characteristics:
runtimeProducts.rasterAssetCountrecords the full runtime raster contract per active tile, including baseforestMask/wetlandMasksidecars plus the effective influence-derived sidecars.passes.routeremains present in geography/routes/detail steps; route auxiliary fetch count reaches 124 entries in the refreshed proof after the low-LOD aggregate route mesh refresh. Border ribbons are intentionally absent because borders are SDF-sampled in the terrain shader.- Cache telemetry records
cumulativeEvictions: 0andevictionsPerSecond: 0across every step; the proof also records no forbidden root high-detail fetches. - Required-product hard-failure probe still records
loaded: falseand zero fallback fetches. - p50/p95 stay close to the frame budget after initial overview streaming in the refreshed headless run; p99 peaks at 194.4 ms during initial continent overview streaming, cumulative long-task count is 4, and interaction-heavy scenarios are not covered by this structural proof.
Residual Risk
The M10 proof is now a structural and decode-foundation pass, not a final performance sign-off.
- Frame p99 peaks at 194.4 ms during initial continent overview streaming in the refreshed headless proof, while cumulative long-task count is 4 by zoom-back-out; interaction-heavy hover sweeps are not covered by this structural proof. The remaining main-thread cost is the per-frame Three.js render loop (matrix uploads, frustum culling, label collision projection over up to 69 visible labels in this proof) and the rasterizer load of a continent-scale scene with overlay+terrain dual passes.
npm run buildstill emits Vite's large chunk-size warning for the viewer bundle (~621 kB minified). The decoder worker is correctly emitted as its own ~5 kB chunk; the main bundle weight is Three.js. Bundle splitting is deferred.- Terrain and auxiliary mesh fade-in are deferred to a future pass (geomorph or shared-shader uniform via
onBeforeCompile/onBeforeRender). Label fade is in place.
Remaining Work
No structural M10 code blocker remains after this proof. The next lane should continue verifier/audit work and address the residual perf surface:
- Readability of labels/borders/routes across macro-region, realm, province-cluster, province, and location-detail bands (visual review).
- Location-detail label collision quality across more tile boundaries.
- Long-task count and p95/p99 frame behavior on slower browser hardware.
- Bundle splitting or an explicit Vite chunk-size budget decision.
- Terrain and auxiliary fade-in via geomorph or shader uniform (without per-mesh material clones).
- Label collision layout pass off the main thread (worker-driven) once the layout cost shows up as the next dominant long task.