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 Original line Diff line number Diff line
@@ -27,6 +27,7 @@


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


#include "TimeStats.h"
#include "TimeStats.h"
@@ -177,6 +178,12 @@ bool TimeStats::populateLayerAtom(std::string* pulledData) {
            *atom->mutable_present_to_present() =
            *atom->mutable_present_to_present() =
                    histogramToProto(present2PresentHist->second.hist, mMaxPulledHistogramBuckets);
                    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");
        const auto& post2presentHist = layer->deltas.find("post2present");
        if (post2presentHist != layer->deltas.cend()) {
        if (post2presentHist != layer->deltas.cend()) {
            *atom->mutable_post_to_present() =
            *atom->mutable_post_to_present() =
@@ -448,6 +455,7 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR


    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
    LayerRecord& layerRecord = mTimeStatsTracker[layerId];
    TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
    TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
    std::optional<int32_t>& prevPresentToPresentMs = layerRecord.prevPresentToPresentMs;
    std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
    std::deque<TimeRecord>& timeRecords = layerRecord.timeRecords;
    const int32_t refreshRateBucket =
    const int32_t refreshRateBucket =
            clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
            clampToNearestBucket(displayRefreshRate, REFRESH_RATE_BUCKET_WIDTH);
@@ -525,6 +533,12 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayR
            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
            ALOGV("[%d]-[%" PRIu64 "]-present2present[%d]", layerId,
                  timeRecords[0].frameTime.frameNumber, presentToPresentMs);
                  timeRecords[0].frameTime.frameNumber, presentToPresentMs);
            timeStatsLayer.deltas["present2present"].insert(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];
        prevTimeRecord = timeRecords[0];
        timeRecords.pop_front();
        timeRecords.pop_front();
+1 −0
Original line number Original line Diff line number Diff line
@@ -219,6 +219,7 @@ class TimeStats : public android::TimeStats {
        uint32_t lateAcquireFrames = 0;
        uint32_t lateAcquireFrames = 0;
        uint32_t badDesiredPresentFrames = 0;
        uint32_t badDesiredPresentFrames = 0;
        TimeRecord prevTimeRecord;
        TimeRecord prevTimeRecord;
        std::optional<int32_t> prevPresentToPresentMs;
        std::deque<TimeRecord> timeRecords;
        std::deque<TimeRecord> timeRecords;
    };
    };


+5 −1
Original line number Original line Diff line number Diff line
@@ -288,7 +288,11 @@ message SurfaceflingerStatsLayerInfo {
    // Introduced in Android 12.
    // Introduced in Android 12.
    optional FrameTimingHistogram app_deadline_misses = 25;
    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 Original line Diff line number Diff line
@@ -44,11 +44,14 @@ namespace android {
namespace {
namespace {


using testing::_;
using testing::_;
using testing::AllOf;
using testing::AnyNumber;
using testing::AnyNumber;
using testing::Contains;
using testing::Contains;
using testing::ElementsAre;
using testing::HasSubstr;
using testing::HasSubstr;
using testing::InSequence;
using testing::InSequence;
using testing::Not;
using testing::Not;
using testing::Property;
using testing::SizeIs;
using testing::SizeIs;
using testing::StrEq;
using testing::StrEq;
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAre;
@@ -645,7 +648,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
    ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));


    ASSERT_EQ(1, globalProto.stats_size());
    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());
    ASSERT_TRUE(layerProto.has_layer_name());
    EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
    EXPECT_EQ(genLayerName(LAYER_ID_0), layerProto.layer_name());
    ASSERT_TRUE(layerProto.has_total_frames());
    ASSERT_TRUE(layerProto.has_total_frames());
@@ -653,7 +656,7 @@ TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
    ASSERT_EQ(6, layerProto.deltas_size());
    ASSERT_EQ(6, layerProto.deltas_size());
    for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
    for (const SFTimeStatsDeltaProto& deltaProto : layerProto.deltas()) {
        ASSERT_EQ(1, deltaProto.histograms_size());
        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());
        EXPECT_EQ(1, histogramProto.frame_count());
        if ("post2acquire" == deltaProto.delta_name()) {
        if ("post2acquire" == deltaProto.delta_name()) {
            EXPECT_EQ(1, histogramProto.time_millis());
            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) {
TEST_F(TimeStatsTest, canNotInsertInvalidLayerNameTimeStats) {
    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
    EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());