perf(ui): render frames on demand instead of continuously

While theme effects were on (the default) the main loop drew every
frame it could. Where vsync is off (offscreen, some drivers, remote
sessions) an idle wallet spun at ~3,000 fps, 88% of a core; with vsync
it still drew every refresh. Frames are now paced by what needs them:

- full rate while there is input, a held key, a window appearing or
  settling, a smooth scroll or the UI sweep;
- ambient (30 fps focused, 10 fps unfocused) for theme effects, the
  acrylic backdrop, avatar animations and notifications;
- progress (10 fps focused, 4 fps unfocused) for UI that changes
  without input. The updater and bootstrap downloads, console
  commands, lite-wallet operations, seed migration, the loading
  overlay and the lock-screen countdowns ask for it with
  material::RequestProgressFrame();
- otherwise idle, as before: wait for events, with a housekeeping wake
  every 2 s (5 s unfocused).

A software frame cap (0.9x the display refresh) bounds the loop where
vsync is off, and hidden, occluded or minimized windows get no ambient
frames. Reduce Motion freezes the theme-effect clock
(ThemeEffects::setMotionFrozen), so it needs no ambient frames either.

The displayed-balance roll goes through one clamped StepDisplayLerp
(balance_lerp.h). An idle-length frame pushed dt*speed past 1, so the
roll overshot, and under Reduce Motion's speed of 999 it diverged to
inf/NaN. The classic layout had its own copy of the old code.

Idle CPU, Lite build, offscreen (no vsync): 88% -> 5% of a core with
effects on, and 91% -> 3% with effects saved off (with the previous
commit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 19:52:09 -05:00
co-authored by Claude Opus 5.5
parent fffab228d1
commit 575abd4bcc
15 changed files with 362 additions and 74 deletions
+7 -12
View File
@@ -3,6 +3,7 @@
// Released under the GPLv3
#include "balance_components.h"
#include "balance_lerp.h"
#include "balance_tab_helpers.h"
#include "balance_address_list.h"
#include "balance_recent_tx.h"
@@ -46,18 +47,12 @@ void UpdateBalanceLerp(App* app) {
const auto& S = schema::UISchema::instance();
const float kBalanceLerpSpeed = S.drawElement("tabs.balance", "balance-lerp-speed").sizeOr(8.0f);
const auto& state = app->state();
float dt = ImGui::GetIO().DeltaTime;
float speed = (app->settings() && app->settings()->getReduceMotion()) ? 999.0f : kBalanceLerpSpeed;
auto lerp = [](double& disp, double target, float dt, float spd) {
double diff = target - disp;
if (std::abs(diff) < 1e-9) { disp = target; return; }
disp += diff * (double)(dt * spd);
if (std::abs(target - disp) < 1e-9) disp = target;
};
lerp(s_dispTotal, app->getTotalBalance(), dt, speed);
lerp(s_dispShielded, app->getShieldedBalance(), dt, speed);
lerp(s_dispTransparent, app->getTransparentBalance(), dt, speed);
lerp(s_dispUnconfirmed, state.unconfirmed_balance, dt, speed);
const float dt = ImGui::GetIO().DeltaTime;
const bool instant = app->settings() && app->settings()->getReduceMotion();
StepDisplayLerp(s_dispTotal, app->getTotalBalance(), dt, kBalanceLerpSpeed, instant);
StepDisplayLerp(s_dispShielded, app->getShieldedBalance(), dt, kBalanceLerpSpeed, instant);
StepDisplayLerp(s_dispTransparent, app->getTransparentBalance(), dt, kBalanceLerpSpeed, instant);
StepDisplayLerp(s_dispUnconfirmed, state.unconfirmed_balance, dt, kBalanceLerpSpeed, instant);
}
// Render compact hero line: logo + balance + USD + mining on one line