Make Canvas immediate-mode (per-frame vertex buffer) #13

Open
opened 2026-09-25 20:36:45 -07:00 by burkkyy · 0 comments
Owner

Canvas is meant to be an immediate-mode drawing API (debug draw, gizmos, prototypes, examples), but it currently behaves as a retained one.

Problem

Each Canvas::triangle / Canvas::quad call goes through Application::pushMesh, which creates a new GPU Mesh that lives until Canvas::clear(). clear() calls device.waitIdle() before freeing them.

  • Calling Canvas from onUpdate every frame creates GPU buffers without limit.
  • Drawing once in onCreate only works by accident.

Proposal

  • Canvas calls append vertices/indices to a per-frame CPU array.
  • Each frame, that array is uploaded into a dynamic vertex/index buffer (one per frame in flight), drawn, then reset automatically.
  • Remove Canvas::clear(), the waitIdle in clearMeshes, and the meshes list in Application::Impl.
  • Decide what Canvas draws into: the main backbuffer, on top of everything, to start with. Later possibly a chosen scene view, e.g. editor viewport overlays.
  • Application::camera (Camera2D) only exists for Canvas. Move it onto Canvas, or make Canvas draw in screen space, so it isn't confused with scene cameras.

Done when

  • examples/triangle works with drawing done in onUpdate each frame
  • GPU memory stays flat when Canvas is used every frame
  • No waitIdle on the Canvas path
Canvas is meant to be an immediate-mode drawing API (debug draw, gizmos, prototypes, examples), but it currently behaves as a retained one. ## Problem Each `Canvas::triangle` / `Canvas::quad` call goes through `Application::pushMesh`, which creates a new GPU `Mesh` that lives until `Canvas::clear()`. `clear()` calls `device.waitIdle()` before freeing them. - Calling Canvas from `onUpdate` every frame creates GPU buffers without limit. - Drawing once in `onCreate` only works by accident. ## Proposal - Canvas calls append vertices/indices to a per-frame CPU array. - Each frame, that array is uploaded into a dynamic vertex/index buffer (one per frame in flight), drawn, then reset automatically. - Remove `Canvas::clear()`, the `waitIdle` in `clearMeshes`, and the `meshes` list in `Application::Impl`. - Decide what Canvas draws into: the main backbuffer, on top of everything, to start with. Later possibly a chosen scene view, e.g. editor viewport overlays. - `Application::camera` (`Camera2D`) only exists for Canvas. Move it onto Canvas, or make Canvas draw in screen space, so it isn't confused with scene cameras. ## Done when - `examples/triangle` works with drawing done in `onUpdate` each frame - GPU memory stays flat when Canvas is used every frame - No `waitIdle` on the Canvas path
burkkyy added the enhancement label 2026-09-25 20:36:45 -07:00
burkkyy added this to the sophia project 2026-09-25 20:37:46 -07:00
Sign in to join this conversation.