Skip to content

2D canvas batching disabled (use_batching=false) — quantify and likely re-enable #89

Description

@dsrw

Summary

Enu ships with 2D canvas batching disabledbatching/options/use_batching=false (and use_batching_in_editor=false) in app/project.godot. This is the committed baseline, not drift. We should quantify its cost and, most likely, re-enable it.

Why this is on the radar

While chasing the island load stall, profiling the render thread (thread_model=2, VisualServer on its own thread) under load repeatedly showed it saturated on the 2D canvas path, not on voxel work:

render_canvas → _legacy_canvas_item_render_command → _draw_polygon → safe_buffer_sub_data → glBufferSubData → GLDContextRec::getCommandBuffer → new MTLCommandBuffer → semaphore_wait

On Apple Silicon, GLES3 runs through Apple's GL-on-Metal layer, so each un-batched canvas item forces its own glBufferSubData → a fresh Metal command-buffer allocation, and when the pool is exhausted the render thread blocks in semaphore_wait. With batching off, every glyph in the console/stats overlay, every toolbar element, and every turtle-path polygon pays this individually.

This is NOT the load-stall bug (that's a separate intermittent missing-terrain-chunk issue — the support raycast finds no ground under spawn and the gate rides its 30s deadline; being fixed separately). But the batched-vs-unbatched render cost is real and independent, and it directly affects the "30+ fps while the world streams" goal because the 2D UI shares the render thread with voxel mesh uploads.

What to look at

  1. use_batching=false — the main lever. Find out why it's off. Godot 3.5 batching has historically caused 2D artifacts (custom shaders, overlapping items) — it was likely disabled to fix a visual bug, so flipping it on is not free: verify the turtle path, toolbar, and stats/console overlays render correctly before/after.
  2. TurtleOverlay is its own Viewport (separate render target inside a ViewportContainer, app/components/TurtleOverlay.gdns). Check it isn't running a full render pass every frame when the turtle isn't drawing.
  3. Stats/console overlay redraw rate — if it re-emits its whole text canvas every frame, that's a large un-batched item count; redraw-on-change would cut it.

How to quantify (no new instrumentation needed)

ENU_PERF_LOG=1 already emits a PERF line every ~2s (src/game.nim) with exactly the right metrics: fps, frame_ms, draw_calls (RENDER_DRAW_CALLS_IN_FRAME), objects, vertex_mem_kb. draw_calls will show batching's effect most directly.

Suggested A/B: ENU_PERF_LOG=1 nim start with use_batching=false vs true, comparing steady-state (idle in a built level, console/toolbar visible) and while moving during streaming. Report fps + draw_calls deltas.

A crude earlier A/B (during load, not steady state) showed batching-on measurably reduced render-thread pressure — treat as a hint, not a real number.

Context / handoff

Found during a load-perf debugging session on branch island-load-perf. The missing-chunk root cause is being handled separately; this issue is purely the 2D-rendering efficiency workstream. Owner intends this fixed regardless of the fps delta.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions