Loading services/surfaceflinger/TimeStats/TimeStats.cpp +14 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <algorithm> #include <chrono> #include <cmath> #include <unordered_map> #include "TimeStats.h" Loading Loading @@ -180,6 +181,12 @@ bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) { *atom->mutable_present_to_present() = histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets); } const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta"); if (present2PresentDeltaHist != layer->deltas.cend()) { *atom->mutable_present_to_present_delta() = histogramToProto(present2PresentDeltaHist->second.hist, mMaxPulledHistogramBuckets); } const auto& post2presentHist = layer->deltas.find("post2present"); if (post2presentHist != layer->deltas.cend()) { *atom->mutable_post_to_present() = Loading Loading @@ -452,6 +459,7 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR LayerRecord& layerRecord = mTimeStatsTracker[layerId]; TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs; std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords; const int32_t refreshRateBucket = clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH); Loading Loading @@ -529,6 +537,12 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId, timeRecords[0].frameTime.frameNumber, presentToPresentMs); timeStatsLayer.deltas["present2present"].insert(presentToPresentMs); if (prevPresentToPresentMs) { const int32_t presentToPresentDeltaMs = std::abs(presentToPresentMs - *prevPresentToPresentMs); timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs); } prevPresentToPresentMs = presentToPresentMs; } prevTimeRecord = timeRecords[0]; timeRecords.pop_front(); Loading services/surfaceflinger/TimeStats/TimeStats.h +1 −0 Original line number Diff line number Diff line Loading @@ -219,6 +219,7 @@ class TimeStats : public android::TimeStats { uint32_t lateAcquireFrames = 0; uint32_t badDesiredPresentFrames = 0; TimeRecord prevTimeRecord; std::optional<int32_t> prevPresentToPresentMs; std::deque<TimeRecord> timeRecords; }; Loading services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto +5 −1 Original line number Diff line number Diff line Loading @@ -289,7 +289,11 @@ message SurfaceflingerStatsLayerInfo { // Introduced in Android 12. optional FrameTimingHistogram app_deadline_misses = 25; // Next ID: 27 // Variability histogram of present_to_present timings. // Introduced in Android 14. optional FrameTimingHistogram present_to_present_delta = 27; // Next ID: 28 } /** Loading services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +45 −2 Original line number Diff line number Diff line Loading @@ -44,11 +44,14 @@ namespace android { namespace { using testing::_; using testing::AllOf; using testing::AnyNumber; using testing::Contains; using testing::ElementsAre; using testing::HasSubstr; using testing::InSequence; using testing::Not; using testing::Property; using testing::SizeIs; using testing::StrEq; using testing::UnorderedElementsAre; Loading Loading @@ -645,7 +648,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); ASSERT_EQ(1, globalProto.stats_size()); const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0); const SFTimeStatsLayerProto& layerProto = globalProto.stats(0); ASSERT_TRUE(layerProto.has_layer_name()); EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name()); ASSERT_TRUE(layerProto.has_total_frames()); Loading @@ -653,7 +656,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { ASSERT_EQ(6, layerProto.deltas_size()); for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) { ASSERT_EQ(1, deltaProto.histograms_size()); const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0); const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0); EXPECT_EQ(1, histogramProto.frame_count()); if ("post2acquire" == deltaProto.delta_name()) { EXPECT_EQ(1, histogramProto.time_millis()); Loading @@ -673,6 +676,46 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { } } using LayerProto = SFTimeStatsLayerProto; using DeltaProto = SFTimeStatsDeltaProto; using BucketProto = SFTimeStatsHistogramBucketProto; TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta // Slightly unstable frames insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta SFTimeStatsGlobalProto globalProto; ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); EXPECT_THAT(globalProto.stats(), ElementsAre(AllOf( Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)), Property(&LayerProto::total_frames, 4), Property(&LayerProto::deltas, Contains(AllOf(Property(&DeltaProto::delta_name, "present2presentDelta"), Property(&DeltaProto::histograms, UnorderedElementsAre( AllOf(Property(&BucketProto:: time_millis, 0), Property(&BucketProto:: frame_count, 1)), AllOf(Property(&BucketProto:: time_millis, 1), Property(&BucketProto:: frame_count, 2)))))))))); } TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); Loading Loading
services/surfaceflinger/TimeStats/TimeStats.cpp +14 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <algorithm> #include <chrono> #include <cmath> #include <unordered_map> #include "TimeStats.h" Loading Loading @@ -180,6 +181,12 @@ bool TimeStats::populateLayerAtom(std::vector<uint8_t>* pulledData) { *atom->mutable_present_to_present() = histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets); } const auto& present2PresentDeltaHist = layer->deltas.find("present2presentDelta"); if (present2PresentDeltaHist != layer->deltas.cend()) { *atom->mutable_present_to_present_delta() = histogramToProto(present2PresentDeltaHist->second.hist, mMaxPulledHistogramBuckets); } const auto& post2presentHist = layer->deltas.find("post2present"); if (post2presentHist != layer->deltas.cend()) { *atom->mutable_post_to_present() = Loading Loading @@ -452,6 +459,7 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR LayerRecord& layerRecord = mTimeStatsTracker[layerId]; TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord; std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs; std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords; const int32_t refreshRateBucket = clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH); Loading Loading @@ -529,6 +537,12 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId, timeRecords[0].frameTime.frameNumber, presentToPresentMs); timeStatsLayer.deltas["present2present"].insert(presentToPresentMs); if (prevPresentToPresentMs) { const int32_t presentToPresentDeltaMs = std::abs(presentToPresentMs - *prevPresentToPresentMs); timeStatsLayer.deltas["present2presentDelta"].insert(presentToPresentDeltaMs); } prevPresentToPresentMs = presentToPresentMs; } prevTimeRecord = timeRecords[0]; timeRecords.pop_front(); Loading
services/surfaceflinger/TimeStats/TimeStats.h +1 −0 Original line number Diff line number Diff line Loading @@ -219,6 +219,7 @@ class TimeStats : public android::TimeStats { uint32_t lateAcquireFrames = 0; uint32_t badDesiredPresentFrames = 0; TimeRecord prevTimeRecord; std::optional<int32_t> prevPresentToPresentMs; std::deque<TimeRecord> timeRecords; }; Loading
services/surfaceflinger/TimeStats/timestatsatomsproto/timestats_atoms.proto +5 −1 Original line number Diff line number Diff line Loading @@ -289,7 +289,11 @@ message SurfaceflingerStatsLayerInfo { // Introduced in Android 12. optional FrameTimingHistogram app_deadline_misses = 25; // Next ID: 27 // Variability histogram of present_to_present timings. // Introduced in Android 14. optional FrameTimingHistogram present_to_present_delta = 27; // Next ID: 28 } /** Loading
services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +45 −2 Original line number Diff line number Diff line Loading @@ -44,11 +44,14 @@ namespace android { namespace { using testing::_; using testing::AllOf; using testing::AnyNumber; using testing::Contains; using testing::ElementsAre; using testing::HasSubstr; using testing::InSequence; using testing::Not; using testing::Property; using testing::SizeIs; using testing::StrEq; using testing::UnorderedElementsAre; Loading Loading @@ -645,7 +648,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); ASSERT_EQ(1, globalProto.stats_size()); const SFTimeStatsLayerProto& layerProto = globalProto.stats().Get(0); const SFTimeStatsLayerProto& layerProto = globalProto.stats(0); ASSERT_TRUE(layerProto.has_layer_name()); EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name()); ASSERT_TRUE(layerProto.has_total_frames()); Loading @@ -653,7 +656,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { ASSERT_EQ(6, layerProto.deltas_size()); for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) { ASSERT_EQ(1, deltaProto.histograms_size()); const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms().Get(0); const SFTimeStatsHistogramBucketProto& histogramProto = deltaProto.histograms(0); EXPECT_EQ(1, histogramProto.frame_count()); if ("post2acquire" == deltaProto.delta_name()) { EXPECT_EQ(1, histogramProto.time_millis()); Loading @@ -673,6 +676,46 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) { } } using LayerProto = SFTimeStatsLayerProto; using DeltaProto = SFTimeStatsDeltaProto; using BucketProto = SFTimeStatsHistogramBucketProto; TEST_F(TimeStatsTest, canComputeLayerStabilityHistogram) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000); insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000); // 0ms delta // Slightly unstable frames insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000); // 1ms delta insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 6000000); // 1ms delta SFTimeStatsGlobalProto globalProto; ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); EXPECT_THAT(globalProto.stats(), ElementsAre(AllOf( Property(&LayerProto::layer_name, genLayerName(LAYER_ID_0)), Property(&LayerProto::total_frames, 4), Property(&LayerProto::deltas, Contains(AllOf(Property(&DeltaProto::delta_name, "present2presentDelta"), Property(&DeltaProto::histograms, UnorderedElementsAre( AllOf(Property(&BucketProto:: time_millis, 0), Property(&BucketProto:: frame_count, 1)), AllOf(Property(&BucketProto:: time_millis, 1), Property(&BucketProto:: frame_count, 2)))))))))); } TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); Loading