Skip to main content

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-config through 14-previews, plus run-local 15-valenar-worlddata export 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.
  • 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 === [], and contentHash matches the manifest-derived mapv10-content-v1|mapv10-artifacts-v1|continent-lod6|20260502|continent|1778664235467|0,0,2400,1800|2048x1536|62:623440281|131083:57305509 at load and final proof state.
    • Terrain shading probe: __mapv10TerrainShadingProbe reports hasDirectionalLight: 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, and HTMLCanvasElement.toDataURL; totalCalls is 0 at load and after all proof steps. The verifier launches Chromium with GPU compositing enabled and promotes matching WebGL/THREE/GPU warning signatures into window.__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.21 contains map feature/crossing evidence and the browser proof requires a visible marker pass.
  • 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 product meshTileManifest leaves window.__mapv10Ready.loaded false, shows manifest is missing required product meshTileManifest, fetches no generated products after the manifest, and makes no fallback request to any other generated run such as continent-lod6 or sample-run.

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 from navigator.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-thread TileRequestScheduler stays in front to gate concurrent network requests.
  • Worker-prepared mesh buffers: LoadedMeshAsset now carries pre-transformed scene-local positions, optional normals, optional worldKm sidecar, and optional zero-init RGB color buffer. The renderer's geometryFromMeshAsset is a thin attribute wrap with no main-thread vertex loop and no computeVertexNormals call.
  • Incremental auxiliary batching (Mapv10ThreeRenderer.applyAuxiliaryMeshSet + rebuildAuxiliaryBatch): one merged THREE.BufferGeometry per 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 into FrameBudget lane auxiliary and 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 + GPU CanvasTexture. Reference-counted release frees the texture on the last consumer's release. Each label still owns a per-label SpriteMaterial so per-label fade opacity uniforms cannot alias when two labels share text.
  • Footprint material cache: footprints with the same kind share a single MeshBasicMaterial (the renderer used to allocate one per footprint).
  • Per-label fade-in/fade-out: label sprites fade over LOD_FADE_MS = 320 via computeFadeProgress in 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)

StepFrame p50 / p95 / p99 mslongTaskCountroute drawsbudgetDrainedTotallabelCacheSize
continent-overview1.1 / 16.8 / 194.44151
province-cluster1.2 / 6.0 / 120.0419109
location-detail1.6 / 7.3 / 37.6412262
detail-boundary-a1.9 / 7.1 / 37.6412362
detail-boundary-b1.9 / 6.9 / 37.6412865
zoom-back-out1.9 / 6.5 / 32.6413152

Current canonical proof characteristics:

  • runtimeProducts.rasterAssetCount records the full runtime raster contract per active tile, including base forestMask/wetlandMask sidecars plus the effective influence-derived sidecars.
  • passes.route remains 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: 0 and evictionsPerSecond: 0 across every step; the proof also records no forbidden root high-detail fetches.
  • Required-product hard-failure probe still records loaded: false and 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 build still 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.