Files
arek 81a1b26842 perf(render): redraw on demand and only changed cells
Rendering used to clear the whole canvas and redraw every cell on
every frame callback (~60 fps) even though the simulation ticks at
20 tps and starts paused, keeping CPU usage at ~85% while idle.

Split simulation timing out of redraw() into the event loop and
render only when displayed content can have changed (tick, painting,
pattern placement, preview movement, grid-line toggle, resize);
idle frames commit only a new frame callback. Repaint incrementally
by diffing the grid against a rendered-state snapshot per row and
redrawing just the changed cells, with damage limited to the
repainted bounding box.

Idle CPU drops to ~1% of one core; running frames render at tick
rate (~20 fps) at microsecond cost. Adds grid_copy() snapshot
helper with tests and archives the optimize-render-on-demand change
with the new on-demand-rendering spec.
2026-11-08 20:11:55 +01:00

2.6 KiB

Tasks

1. Event-driven rendering (stage 1)

  • 1.1 Move simulation tick timing (dt accumulation, simulation_tick() calls) out of redraw() into the main dispatch loop; verify with ctest --test-dir build and by running ./build/wlgol that animation still advances when unpaused
  • 1.2 Add screen_dirty / full_redraw flags set from all content sources (tick, paint drag, click placement, pointer motion with active tool, tool select/rotate, grid-line toggle, need_resize); verify each source produces a repaint by exercising it in the app
  • 1.3 Rework wl_callack_done/loop so a clean frame commits only a new frame callback (no attach, no damage) and a dirty frame renders only when the buffer is released, otherwise defers to the next callback; verify with top that paused idle CPU is ~0% and that unpausing at 20 tps renders without visual glitches
  • 1.4 Update the FPS log line to report rendered frames per second and verify the printed values match expected render counts (≈20 while running, 0 while idle)

2. Incremental repaint (stage 2)

  • 2.1 Add a grid_rendered snapshot buffer sized/moved together with grid in canvas_resize (helper in grid.c/grid.h is acceptable), and verify it survives a window resize without out-of-bounds access
  • 2.2 Implement the per-row diff between grid and grid_rendered and a repaint_cell() that draws background, grid-line fragments on all four edges, and the cell body for each changed cell; verify by stepping (ACTION_STEP) a single-cell change and inspecting that only that cell's pixels change and shared edges stay intact
  • 2.3 Track last-frame preview cells (and whether the preview was shown) and add them to the repaint set alongside the diff, drawing the new preview last; verify the preview leaves no smearing when moving the cursor and rotates/selects cleanly
  • 2.4 Copy grid into grid_rendered at the end of each rendered frame and report damage as the bounding box of repainted cells (whole canvas on full_redraw); verify with WAYLAND_DEBUG=1 ./build/wlgol that damage rects match changed cells
  • 2.5 Extend grid tests in tests/test_grid.c for the snapshot helper (resize copy bounds, snapshot/diff round-trip) and verify with ctest --test-dir build

3. Integration verification

  • 3.1 Full visual pass vs the pre-change renderer: paused idle, painting while paused, pattern placement and rotation, grid-line toggle, resize with fractional scale, unpause at min/max speed — confirm no visual differences; measure CPU with top in each state and record before/after numbers