Skip to content

Spike: Galileo OSM basemap integration#273

Draft
frewsxcv wants to merge 3 commits into
mainfrom
spike/galileo-basemap
Draft

Spike: Galileo OSM basemap integration#273
frewsxcv wants to merge 3 commits into
mainfrom
spike/galileo-basemap

Conversation

@frewsxcv
Copy link
Copy Markdown
Collaborator

@frewsxcv frewsxcv commented Apr 13, 2026

Summary

  • Adds a new workspace crate rgis-basemap-spike that renders an OpenStreetMap basemap via Galileo into an offscreen wgpu texture and displays the RGBA bytes as a Bevy sprite tracking the rgis Camera2d.
  • Proof-of-concept for landing a real basemap layer — this is not production code, it's a spike to validate that Galileo (wgpu 29) and Bevy 0.18 (wgpu 27) can coexist in one binary and that the CPU-bridged pipeline is workable.
  • Native-only (#[cfg(not(target_arch = "wasm32"))]). wasm is deliberately out of scope for this spike.

Architecture

  • A dedicated OS worker thread owns both a tokio multi-thread runtime and Galileo's WgpuRenderer for its entire lifetime. The main Bevy thread never touches either.
  • The plugin communicates with the worker through std::sync::mpsc request/response channels. The worker drains its request backlog before each render (coalescing rapid camera moves to only honor the newest).
  • Main-thread refresh_basemap system each Update:
    1. Drains pending RenderResponses with try_recv (latest wins) and replaces the sprite's Image asset in-place via images.insert(&handle, new_image). (An earlier version swapped to a fresh handle, which works in debug but races Bevy's asset extraction in release mode — keeping the handle identity stable fixes it.)
    2. Reads the Camera2d transform; if it has zoomed past SCALE_DELTA or panned past PAN_DELTA of the last rendered texture's half-width, queues a new render request (respecting in_flight + REFRESH_DEBOUNCE).
    3. Syncs the sprite transform to the latest render snapshot so the basemap sits at the correct world position and scale.
  • The basemap sprite is parked at BASEMAP_Z = -0.05 — just inside Bevy 2D's default visible z range ([-0.1, 1999.9] with the camera at z=999.9) so rgis layers at z≥0 still draw on top.
  • A tiny demo_jump_to_nyc system teleports the camera to NYC on frame 1 and Paris on frame 240 so you can see the basemap swap in without needing to click anything. Remove before this graduates to real code.

Release-mode fixes

Getting this working in release required two fixes landed in this branch:

Build command: RUSTFLAGS="-Zinline-mir=no" cargo +nightly build -p rgis --release

Known limitations / TBD

  • Texture size is fixed at 1024². Should follow the window/viewport in a real integration.
  • wasm support not attempted (no tokio multi-thread runtime on wasm; would need a different concurrency story).
  • Real GPU texture sharing is blocked by the wgpu version skew between Bevy 0.18 and Galileo — this spike intentionally uses the CPU bridge (get_image() → RGBA bytes → Bevy Image) which works but is wasteful.
  • RasterTileLayer is reconstructed on every render inside the worker. A real integration should keep it alive and just update the MapView.
  • demo_jump_to_nyc should be deleted before merging.
  • The pathfinder_simd patch is a temporary hack pending an upstream fix.

Test plan

  • Debug build: cargo build -p rgis
  • Release build: RUSTFLAGS="-Zinline-mir=no" cargo +nightly build -p rgis --release
  • Run the binary, confirm NYC renders at frame 1, Paris at frame 240
  • Drag the map — confirm basemap re-renders as the camera pans past thresholds
  • Confirm no main-thread hitches during renders (worker renders are 30–120ms each in release, 400–1000ms in debug, all off-thread)
  • /tmp/rgis-basemap-latest.png gets written on every render for visual verification

New workspace crate that renders an OpenStreetMap basemap via Galileo
into an offscreen wgpu texture, then displays the RGBA bytes as a Bevy
sprite that tracks the rgis Camera2d. Gated to native targets only
since Galileo pulls tokio and a separate wgpu 29 runtime.

Architecture: a dedicated OS worker thread owns both the tokio runtime
and Galileo's WgpuRenderer for its entire lifetime, so the main Bevy
thread never touches them. The plugin communicates with the worker via
std::sync::mpsc request/response channels, coalescing backlog in the
worker so rapid camera moves don't queue stale renders. The main thread
drains responses non-blockingly each Update.

Release-mode build is currently blocked by two pre-existing/ecosystem
issues (geo-bevy trait-solver overflow on stable; pathfinder_simd 0.5.5
broken on recent nightly via galileo->font-kit). Debug mode works
end-to-end.
- Patch pathfinder_simd to PaulWagener's fork that renames simd_fmin/
  simd_fmax to the new intrinsic names (servo/pathfinder#584 / rust-lang#154043).
- Replace Bevy image handle in-place via images.insert() instead of
  swapping to a fresh handle each render — handle swap races Bevy 0.18's
  asset extraction under release-mode parallelism, so the sprite never
  saw the new texture.
Removes pieces that were only there for spike validation, not the integration:
- demo_jump_to_nyc system that teleported the camera between NYC and Paris
- debug_dump_png side-output to /tmp and the image crate dep that fed it
- src/main.rs standalone binary from before the plugin was wired into rgis
- per-frame info! logging on the main thread and worker lifecycle logs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant