Skip to content

perf: skip redundant work in the scene update and draw loops - #52

Merged
alegemaate merged 1 commit into
mainfrom
perf/scene-loop
Aug 17, 2026
Merged

alegemaate merged 1 commit into
mainfrom
perf/scene-loop

Conversation

@alegemaate

Copy link
Copy Markdown
Member

Split out of #47.

Four per-frame costs in the scene loop that only pay off when something actually changed:

  • Dead object compaction. update ran erase_if over the whole vector every frame. Scan for a dead object first and only compact when one exists — the scan is a read-only pass that short-circuits on the first hit, the compaction moves elements and reallocates.
  • Z-index sort. draw re-sorted every object every frame. Sort only when the vector is no longer in z-order. As a side benefit this stops the unstable ranges::sort from permuting equal-z-index objects between frames, so draw order among ties is now stable.
  • Batched insertion. Pending objects were appended one push_back at a time. Reserve once and insert the range.
  • Clock reads. The managed scene loops sampled high_resolution_clock twice for the frame delta and again for the FPS counter. One read per frame now.

The original PR drove the first two off _needs_sort / _has_dead_objects member flags. Both are dropped here:

  • _has_dead_objects was cleared before the update loop and re-set during it, so an object killed outside update (during draw, or by external code between frames) wasn't noticed until the following update — one frame late. Because the draw loop only checks active, not alive, that dead object got drawn for an extra frame. The any_of scan has the same cost class as the flag path and is exactly the original semantics.
  • _needs_sort was redundant: it was ||'d with the is_sorted check, which already catches every case that would set it. An insertion either breaks sortedness — is_sorted sees it — or doesn't, in which case no sort was needed.

Three per-frame costs in the scene loop that only pay off when something
actually changed:

- update compacted the object vector with erase_if every frame. Scan for
  a dead object first and only compact when one exists; the scan is a
  read-only pass that short-circuits, the compaction moves elements.
- draw re-sorted every object by z-index every frame. Sort only when the
  vector is no longer in z-order. This also stops the unstable sort from
  permuting equal z-index objects between frames.
- Pending objects were appended one push_back at a time. Reserve once and
  insert the range.

Also read the clock once per frame in the managed scene loops instead of
sampling it twice for the delta and again for the FPS counter.
@sonarqubecloud

Copy link
Copy Markdown

@alegemaate
alegemaate merged commit 9c99a30 into main Aug 17, 2026
3 checks passed
@alegemaate
alegemaate deleted the perf/scene-loop branch August 17, 2026 17:46
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