[Feat] (Metrics, Config): make the boundary-counter summary cadence a knob, because the device harness never reaches the teardown dump

- MOBILEGL_PIPE_STATS_PERIOD (default 120, clamped to [1, 1000000]) sets the frames per
  summary line; Init() latches it and a zero falls back to the default
- the trace APK's replay never tears MobileGL down, so MOBILEGL_PIPE_STATS_FILE never
  fires on device and a fixture shorter than the period (create-indirect) reported nothing
- PipeStatsTest pins the latch and the zero fallback
This commit is contained in:
2026-09-05 21:21:54 -04:00
parent 901d48a678
commit 458ccde176
5 changed files with 38 additions and 6 deletions
+5
View File
@@ -347,6 +347,11 @@ namespace MobileGL::MG_Config {
// the server without shipping index bytes per draw. Over budget it degrades to
// per-draw staging, counted separately in the stats.
Uint32 PipeIndexMirrorMb = 64;
// MOBILEGL_PIPE_STATS_PERIOD: frames per boundary-counter summary line. 120 is the
// steady-state cadence; the device retrace harness never reaches the teardown dump
// and a trimmed fixture (create-indirect) is shorter than 120 frames, so a run that
// needs its numbers at all sets this low enough to land at least one window.
Uint32 PipeStatsPeriod = 120;
// MOBILEGL_PIPE_STATS_FILE: where the boundary counters' teardown JSON dump goes.
// Empty (the default) means no dump; the per-120-frame summary line still goes to
// the log whenever PipeStats is on, so a device run needs no writable path.
+1
View File
@@ -252,6 +252,7 @@ namespace MobileGL::MG_ConfigLoader {
QueryEnvQuirkOverride("MOBILEGL_PIPE_LEGACY_MEMOS") != MG_Config::QuirkOverride::ForceOff;
features.PipeTexelRetainMb = QueryEnvUint32("MOBILEGL_PIPE_TEXEL_RETAIN_MB", 0, 0, 4096);
features.PipeIndexMirrorMb = QueryEnvUint32("MOBILEGL_PIPE_INDEX_MIRROR_MB", 64, 0, 4096);
features.PipeStatsPeriod = QueryEnvUint32("MOBILEGL_PIPE_STATS_PERIOD", 120, 1, 1000000);
QueryEnvVariable("MOBILEGL_PIPE_STATS_FILE", features.PipeStatsFile, "");
}
+19 -3
View File
@@ -12,6 +12,7 @@
#include <gtest/gtest.h>
#include <Config.h>
#include <MG_Util/Metrics/PipeStats.h>
#include <cstdio>
@@ -77,6 +78,21 @@ namespace {
EXPECT_EQ(PS::FrameCount(), 1u);
}
TEST_F(PipeStatsTest, InitLatchesTheSummaryPeriodFromTheConfigAndNeverKeepsZero) {
// The device retrace harness never reaches the teardown dump, so the summary
// cadence is the only way a short fixture yields numbers at all: it must follow
// MOBILEGL_PIPE_STATS_PERIOD, and a zero must fall back rather than divide.
const Uint32 saved = MobileGL::MG_Config::Features.PipeStatsPeriod;
MobileGL::MG_Config::Features.PipeStatsPeriod = 7;
PS::Init();
EXPECT_EQ(PS::SummaryFramePeriod(), 7u);
MobileGL::MG_Config::Features.PipeStatsPeriod = 0;
PS::Init();
EXPECT_EQ(PS::SummaryFramePeriod(), PS::kDefaultSummaryFramePeriod);
MobileGL::MG_Config::Features.PipeStatsPeriod = saved;
PS::Init();
}
TEST_F(PipeStatsTest, GateHitsAndMissesAreSeparateCounters) {
for (Uint32 i = 0; i < 5; ++i) {
PS::CountGate(PS::Gate::MagmaPipelineMemo, /*hit=*/true);
@@ -262,10 +278,10 @@ namespace {
// A summary is emitted every kSummaryFramePeriod presents. The period is a constant the
// smoke check depends on, so a change to it has to break a test.
TEST_F(PipeStatsTest, SummaryPeriodIsOneHundredAndTwentyFrames) {
EXPECT_EQ(PS::kSummaryFramePeriod, 120u);
for (Uint64 i = 0; i < PS::kSummaryFramePeriod; ++i) {
EXPECT_EQ(PS::SummaryFramePeriod(), 120u);
for (Uint64 i = 0; i < PS::SummaryFramePeriod(); ++i) {
PS::OnPresent();
}
EXPECT_EQ(PS::FrameCount(), PS::kSummaryFramePeriod);
EXPECT_EQ(PS::FrameCount(), PS::SummaryFramePeriod());
}
} // namespace
+9 -2
View File
@@ -130,6 +130,8 @@ namespace MobileGL::MG_Util::PipeStats {
Uint64 g_windowBaseFrames = 0;
Bool g_shutdownDone = false;
// Frames per summary line, latched by Init() from MOBILEGL_PIPE_STATS_PERIOD.
Uint64 g_summaryPeriod = kDefaultSummaryFramePeriod;
inline void Bump(Counter& counter, Uint64 amount) {
counter.fetch_add(amount, std::memory_order_relaxed);
}
@@ -253,14 +255,19 @@ namespace MobileGL::MG_Util::PipeStats {
ResetCounters();
g_shutdownDone = false;
g_pipeStatsEnabled = MG_Config::Features.PipeStats;
g_summaryPeriod = MG_Config::Features.PipeStatsPeriod == 0
? kDefaultSummaryFramePeriod
: static_cast<Uint64>(MG_Config::Features.PipeStatsPeriod);
if (g_pipeStatsEnabled) {
MGLOG_I("MGPipe stats: counters ON (MOBILEGL_PIPE_STATS), summary every %llu frames%s%s",
static_cast<unsigned long long>(kSummaryFramePeriod),
static_cast<unsigned long long>(g_summaryPeriod),
MG_Config::Features.PipeStatsFile.empty() ? "" : ", JSON dump to ",
MG_Config::Features.PipeStatsFile.c_str());
}
}
Uint64 SummaryFramePeriod() { return g_summaryPeriod; }
void Shutdown() {
if (!g_pipeStatsEnabled || g_shutdownDone) {
return;
@@ -336,7 +343,7 @@ namespace MobileGL::MG_Util::PipeStats {
#endif
}
const Uint64 frames = g_frameCount.fetch_add(1, std::memory_order_relaxed) + 1;
if (frames % kSummaryFramePeriod == 0) {
if (frames % g_summaryPeriod == 0) {
EmitSummaryLine();
}
}
+4 -1
View File
@@ -129,7 +129,10 @@ namespace MobileGL::MG_Util::PipeStats {
inline constexpr Uint32 kPayloadHistogramBuckets = 24;
// Frames between two summary lines when MOBILEGL_PIPE_STATS=1.
inline constexpr Uint64 kSummaryFramePeriod = 120;
inline constexpr Uint64 kDefaultSummaryFramePeriod = 120;
// The period Init() latched from MOBILEGL_PIPE_STATS_PERIOD (kDefaultSummaryFramePeriod
// when unset); never 0.
Uint64 SummaryFramePeriod();
// The latch. Read directly by Enabled() so the off path is a global load and a
// predicted branch - do not turn this into a function call.