Skip to content

perf(sim): adaptive frame rate cap in main loop#110

Closed
subinita01 wants to merge 3 commits into
odudex:masterfrom
subinita01:feat/mnemonic-words-numbered-rows
Closed

perf(sim): adaptive frame rate cap in main loop#110
subinita01 wants to merge 3 commits into
odudex:masterfrom
subinita01:feat/mnemonic-words-numbered-rows

Conversation

@subinita01

@subinita01 subinita01 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

hiii @odudex ,
heres something that could be given a thought upon....please check

Summary

Replace the fixed 33ms (~30fps) frame-rate cap in the simulator main loop with an adaptive cap driven by a moving average of actual render time.

  • Measures lv_timer_handler() wall time each frame via SDL_GetTicks()

  • Maintains an 8-sample moving average to smooth transient spikes (e.g. from the background camera thread)

  • Selects a cap tier based on average render time:

    Avg render time Cap Target FPS
    < 15 ms 16 ms 60 fps
    15–24 ms 22 ms 45 fps
    ≥ 25 ms 33 ms 30 fps
  • 8-frame warmup period uses the fixed 33 ms cap until the window is populated

  • Still honours ms_til_next when LVGL needs to fire sooner than the cap

  • Floor of 16 ms ensures the main thread keeps pace with the camera thread (~33 ms frame delivery)

Risk: Low — change is isolated to the simulator main loop and has no effect on firmware behaviour. Worst case the adaptive logic reverts to the existing 30 fps cap.

Test plan

  • Clean rebase onto master — no conflicts
  • Format check passes: ./scripts/format.sh --check
  • All 229 tests pass: ./scripts/test.sh
  • Simulator builds cleanly: make -j$(nproc)
  • Simulator runs and displays correctly via WSLg on Windows 11

Replace the fixed 33ms (~30fps) delay with an 8-sample moving-average
of lv_timer_handler() render time that selects a 16ms (60fps), 22ms
(45fps), or 33ms (30fps) cap tier. An 8-frame warmup holds the fixed
cap until the window is populated. Fast machines now run at 60fps
instead of sleeping away the idle budget each frame.
@subinita01 subinita01 force-pushed the feat/mnemonic-words-numbered-rows branch from 7978be1 to 640cf33 Compare June 26, 2026 19:43
@odudex

odudex commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Can you give me a more "human" description of this PR. Did it solve a problem that you had? What was your motivation to do this change?

@subinita01

subinita01 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

@odudex ,
I was brainstorming some ideas I can implement and examining the simulator for changes .
It is likely that the simulator caps the LVGL delay to 33ms (~30fps) unconditionally -- main_sim.c: 275-280 . This wastes CPU cycles when the actual rendering is faster, and can cause stuttering when slower.

This PR makes the simulator's frame pacing adaptive rather than fixed, so it's more responsive on fast hardware without being wasteful on slow hardware.

It's not a critical fix. It won't change anything on the actual device. But when evaluating an UI PR from a simulator build , it is to judge the feature at 30fps when the device might deliver it at 45fps. That gap felt worth closing 🥲.

However I have noticed I need a bit more of fix into this , there are some bugs in the code i will fix it in some time. Its more on the performance optimisation end 😊😁.

The render_time measurement currently includes the SDL_Delay() call, so the moving average is measuring sleep time, not render time. main_sim.c:275-280

Shall I proceed with it or drop it?

…path

The warmup branch's SDL_Delay + continue skipped render_history updates,
so the first post-warmup avg_render was always 0 (all-zeros window),
forcing adaptive_cap to 16 regardless of actual render cost. The separate
warmup delay was also unnecessary since the moving average self-corrects
within 8 frames. Remove the branch; let every iteration update history
and use the single unified delay at the loop tail.
…tart bias

During warmup the history update was either skipped (original) or placed
after the warmup guard but before a continue that no longer existed, leaving
all 8 slots at zero. Move the history write before the warmup check so every
frame contributes a real sample; the adaptive cap is then immediately accurate
on the first post-warmup iteration instead of computing from a zero-padded
window.
@subinita01 subinita01 force-pushed the feat/mnemonic-words-numbered-rows branch from b991c2f to 15d489b Compare June 30, 2026 17:10
@odudex

odudex commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Back button on entropy capture commit is out of scope of this PR

@subinita01 subinita01 force-pushed the feat/mnemonic-words-numbered-rows branch from 15d489b to 2df70f9 Compare July 2, 2026 11:23
@odudex

odudex commented Jul 2, 2026

Copy link
Copy Markdown
Owner

If it doesn't fix a real problem you faced, and is just a theoretical rabdom problem raised by AI, unrelated to design, I'll close this for now, and you can make your other PR ready to review.
Contributors can define only one PR at a time to help everyone define real priorities.

@odudex odudex closed this Jul 2, 2026
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.

2 participants