feat: add STEP-113 Android PBF fast gate diagnostics
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
@@ -1362,3 +1362,138 @@ TEST(RenderSegmentPlan, InvalidJSONClearsRendererPlanAndValidPlanRecovers) {
|
||||
ASSERT_TRUE(state.setRenderSegmentPlan(std::move(recovered)));
|
||||
EXPECT_TRUE(resolveRenderSegmentPlan(state.renderSegmentPlanConfig(), segmentLayerIds(), true).valid);
|
||||
}
|
||||
|
||||
TEST(FastGateTrace, EveryGateBitMatchesProductionDecisionOrder) {
|
||||
using Field = bool ParticleFastPathGateInput::*;
|
||||
const std::array<Field, 24> fields{{
|
||||
&ParticleFastPathGateInput::hasPreparedTree, &ParticleFastPathGateInput::retainedEnabled,
|
||||
&ParticleFastPathGateInput::cacheValid, &ParticleFastPathGateInput::revisionMatches,
|
||||
&ParticleFastPathGateInput::contentRevisionMatches, &ParticleFastPathGateInput::viewportMatches,
|
||||
&ParticleFastPathGateInput::cameraMatches, &ParticleFastPathGateInput::updateParametersMatch,
|
||||
&ParticleFastPathGateInput::styleLoaded, &ParticleFastPathGateInput::boundaryMatches,
|
||||
&ParticleFastPathGateInput::topologyMatches, &ParticleFastPathGateInput::staticStable,
|
||||
&ParticleFastPathGateInput::hasPlacementTransition, &ParticleFastPathGateInput::hasPendingChanges,
|
||||
&ParticleFastPathGateInput::hasNonParticleTransition, &ParticleFastPathGateInput::hasNonParticleRepaint,
|
||||
&ParticleFastPathGateInput::hasFadingTiles, &ParticleFastPathGateInput::particleNeedsRepaint,
|
||||
&ParticleFastPathGateInput::stillImageRequest, &ParticleFastPathGateInput::continuousMode,
|
||||
&ParticleFastPathGateInput::debugDisabled, &ParticleFastPathGateInput::unsupportedContinuousLayer,
|
||||
&ParticleFastPathGateInput::supportedScene, &ParticleFastPathGateInput::annotationMutation,
|
||||
}};
|
||||
EXPECT_EQ(particleFastGateFailureMask(fastPathInput()), 0u);
|
||||
auto allFail = fastPathInput();
|
||||
for (std::size_t i = 0; i < fields.size(); ++i) {
|
||||
auto input = fastPathInput();
|
||||
input.*fields[i] = !(input.*fields[i]);
|
||||
allFail.*fields[i] = input.*fields[i];
|
||||
EXPECT_EQ(particleFastGateFailureMask(input), uint32_t{1} << i);
|
||||
EXPECT_EQ(evaluateParticleFastPath(input).reason, particleFastGateReasons[i]);
|
||||
EXPECT_FALSE(evaluateParticleFastPath(input).candidate);
|
||||
}
|
||||
EXPECT_EQ(particleFastGateFailureMask(allFail), 0x00ffffffu);
|
||||
}
|
||||
|
||||
TEST(FastGateTrace, CacheMissRetainsLaterFailuresAndBoundedOrderedWindow) {
|
||||
RetainedUnderlayRuntimeState state;
|
||||
state.resetFastGateTrace();
|
||||
auto input = fastPathInput();
|
||||
input.cacheValid = false;
|
||||
input.unsupportedContinuousLayer = true;
|
||||
input.supportedScene = false;
|
||||
const auto decision = evaluateParticleFastPath(input);
|
||||
for (uint64_t i = 0; i < 137; ++i) {
|
||||
state.recordFastGateAttempt(i, static_cast<int64_t>(i * 33333333), input, decision, true);
|
||||
if (i == 0) state.completeFastGateAttempt("no-render-tree");
|
||||
else {
|
||||
state.recordFullCpuFrame();
|
||||
state.completeFastGateAttempt("full-rendered");
|
||||
}
|
||||
}
|
||||
EXPECT_FALSE(state.renderSegmentSnapshot().fastGateTrace); // No ring copy on internal render hot path.
|
||||
const auto snapshot = state.renderSegmentSnapshot(true);
|
||||
ASSERT_TRUE(snapshot.fastGateTrace);
|
||||
const auto& t = *snapshot.fastGateTrace;
|
||||
EXPECT_EQ(t.attempts, 137u);
|
||||
EXPECT_EQ(t.overwritten, 17u);
|
||||
EXPECT_EQ(t.decisionMismatch, 0u);
|
||||
EXPECT_EQ(t.fullRendered, 136u);
|
||||
EXPECT_EQ(t.noRender, 1u);
|
||||
ASSERT_EQ(t.frames.size(), 120u);
|
||||
EXPECT_EQ(t.frames.front().sequence, 18u);
|
||||
EXPECT_EQ(t.frames.back().sequence, 137u);
|
||||
EXPECT_EQ(t.frames.back().firstFailure, "cache-invalid");
|
||||
EXPECT_EQ(t.frames.back().failMask, (1u << 2) | (1u << 21) | (1u << 22));
|
||||
EXPECT_EQ(t.frames.back().pre.fullCpuFrame, 135u);
|
||||
EXPECT_EQ(t.frames.back().post.fullCpuFrame, 136u);
|
||||
EXPECT_EQ(t.failureCounts[2], 137u);
|
||||
EXPECT_EQ(t.failureCounts[21], 137u);
|
||||
EXPECT_EQ(t.failureCounts[22], 137u);
|
||||
EXPECT_EQ(t.firstFailureCounts[2], 137u);
|
||||
const auto json = renderSegmentDiagnosticsToJSON(snapshot);
|
||||
rapidjson::Document document;
|
||||
ASSERT_FALSE(document.Parse(json.c_str()).HasParseError());
|
||||
const auto& last = document["fastGateTrace"]["frames"][119];
|
||||
ASSERT_EQ(last["gateStatus"].Size(), 24u);
|
||||
EXPECT_STREQ(last["gateStatus"][2].GetString(), "FAIL");
|
||||
EXPECT_STREQ(last["gateStatus"][21].GetString(), "FAIL");
|
||||
EXPECT_STREQ(last["gateStatus"][22].GetString(), "FAIL");
|
||||
EXPECT_STREQ(last["pre"]["phase"].GetString(), "pre-fast");
|
||||
EXPECT_STREQ(last["post"]["phase"].GetString(), "post-render");
|
||||
state.resetFastGateTrace();
|
||||
const auto reset = state.renderSegmentSnapshot(true);
|
||||
ASSERT_TRUE(reset.fastGateTrace);
|
||||
EXPECT_EQ(reset.fastGateTrace->lifecycle, t.lifecycle + 1);
|
||||
EXPECT_TRUE(reset.fastGateTrace->frames.empty());
|
||||
EXPECT_EQ(reset.fastGateTrace->attempts, 0u);
|
||||
EXPECT_EQ(reset.fastGateTrace->failureCounts[2], 0u);
|
||||
}
|
||||
|
||||
TEST(FastGateTrace, PendingAttemptAndInconsistentShadowAreExplicit) {
|
||||
RetainedUnderlayRuntimeState state;
|
||||
auto input = fastPathInput();
|
||||
state.recordFastGateAttempt(8, 123, input, {false, ParticleFastPathMissReason::CacheInvalid}, false);
|
||||
auto snapshot = state.renderSegmentSnapshot(true);
|
||||
ASSERT_TRUE(snapshot.fastGateTrace);
|
||||
ASSERT_EQ(snapshot.fastGateTrace->frames.size(), 1u);
|
||||
EXPECT_EQ(snapshot.fastGateTrace->decisionMismatch, 1u);
|
||||
EXPECT_FALSE(snapshot.fastGateTrace->frames[0].completed);
|
||||
EXPECT_FALSE(snapshot.fastGateTrace->frames[0].placementTransitionsEnabled);
|
||||
const auto json = renderSegmentDiagnosticsToJSON(snapshot);
|
||||
rapidjson::Document document;
|
||||
ASSERT_FALSE(document.Parse(json.c_str()).HasParseError());
|
||||
EXPECT_TRUE(document["fastGateTrace"]["frames"][rapidjson::SizeType{0}]["post"].IsNull());
|
||||
state.completeFastGateAttempt("fast-rendered");
|
||||
state.completeFastGateAttempt("fast-rendered"); // Completion must be idempotent.
|
||||
snapshot = state.renderSegmentSnapshot(true);
|
||||
EXPECT_EQ(snapshot.fastGateTrace->fastRendered, 1u);
|
||||
}
|
||||
|
||||
TEST(FastGateTrace, RetainedSamplesHaveDistinctPhasesAndExplicitLimit) {
|
||||
RetainedUnderlayRuntimeState state;
|
||||
RenderSegmentPlan configured;
|
||||
std::vector<std::string> ids;
|
||||
std::vector<StyleLayerDiagnostics> layers;
|
||||
for (int32_t i = 0; i < 18; ++i) {
|
||||
ids.push_back("r-" + std::to_string(i));
|
||||
configured.segments.push_back({RenderSegmentStrategy::Retained,
|
||||
i ? std::optional<std::string>(ids.back()) : std::nullopt});
|
||||
layers.push_back({i, ids.back(), "background", true, std::nullopt, std::nullopt});
|
||||
}
|
||||
ASSERT_TRUE(state.setRenderSegmentPlan(configured));
|
||||
const auto resolved = resolveRenderSegmentPlan(state.renderSegmentPlanConfig(), ids, true);
|
||||
ASSERT_TRUE(resolved.valid);
|
||||
state.recordRenderSegmentResolution(resolved, layers);
|
||||
const auto input = fastPathInput();
|
||||
state.recordFastGateAttempt(0, 0, input, evaluateParticleFastPath(input), true);
|
||||
state.recordSegmentBuild(0);
|
||||
state.recordSegmentExecution(0, true, true, true, 7, "build", nullptr);
|
||||
state.completeFastGateAttempt("full-rendered");
|
||||
const auto snapshot = state.renderSegmentSnapshot(true);
|
||||
const auto& frame = snapshot.fastGateTrace->frames.front();
|
||||
ASSERT_EQ(frame.pre.retained.size(), 16u);
|
||||
ASSERT_EQ(frame.post.retained.size(), 16u);
|
||||
EXPECT_TRUE(frame.pre.retainedSamplesTruncated);
|
||||
EXPECT_TRUE(frame.post.retainedSamplesTruncated);
|
||||
EXPECT_EQ(frame.pre.retained[0].cache.build, 0u);
|
||||
EXPECT_EQ(frame.post.retained[0].cache.build, 1u);
|
||||
EXPECT_EQ(frame.post.retained[0].cache.sourceRevision, 7u);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user