Skip to content

perf(feeds): frame-budget governor for camera feeds#22

Merged
sepahead merged 1 commit into
mainfrom
perf/frame-budget-camera-feeds
Jun 29, 2026
Merged

perf(feeds): frame-budget governor for camera feeds#22
sepahead merged 1 commit into
mainfrom
perf/frame-budget-camera-feeds

Conversation

@sepahead

Copy link
Copy Markdown
Owner

What

Builds on the round-robin camera-feed scheduler (one camera render + pixel readback per tick) with an adaptive frame-budget governor.

The heavy part of a feed tick is a full scene render to the camera's render target plus a synchronous readRenderTargetPixels readback — the op that stalls the main render loop, worst with multi-million-splat scenes. The governor:

  • times that heavy work into an EMA (feedCostEmaRef);
  • computes stride = clamp(round(cost / FEED_FRAME_BUDGET_MS), 1, MAX_FEED_STRIDE);
  • runs the heavy path only every stride ticks, so feeds back off and yield the main thread/GPU under load.

Properties

  • No regression at low load — when cost ≤ budget, stride === 1 (every tick), identical to current behavior.
  • Smooth motion preserved — the cheap per-tick patrol updates still run every tick; only the heavy render+readback is throttled.
  • Bounded worst case — at MAX_FEED_STRIDE = 6 and the 83 ms interval, a feed still refreshes ~every 0.5 s.
  • Detection path stays adaptive — when feeds are hidden, the render-to-target cost is still folded into the EMA.

Why

Completes the lag-fix arc (splat performance mode → round-robin feeds → frame-budget governor): the feed subsystem now spends only a bounded slice per tick and gives time back to the render loop precisely when the scene is heavy.

Verification

  • bun run typecheck — clean
  • eslint — clean (only the pre-existing max-lines warning on this file)
  • bun run test:run — 230 passed, 8 skipped

Guarded going forward by scripts/perf-smoke.mjs (splatWithFeeds FPS floor).

Build on the round-robin feed scheduler (one camera render+readback per tick)
with an adaptive frame-budget governor. The heavy part of a feed tick — a full
scene render to the camera's target plus a synchronous pixel readback — is timed
into an EMA; when that cost exceeds FEED_FRAME_BUDGET_MS the heavy path runs only
every Nth tick (stride grows with cost, capped at MAX_FEED_STRIDE=6), so feeds
back off and yield the main thread/GPU to the render loop under load.

Cheap per-tick patrol updates still run every tick, so camera motion stays
smooth; at low load stride stays 1 (unchanged cadence). When feeds are hidden the
render-to-target cost is still folded into the EMA so the governor keeps adapting
for the detection path. Worst case at MAX_FEED_STRIDE still refreshes ~every
0.5 s. Closes the lag-fix arc started by the round-robin + splat perf-mode work.

typecheck + lint clean; 230 unit tests pass.
@sepahead sepahead merged commit 2673985 into main Jun 29, 2026
7 of 8 checks passed
@sepahead sepahead deleted the perf/frame-budget-camera-feeds branch June 29, 2026 10:15
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