Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fe1451f authored by Alberto Gonzalez's avatar Alberto Gonzalez
Browse files

Record rendering stability histogram.

The new present2presentDelta histogram records the stability
in present2present timings between consecutive frames.

Bug: 239083860
Test: atest libsurfaceflinger_unittest
Change-Id: I9559daaab831c85ecb2aab0ef8ba0fdeb84bb345
parent 6dc42386
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include <algorithm>
#include <chrono>
#include <cmath>
#include <unordered_map>

#include "TimeStats.h"
@@ -177,6 +178,12 @@ bool TimeStats::populateLayerAtom(std::string* 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() =
@@ -448,6 +455,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);
@@ -525,6 +533,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();
+1 −0
Original line number Diff line number Diff line
@@ -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;
    };

+5 −1
Original line number Diff line number Diff line
@@ -288,7 +288,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
}

/**
+45 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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());
@@ -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());
@@ -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());