fix(mining): keep the thread benchmark running while the tab is hidden

The thread benchmark state machine was advanced from the Mining tab's
render path, so it only progressed while that tab was visible and the
window was on screen. Hiding the tab or minimizing the window froze the
benchmark mid-sweep.

- The benchmark now advances from the main update loop, ungated by
  connectivity, so it keeps running when the tab is hidden or the window
  is minimized.
- Its clock is driven from a steady wall-clock reading rather than the
  render delta, and each step is clamped like the other logic timers so
  a suspend/resume cannot leap a benchmark phase.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 16:00:10 -05:00
co-authored by Claude Opus 4.8
parent fae5ef05b9
commit 32ea4d0524
5 changed files with 41 additions and 19 deletions
+20 -19
View File
@@ -29,6 +29,7 @@
#include "imgui.h"
#include "imgui_internal.h"
#include <chrono>
#include <thread>
#include <algorithm>
#include <cmath>
@@ -67,6 +68,23 @@ void CancelMiningBenchmark(App* app, bool restoreMiner) {
app->startPoolMining(plan.restartThreads);
}
void TickMiningBenchmark(App* app, double hashrate10s) {
static double s_lastTickSec = -1.0;
if (!s_benchmark.active()) { s_lastTickSec = -1.0; return; } // next run starts its clock from 0
const double nowSec = std::chrono::duration<double>(std::chrono::steady_clock::now().time_since_epoch()).count();
// Capped like the logic timers (App::kMaxLogicDeltaSeconds): a suspend/resume must not leap a phase.
const float dt = AdvanceBenchmarkClock(s_lastTickSec, nowSec, App::kMaxLogicDeltaSeconds);
auto benchmarkUpdate = AdvanceThreadBenchmark(s_benchmark, dt, hashrate10s);
if (benchmarkUpdate.stopPoolMining) app->stopPoolMining();
if (benchmarkUpdate.saveOptimalThreads) {
s_selected_threads = benchmarkUpdate.optimalThreads;
app->settings()->setPoolThreads(s_selected_threads);
app->settings()->save();
}
if (benchmarkUpdate.startPoolMining) app->startPoolMining(benchmarkUpdate.startThreads);
if (benchmarkUpdate.inconclusive) Notifications::instance().warning(TR("grpc_benchmark_inconclusive"));
}
// Miner-update version check (one shot per session): fetches the latest DRG-XMRig release tag in
// the background so the "Update" button can show it. Network call to the project Gitea, started
// the first time the pool section is shown.
@@ -249,25 +267,8 @@ static void RenderMiningTabContent(App* app)
// Thread Benchmark state machine — runs pool mining at each candidate
// thread count to find the optimal setting for this CPU.
// ================================================================
if (s_benchmark.active()) {
auto benchmarkUpdate = AdvanceThreadBenchmark(
s_benchmark, app->logicDeltaTime(), state.pool_mining.hashrate_10s);
if (benchmarkUpdate.stopPoolMining) {
app->stopPoolMining();
}
if (benchmarkUpdate.saveOptimalThreads) {
s_selected_threads = benchmarkUpdate.optimalThreads;
app->settings()->setPoolThreads(s_selected_threads);
app->settings()->save();
}
if (benchmarkUpdate.startPoolMining) {
app->startPoolMining(benchmarkUpdate.startThreads);
}
if (benchmarkUpdate.inconclusive) {
Notifications::instance().warning(
TR("grpc_benchmark_inconclusive"));
}
}
// The benchmark state machine is advanced from App::update() via TickMiningBenchmark() so it keeps running
// while this tab is hidden or the window is minimized (it used to freeze there).
// ================================================================
// Proportional section budget — ensures all content fits without