Skip to content

FUG-82: autoscale effect framerate (5-fps ladder + abort + app override) - #55

Open
issuefleet[bot] wants to merge 1 commit into
mainfrom
agent/fug-82-autoscale-framerate
Open

FUG-82: autoscale effect framerate (5-fps ladder + abort + app override)#55
issuefleet[bot] wants to merge 1 commit into
mainfrom
agent/fug-82-autoscale-framerate

Conversation

@issuefleet

@issuefleet issuefleet Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

FUG-82: Autoscale framerate

Effects rendered at a fixed ~30 fps regardless of shader cost, so a heavy effect starved the WiFi/BLE tasks and stuttered while a cheap one wasted headroom. This closes the loop: the render loop adapts each effect's target FPS to its real per-frame cost on the device, and aborts an effect it genuinely can't run.

Behavior (matches the issue)

  • 5-fps ladder, 25–80 fps. Discrete rungs — legible to the app, damps oscillation.
  • ≥5% headroom. A frame misses when its cost (update + shade + show) exceeds 95% of the target period.
  • x/N window → drop. Over a 30-frame window, if more than a quarter miss, drop one rung. A clean window whose worst frame still fits the next rung steps up.
  • Abort at the floor. If 25 fps (or a user-pinned rate) still can't hold, the effect is parked (freeing the CPU) and the app is notified.
  • App override. set_fps{target_fps} pins an exact rate (snapped to the ladder); 0 returns to autoscale. A pinned rate the device can't hit aborts + notifies rather than silently dropping.
  • Consistent FPS. The render task sleeps period − work, so frame starts land one period apart.

What's here

  • //firmware/fps — a pure, no_std, integer-only FpsController with the ladder/headroom/window/abort/pin logic and 12 host unit tests.
  • Render loop (main.cpp / ffi.rs) — measures every effect frame's cost (ungated from the perf tier), drives the controller, parks-on-abort, pushes fps_state{aborted} from the network loop. Overrun budget now tracks the live target (cpu_hz / current_fps).
  • ProtocolSetFps (client arm 32) + FpsState (server arm 19); PerfReport.current_fps.
  • Appclient.setFps / onFpsState, a Framerate control (Auto / 25–80) with a live readout in the perf panel, and an abort toast.
  • Docs — a "Framerate autoscaling" section in docs/design/perf-monitoring.md.

Testing (green locally on the rebased tree)

  • firmware//firmware/fps, //firmware/player_app:ffi_test (set_fps → fps_state, snap-to-rung, pinned-abort over the C ABI), the exhaustive-arm phone_client_frames_test / session_test, plus fx_vm/arena/pulse/store; esp32c6 builds -c opt.
  • protocol — rust conformance, python roundtrip, TS typecheck.
  • web — full unit suite (60) + web_ts_typecheck_test; proto_test round-trips the new arms.

Rebase notes

Rebased onto latest main. Two rounds of upstream integration:

Notes

  • The abort push shares the perf report's ws:81-only limitation (a phone on wss learns of it by polling — the parked effect shows as "off" in playback_state).
  • The checked-in @ledmapper/protocol flat types don't carry these arms yet (same as the perf/brightness arms — web-local in net/proto.ts).
  • Autoscaling applies to the .fxb shader effects (the heavy, variable-cost path); the cheap fixed built-in patterns are unchanged.

🤖 Generated with Claude Code

Closes-Linear: FUG-82 (https://linear.app/fughilli/issue/FUG-82/autoscale-framerate)

@linear-code

linear-code Bot commented Aug 6, 2026

Copy link
Copy Markdown
FUG-82 Autoscale framerate

Scale framerate to achieve consistent FPS in 5 FPS steps between 80 and 25 FPS. If 25 FPS is not achievable, abort the current effect (to prevent starving the other threads). There should be at least 5% headroom at any given FPS setting, and if more than x/N frames in a given interval are missed, the FPS target should be dropped.

Also allow overriding the FPS target from the app; if the user-set FPS target is not achievable, abort the effect (and notify the user).

Review in Linear

@issuefleet
issuefleet Bot force-pushed the agent/fug-82-autoscale-framerate branch from edd70e8 to dca7273 Compare August 9, 2026 02:30
@issuefleet
issuefleet Bot force-pushed the agent/fug-82-autoscale-framerate branch from dca7273 to 99345e0 Compare August 9, 2026 02:48
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://fughilli.github.io/splanc/pr-preview/pr-55/

Built to branch gh-pages at 2026-08-15 21:45 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@issuefleet
issuefleet Bot force-pushed the agent/fug-82-autoscale-framerate branch from 99345e0 to 931ca7f Compare August 14, 2026 22:22
… override

Effects rendered at a fixed ~30 fps regardless of how expensive the shader was,
so a heavy effect starved the WiFi/BLE tasks and stuttered while a cheap one
wasted headroom. Close the loop: adapt each effect's target FPS to its measured
per-frame cost on this device.

- New no_std crate //firmware/fps: a pure `FpsController` (thoroughly
  host-unit-tested, integer-only) — a 25..80 fps ladder in 5-fps steps, a "5%
  headroom" miss rule, an x/N missed-frame window that drops a rung (or steps up
  when a clean window's worst frame still fits the next rung), a user-pin
  override, and an abort decision when even the floor / pinned rate can't hold.
- Render loop (main.cpp/ffi.rs): measure every effect frame's cost (update +
  shade + show; ungated from the perf tier), feed the controller, and sleep
  period-minus-work so the frame INTERVAL tracks the target (a *consistent*
  FPS). On an unachievable target the effect is parked (freeing the CPU) and the
  network loop pushes an fps_state{aborted} to notify the app. The perf-ring
  overrun budget now tracks the live target (cpu_hz / current_fps).
- Protocol: SetFps{target_fps} (0 = auto) + FpsState{target,current,min,max,
  auto,aborted}; PerfReport gains current_fps.
- Web: client.setFps/onFpsState, a Framerate control (Auto / 25..80) in the perf
  panel with a live readout, and an abort toast.
- Docs: perf-monitoring.md gains a "Framerate autoscaling" section.

Tests: firmware/fps unit tests (ladder/headroom/window/abort/pin/consistent
delay); host FFI test exercises set_fps -> fps_state + the pinned-abort path;
web proto round-trip for the new arms. esp32c6 firmware + all web/rust/python
protocol tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@issuefleet
issuefleet Bot force-pushed the agent/fug-82-autoscale-framerate branch from 931ca7f to 900912c Compare August 15, 2026 21:34
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.

0 participants