Files
wlgol/openspec/specs/on-demand-rendering/spec.md
T
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.8 KiB

on-demand-rendering Specification

Purpose

Schedules window repaints for the Game of Life canvas: frames are produced only when displayed content changes, and only the changed cells are drawn, so idle CPU usage is near zero while the visible output stays identical to a full repaint.

Requirements

Requirement: Frame is rendered only on content change

The system SHALL produce a new rendered frame only when displayed content has changed since the last rendered frame, and SHALL NOT redraw when it has not.

Scenario: Idle while paused

  • WHEN the simulation is paused, no tool is active, and no input arrives
  • THEN no new frame is rendered over consecutive display refreshes

Scenario: Tick changes grid

  • WHEN a simulation tick changes the grid while the simulation is running
  • THEN a new frame is rendered that reflects the new generation

Scenario: Edit marks content dirty

  • WHEN the user paints cells or places a pattern
  • THEN a new frame is rendered with the modified cells

Scenario: Preview follows pointer

  • WHEN a tool is selected and the pointer moves to a different cell
  • THEN a new frame is rendered with the pattern preview at the new position

Scenario: Preview-affecting action

  • WHEN the user selects a tool, rotates the active tool, or toggles grid lines
  • THEN a new frame is rendered reflecting the change

Scenario: Resize forces full redraw

  • WHEN the canvas size or scale changes
  • THEN a new frame is rendered over the whole canvas

Requirement: Simulation advances independently of rendering

The system SHALL accumulate simulation time and apply ticks based on elapsed time, regardless of whether frames are rendered.

Scenario: Ticks accumulate while idle

  • WHEN multiple tick intervals elapse without a rendered frame
  • THEN every elapsed interval results in exactly one applied tick

Requirement: Only changed cells are repainted

The system SHALL redraw only the cells whose state differs from the last rendered frame, drawing each changed cell's background, grid-line fragments, and cell state, and SHALL leave unchanged cells' pixels untouched.

Scenario: Single cell changes

  • WHEN exactly one cell changes state between rendered frames
  • THEN only that cell's pixel area is rewritten in the framebuffer

Scenario: Unchanged pixels are preserved

  • WHEN a frame is rendered after a change
  • THEN pixels of unchanged cells are identical to the previous frame

Requirement: Damage is limited to the changed region

The system SHALL report surface damage covering only the area repainted in the current frame.

Scenario: Small change damages small region

  • WHEN only a few cells changed
  • THEN the reported damage is limited to the union of those cells' areas rather than the whole surface