WORKFLOW TEST — make the map better #5
Labels
No labels
agent
agent:ci
agent:done
agent:failed
agent:needs-input
agent:refined
agent:refining
agent:running
agent:skip
autonomous
driven
local
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jeroen/cartopolis#5
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
"The map should look nicer and be faster" cannot be turned into acceptance criteria as written — it names no surface, no metric and no target device. Below is what the map actually is today and where the measurable headroom sits, so the decision is one reply rather than a design round.
What "the map" is. The default basemap source is the VersaTiles vector mirror (
crates/vibe_core/src/world.rs:47), classified as vector bytile_source_kind(crates/viberfox/src/systems/tile_loader.rs:130-152), rasterised client-side to 512 px textures byrender_mvt(crates/viberfox/src/systems/vector_tiles.rs:35).PAINT_ORDER(vector_tiles.rs:146-206) is the cartography.map_streamplaces the resulting textures as a clipmap pyramid of ground quads (crates/viberfox/src/systems/map_stream.rs). Buildings (osm_buildings.rs,lod22.rs), water (water.rs), vegetation (vegetation.rs) and roads (road_texture.rs,map_geometry.rs) are separate systems on top — whether they count as "the map" is Q3 below.Concrete, cited candidates:
A. Ground tiles are sampled without anisotropy.
apply_stream_texturesbuilds each tile image withImageSampler::linear()(map_stream.rs:635); same for the region path (tile_loader.rs:372) and the globe (globe.rs:986). The road-surface texture in the same codebase already asks foranisotropy_clamp: 8(map_geometry.rs:437-442). A ground quad viewed at a grazing angle is the textbook case for anisotropic filtering, and the mip chain is already built (rendering::build_mip_chain).B. The whole pyramid draws every frame, unculled, with a material per tile.
MIN_ZOOM = 10(map_stream.rs:59) up to base zoom 17 (map_stream.rs:248) is 8 levels; the finest ring is radius 4 → 9×9 = 81 tiles (map_stream.rs:53), each coarser ring radius 3 → 7×7 = 49 (map_stream.rs:56). That is 424 quads at low altitude, derived from those constants (not measured — see below). Every one carriesNoFrustumCulling(map_stream.rs:477-479, deliberately: flat quads plus big_space rebasing confused culling) and gets its ownStandardMaterial+ 512² RGBA texture (map_stream.rs:638-644), so nothing batches. Nothing hides a coarse tile that a finer one fully covers — coarse levels are only pushed 0.4 m down per zoom step (map_stream.rs:438).I cannot confirm the render cost of this from here:
ShotMetricshasframe_msbut no draw-call or flat-tile count (shot_harness.rs:243-281), and there is no GPU in this container.C. Overzoomed tiles refetch and reparse the same source body once per sub-tile. Shortbread stops at z14 (
vector_tiles.rs:39), so every z15–z17 request resolves to its z14 ancestor and fetches that whole body (tile_loader.rs:240-242).load_tile_imagededups only through the on-disk cache (tile_loader.rs:169-183) — there is no in-flight map, so on a fresh anchor the 81 z17 tiles (which share only a handful of ancestors) each issue their own request. Each sub-tile then pays its own MVT parse +get_features+ rasterise (tile_loader.rs:223-227): measured ~2.7 ms + ~4 ms + ~35 ms per overzoomed sub-tile, min-of-30 on the Groningen z14 fixture (docs/notes/tile-raster-cost.md). Andmap_streamspawns one task per wanted tile with no admission gate (map_stream.rs:486-495), unlike the region path which caps concurrency atTILE_WORKERS = 3(tile_loader.rs:15,46).D. Fill rate is frame-capped.
MAX_UPLOADS_PER_FRAME = 8(map_stream.rs:596) andMAX_TILE_DESPAWNS_PER_FRAME = 12(map_stream.rs:67). Both are device-loss guards documented in CLAUDE.md, but they mean a 424-tile pyramid needs ≥53 frames to finish uploading even with every tile already decoded.E. The map has no text.
PAINT_ORDERdeliberately omits*_labels,addressesandpoispending a glyph pipeline (vector_tiles.rs:144-146). This is the largest single "looks nicer" lever and by far the largest job.F. Palette/cartography.
vector_tiles::palette(vector_tiles.rs:89-123) is the entire look, deliberately flatter than osm-carto because it is lit ground texture, not a map read at arm's length (vector_tiles.rs:84-88).G. The perf win the notes already name is untyped
serde_json::Valueparsing for 3DBAG/Overpass (lod22.rs:459,osm_buildings.rs:108,osm_buildings.rs:1845), called out as "the next win if 3DBAG streaming latency matters" indocs/notes/wasm-performance.md. That is buildings, not basemap.Already ruled out — do not re-test first. Atmosphere, env-map IBL, haze, clouds and rain were A/B'd on a real Intel Xe-LPG adapter and all landed within noise (
docs/notes/wasm-performance.md). Shadows were never tested and are ~79% of submitted triangles;?no_shadowsexists to check that one. Moving rasterisation to the GPU is an explicitly deferred open question (docs/notes/tile-raster-cost.md) — it is streaming-latency cost on a worker pool, not frame-time cost, and any proposal must beat that and survive wasm32 + no-C-dependencies.Approach
Not specifiable until Q1–Q3 are answered. For each lane, the blast radius:
crates/viberfox/src/systems/map_stream.rs(+tile_loader.rs,globe.rsif applied consistently)map_stream.rs, plus oneShotMetricsfield inshot_harness.rsto make it measurabletile_loader.rs,map_stream.rs, possiblyvector_tiles.rsmap_stream.rsonly (but they are device-loss guards; raising them needs the weak-GPU check, not just a number)vector_tiles.rs+ a glyph/font path; new dependency decisionvector_tiles.rspalette +PAINT_ORDERlod22.rs,osm_buildings.rsAcceptance criteria
Lane-independent criteria that hold for whichever lane is chosen. The lane-specific list is blocked on Q1.
frame_msand/orsettled_sfromcargo shots --csv, ortools/cdp_probe.ts --gpu --profilefor the web build — against the real adapter, not SwiftShader (docs/notes/wasm-performance.md).ShotMetrics(shot_harness.rs:243) so it lands in the log line,--csvand--dump-stateat once, per the documented pattern in CLAUDE.md.cargo test -p viberfox --bin viberfoxpasses (note--libdoes not exist).VK_ERROR_DEVICE_LOSTguards in CLAUDE.md are not to be loosened without a weak-GPU check.docs/notes/tile-raster-cost.mdordocs/notes/wasm-performance.mdare written back into those notes in place, with date and method.Verification
Runnable here (no GPU needed):
Workstation only — everything visual or frame-timed:
Two notes for whoever writes the verification steps:
docs/shots.toml:23pins the vector source, socargo shotsexercises the client-side rasteriser (CLAUDE.md's line about the built-in shot fallback being the raster basemap is stale —DEFAULT_TILE_URL_TEMPLATEis vector,vibe_core/src/world.rs:47). And a grazing-angle preset is needed to see lane A at all; a top-down shot will show nothing.Out of scope
globe.rs,globe_ocean.rs) unless Q3 says otherwise — it is a separate BRDF and imagery path with its own crossfade rules.docs/notes/tile-raster-cost.md; re-opening it is its own issue.MAX_UPLOADS_PER_FRAME,MAX_TILE_DESPAWNS_PER_FRAME,MAX_GLOBE_TILE_BUILDS_PER_FRAMEor the OSM detail-patch freeze as a standalone "speedup".Open questions
cargo shotsand look.Branch:
issue-5Original request
The map should look nicer and be faster.
🤖 Refined by the viberfox issue agent. Reply with @agent refine and what is wrong to have this rewritten.
🤖 Refining this before anything gets built — reading the code, no branch, no commits. If it turns out I'd have to guess at the requirements I'll stop and ask instead.
🤖 This needs a decision before I build anything. The open questions are in the description above.
Answer them and reply with @agent refine to have the spec rewritten, or @agent to make me implement it as it stands.
🤖 viberfox issue agent on the VPS — 21 turns · $1.71 · session
8f53fc22-e210-4be0-b025-2a0d9e530721. Reply with @agent to continue this session.