Skip to content

Rewrite map rendering system with progressive materialization#78

Open
Senzaiken wants to merge 1 commit into
kaczy93:masterfrom
Senzaiken:senz-renderer-rewrite
Open

Rewrite map rendering system with progressive materialization#78
Senzaiken wants to merge 1 commit into
kaczy93:masterfrom
Senzaiken:senz-renderer-rewrite

Conversation

@Senzaiken

Copy link
Copy Markdown
Contributor

Rewrites the map rendering pipeline so large maps stay smooth, and adds memory-aware safeguards so the editor degrades gracefully (instead of thrashing/OOMing) on machines that can't hold a whole map in RAM. No behavior change on machines with enough memory.

Draw batching (MapRenderer)

  • Batcher pool grown (8 to 32) and batch size raised (4096 to 8192 tiles).
  • Smarter batcher eviction (least-pending / least-recently-used) instead of always evicting slot 0, cutting mid-frame flushes.
  • New cached-geometry path (DrawCachedVertices) backed by a shared quad index buffer.
  • Per-frame stats (draw calls, flushes, evictions, vertex uploads) for profiling.

Progressive block materialization (MapManager)

  • Blocks are turned into render objects ("materialized") lazily rather than all at once.
  • A foreground spiral fills the current view from the camera outward, under an adaptive per-frame budget (time + count) so frame times stay stable while loading.
  • The cap grows when frames are fast/idle and shrinks under load.
  • Optional whole-map background fill (PreloadMapOnConnect) spirals outward when enabled.

Region render caching (low zoom)

  • The map is split into 32×32-block regions; terrain and large statics are baked into per-region vertex buffers.
  • At low zoom (≤0.22 terrain, ≤0.35 statics) the renderer draws these cached buffers instead of thousands of per-object calls.
  • Regions are dirty-tracked; rebuilds are budgeted and nearest-first. Animated/ghost tiles are still drawn live on top.

Selection buffer

  • The pick buffer is only redrawn when something actually changes (camera settles, view state/filter changes) instead of every frame.
  • At extreme zoom-out it renders only a window around the cursor.

Misc

  • Discrete zoom levels and clip-space culling of off-screen detailed objects at low zoom.
  • LandObject.AverageZ is computed once instead of per call.
  • Fixes: RadarMap pooled-buffer leak, a StaticsManager bounds off-by-one, and ghost-tile cleanup on undo/redo/tool-end.
  • CSV frame profiler + a counters panel in the Debug window.

On connect, the map size is compared against available RAM (GC.GetGCMemoryInfo) to pick behavior. All three tiers are no-ops when the map comfortably fits:

  • Preload gating - the whole-map preload runs only if it fits within ~50% of available RAM; otherwise blocks load on demand around the view.
  • Region-cache eviction - if caching the full map would exceed ~25% of RAM, region vertex buffers outside a window around the view are released (and rebuilt if the view returns).
  • Materialize budget + zoom floor + block eviction - the number of materialized blocks is capped; zoom-out is floored so the view itself can't exceed the budget; and off-screen blocks are released as you pan. The raw block stays cached, so returning re-materializes without a server round-trip.

On a low-RAM machine the managed heap stays bounded and zoom-out clamps, instead of climbing toward an out-of-memory crash.

@Senzaiken Senzaiken force-pushed the senz-renderer-rewrite branch from aa56ec3 to 730c1f5 Compare June 17, 2026 14:52
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