diff options
author | 2019-01-24 18:42:10 -0800 | |
---|---|---|
committer | 2019-02-01 22:49:31 -0800 | |
commit | fb571ea6f81e911817e01daada55d0a6b538e8d7 (patch) | |
tree | 7b2fb752a7f79860d939206346173cdd6cf450d3 | |
parent | 8a0222e629b82dda35840aa74eeec55bcc16999d (diff) |
Add refresh rate stats to TimeStats.
Bug: 122905821
Test: libsurfaceflinger_test
Test: dumpsys SurfaceFlinger --timestats -dump --proto
Change-Id: I99b38497f054f86bebba0813134f1c1eaf632b47
17 files changed, 292 insertions, 40 deletions
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index bfe5da571f..026f7c7754 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -47,6 +47,8 @@ public: int configId; // Human readable name of the refresh rate. std::string name; + // Refresh rate in frames per second, rounded to the nearest integer. + uint32_t fps = 0; }; // TODO(b/122916473): Get this information from configs prepared by vendors, instead of @@ -63,7 +65,7 @@ private: void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { // This is the rate that HWC encapsulates right now when the device is in DOZE mode. mRefreshRates.push_back( - RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff"}); + RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); if (configs.size() < 1) { ALOGE("Device does not have valid configs. Config size is 0."); @@ -86,10 +88,11 @@ private: nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; - mRefreshRates.push_back(RefreshRate{RefreshRateType::DEFAULT, - configIdToVsyncPeriod[0].first, - base::StringPrintf("%2.ffps", fps)}); + mRefreshRates.push_back( + RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first, + base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); } + if (configs.size() < 2) { return; } @@ -99,9 +102,9 @@ private: vsyncPeriod = configIdToVsyncPeriod[1].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; - mRefreshRates.push_back(RefreshRate{RefreshRateType::PERFORMANCE, - configIdToVsyncPeriod[1].first, - base::StringPrintf("%2.ffps", fps)}); + mRefreshRates.push_back( + RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first, + base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); } } @@ -109,4 +112,4 @@ private: }; } // namespace scheduler -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 7e22232ee3..dcb2988e07 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -20,6 +20,7 @@ #include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/SchedulerUtils.h" +#include "TimeStats/TimeStats.h" #include "android-base/stringprintf.h" #include "utils/Timers.h" @@ -41,8 +42,10 @@ class RefreshRateStats { public: explicit RefreshRateStats( - const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, + const std::shared_ptr<TimeStats>& timeStats) : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)), + mTimeStats(timeStats), mPreviousRecordedTime(systemTime()) {} ~RefreshRateStats() = default; @@ -116,10 +119,16 @@ private: // this method was called. void flushTimeForMode(int mode) { nsecs_t currentTime = systemTime(); - int64_t timeElapsedMs = ns2ms(currentTime - mPreviousRecordedTime); + nsecs_t timeElapsed = currentTime - mPreviousRecordedTime; + int64_t timeElapsedMs = ns2ms(timeElapsed); mPreviousRecordedTime = currentTime; mConfigModesTotalTime[mode] += timeElapsedMs; + for (const auto& config : mRefreshRateConfigs->getRefreshRates()) { + if (config.configId == mode) { + mTimeStats->recordRefreshRate(config.fps, timeElapsed); + } + } } // Formats the time in milliseconds into easy to read format. @@ -136,6 +145,9 @@ private: // Keeps information about refresh rate configs that device has. std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; + // Aggregate refresh rate statistics for telemetry. + std::shared_ptr<TimeStats> mTimeStats; + int64_t mCurrentConfigMode = 0; int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF; @@ -145,4 +157,4 @@ private: }; } // namespace scheduler -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 2cf2cd8a5d..1626555785 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -740,8 +740,10 @@ void SurfaceFlinger::init() { mVsyncModulator.setPhaseOffsets(early, gl, late); setRefreshRateTo(90.f /* fps */); }); - mRefreshRateStats = std::make_unique<scheduler::RefreshRateStats>( - getHwComposer().getConfigs(*display->getId())); + mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs( + *display->getId()), + mTimeStats); } ALOGV("Done initializing"); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index a48e8113d3..9aaf6de096 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1000,7 +1000,7 @@ private: std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)}; SurfaceTracing mTracing; LayerStats mLayerStats; - std::unique_ptr<TimeStats> mTimeStats; + std::shared_ptr<TimeStats> mTimeStats; bool mUseHwcVirtualDisplays = false; std::atomic<uint32_t> mFrameMissedCount{0}; diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp index 773a5d1bf5..26d2c21848 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp @@ -128,8 +128,8 @@ sp<SurfaceFlinger> createSurfaceFlinger() { return new ColorLayer(args); } - std::unique_ptr<TimeStats> createTimeStats() override { - return std::make_unique<TimeStats>(); + std::shared_ptr<TimeStats> createTimeStats() override { + return std::make_shared<android::impl::TimeStats>(); } }; static Factory factory; diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index ac99e2a7e9..fc1d0f8b7f 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -93,7 +93,7 @@ public: virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0; virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0; - virtual std::unique_ptr<TimeStats> createTimeStats() = 0; + virtual std::shared_ptr<TimeStats> createTimeStats() = 0; protected: ~Factory() = default; diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index 8d3776bd4e..78c6e74150 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -32,6 +32,8 @@ namespace android { +namespace impl { + void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) { ATRACE_CALL(); @@ -450,6 +452,15 @@ void TimeStats::setPowerMode(int32_t powerMode) { mPowerTime.powerMode = powerMode; } +void TimeStats::recordRefreshRate(uint32_t fps, nsecs_t duration) { + std::lock_guard<std::mutex> lock(mMutex); + if (mTimeStats.refreshRateStats.count(fps)) { + mTimeStats.refreshRateStats[fps] += duration; + } else { + mTimeStats.refreshRateStats.insert({fps, duration}); + } +} + void TimeStats::flushAvailableGlobalRecordsToStatsLocked() { ATRACE_CALL(); @@ -547,6 +558,7 @@ void TimeStats::clear() { mTimeStats.clientCompositionFrames = 0; mTimeStats.displayOnTime = 0; mTimeStats.presentToPresent.hist.clear(); + mTimeStats.refreshRateStats.clear(); mPowerTime.prevTime = systemTime(); mGlobalRecord.prevPresentTime = 0; mGlobalRecord.presentFences.clear(); @@ -580,4 +592,6 @@ void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::strin } } +} // namespace impl + } // namespace android diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index e8fbcab9c0..d8c0786cd0 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -36,6 +36,40 @@ using namespace android::surfaceflinger; namespace android { class TimeStats { +public: + virtual ~TimeStats() = default; + + virtual void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) = 0; + virtual bool isEnabled() = 0; + + virtual void incrementTotalFrames() = 0; + virtual void incrementMissedFrames() = 0; + virtual void incrementClientCompositionFrames() = 0; + + virtual void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, + nsecs_t postTime) = 0; + virtual void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) = 0; + virtual void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) = 0; + virtual void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) = 0; + virtual void setAcquireFence(int32_t layerID, uint64_t frameNumber, + const std::shared_ptr<FenceTime>& acquireFence) = 0; + virtual void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) = 0; + virtual void setPresentFence(int32_t layerID, uint64_t frameNumber, + const std::shared_ptr<FenceTime>& presentFence) = 0; + // Clean up the layer record + virtual void onDestroy(int32_t layerID) = 0; + // If SF skips or rejects a buffer, remove the corresponding TimeRecord. + virtual void removeTimeRecord(int32_t layerID, uint64_t frameNumber) = 0; + + virtual void setPowerMode(int32_t powerMode) = 0; + // Source of truth is RefrehRateStats. + virtual void recordRefreshRate(uint32_t fps, nsecs_t duration) = 0; + virtual void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) = 0; +}; + +namespace impl { + +class TimeStats : public android::TimeStats { struct FrameTime { uint64_t frameNumber = 0; nsecs_t postTime = 0; @@ -75,32 +109,33 @@ class TimeStats { public: TimeStats() = default; - ~TimeStats() = default; - void parseArgs(bool asProto, const Vector<String16>& args, std::string& result); - bool isEnabled(); + void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override; + bool isEnabled() override; - void incrementTotalFrames(); - void incrementMissedFrames(); - void incrementClientCompositionFrames(); + void incrementTotalFrames() override; + void incrementMissedFrames() override; + void incrementClientCompositionFrames() override; void setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, - nsecs_t postTime); - void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime); - void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime); - void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime); + nsecs_t postTime) override; + void setLatchTime(int32_t layerID, uint64_t frameNumber, nsecs_t latchTime) override; + void setDesiredTime(int32_t layerID, uint64_t frameNumber, nsecs_t desiredTime) override; + void setAcquireTime(int32_t layerID, uint64_t frameNumber, nsecs_t acquireTime) override; void setAcquireFence(int32_t layerID, uint64_t frameNumber, - const std::shared_ptr<FenceTime>& acquireFence); - void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime); + const std::shared_ptr<FenceTime>& acquireFence) override; + void setPresentTime(int32_t layerID, uint64_t frameNumber, nsecs_t presentTime) override; void setPresentFence(int32_t layerID, uint64_t frameNumber, - const std::shared_ptr<FenceTime>& presentFence); + const std::shared_ptr<FenceTime>& presentFence) override; // Clean up the layer record - void onDestroy(int32_t layerID); + void onDestroy(int32_t layerID) override; // If SF skips or rejects a buffer, remove the corresponding TimeRecord. - void removeTimeRecord(int32_t layerID, uint64_t frameNumber); + void removeTimeRecord(int32_t layerID, uint64_t frameNumber) override; - void setPowerMode(int32_t powerMode); - void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence); + void setPowerMode(int32_t powerMode) override; + // Source of truth is RefrehRateStats. + void recordRefreshRate(uint32_t fps, nsecs_t duration) override; + void setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) override; // TODO(zzyiwei): Bound the timeStatsTracker with weighted LRU // static const size_t MAX_NUM_LAYER_RECORDS = 200; @@ -126,4 +161,6 @@ private: GlobalRecord mGlobalRecord; }; +} // namespace impl + } // namespace android diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp index 75ce4be8f2..16d2da0086 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp +++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp @@ -103,6 +103,11 @@ std::string TimeStatsHelper::TimeStatsGlobal::toString(std::optional<uint32_t> m StringAppendF(&result, "missedFrames = %d\n", missedFrames); StringAppendF(&result, "clientCompositionFrames = %d\n", clientCompositionFrames); StringAppendF(&result, "displayOnTime = %" PRId64 " ms\n", displayOnTime); + StringAppendF(&result, "displayConfigStats is as below:\n"); + for (const auto& [fps, duration] : refreshRateStats) { + StringAppendF(&result, "%dfps=%ldms ", fps, ns2ms(duration)); + } + result.back() = '\n'; StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime()); StringAppendF(&result, "presentToPresent histogram is as below:\n"); result.append(presentToPresent.toString()); @@ -141,6 +146,13 @@ SFTimeStatsGlobalProto TimeStatsHelper::TimeStatsGlobal::toProto( globalProto.set_missed_frames(missedFrames); globalProto.set_client_composition_frames(clientCompositionFrames); globalProto.set_display_on_time(displayOnTime); + for (const auto& ele : refreshRateStats) { + SFTimeStatsDisplayConfigBucketProto* configBucketProto = + globalProto.add_display_config_stats(); + SFTimeStatsDisplayConfigProto* configProto = configBucketProto->mutable_config(); + configProto->set_fps(ele.first); + configBucketProto->set_duration_millis(ns2ms(ele.second)); + } for (const auto& histEle : presentToPresent.hist) { SFTimeStatsHistogramBucketProto* histProto = globalProto.add_present_to_present(); histProto->set_time_millis(histEle.first); diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h index 5f40a1a4e2..f2ac7ff125 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h +++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h @@ -16,6 +16,7 @@ #pragma once #include <timestatsproto/TimeStatsProtoHeader.h> +#include <utils/Timers.h> #include <optional> #include <string> @@ -61,6 +62,7 @@ public: int64_t displayOnTime = 0; Histogram presentToPresent; std::unordered_map<std::string, TimeStatsLayer> stats; + std::unordered_map<uint32_t, nsecs_t> refreshRateStats; std::string toString(std::optional<uint32_t> maxLayers) const; SFTimeStatsGlobalProto toProto(std::optional<uint32_t> maxLayers) const; diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto index 377612a9ae..0dacbebbff 100644 --- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto +++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto @@ -25,7 +25,7 @@ option optimize_for = LITE_RUNTIME; // changes to these messages, and keep google3 side proto messages in sync if // the end to end pipeline needs to be updated. -// Next tag: 9 +// Next tag: 10 message SFTimeStatsGlobalProto { // The stats start time in UTC as seconds since January 1, 1970 optional int64 stats_start = 1; @@ -39,6 +39,8 @@ message SFTimeStatsGlobalProto { optional int32 client_composition_frames = 5; // Primary display on time in milliseconds. optional int64 display_on_time = 7; + // Stats per display configuration. + repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9; // Present to present histogram. repeated SFTimeStatsHistogramBucketProto present_to_present = 8; // Stats per layer. Apps could have multiple layers. @@ -80,3 +82,18 @@ message SFTimeStatsHistogramBucketProto { // Number of frames in the bucket. optional int32 frame_count = 2; } + +// Next tag: 3 +message SFTimeStatsDisplayConfigBucketProto { + // Metadata desribing a display config. + optional SFTimeStatsDisplayConfigProto config = 1; + // Duration in milliseconds for how long the display was in this + // configuration. + optional int64 duration_millis = 2; +} + +// Next tag: 2 +message SFTimeStatsDisplayConfigProto { + // Frames per second, rounded to the nearest integer. + optional int32 fps = 1; +} diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index b2bcb45945..b1d45f39b0 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -58,6 +58,7 @@ cc_test { "mock/MockMessageQueue.cpp", "mock/MockNativeWindowSurface.cpp", "mock/MockSurfaceInterceptor.cpp", + "mock/MockTimeStats.cpp", "mock/system/window/MockNativeWindow.cpp", ], static_libs: [ diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index 0384d9dd9a..3d887ea623 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -23,8 +23,10 @@ #include "Scheduler/RefreshRateStats.h" #include "mock/DisplayHardware/MockDisplay.h" +#include "mock/MockTimeStats.h" using namespace std::chrono_literals; +using testing::_; namespace android { namespace scheduler { @@ -42,6 +44,7 @@ protected: void init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs); std::unique_ptr<RefreshRateStats> mRefreshRateStats; + std::shared_ptr<android::mock::TimeStats> mTimeStats; }; RefreshRateStatsTest::RefreshRateStatsTest() { @@ -57,7 +60,8 @@ RefreshRateStatsTest::~RefreshRateStatsTest() { } void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) { - mRefreshRateStats = std::make_unique<RefreshRateStats>(configs); + mTimeStats = std::make_shared<android::mock::TimeStats>(); + mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats); } namespace { @@ -82,6 +86,9 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { init(configs); + EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(4); + EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(2); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); ASSERT_EQ(2, times.size()); ASSERT_EQ(0, times["ScreenOff"]); @@ -136,6 +143,10 @@ TEST_F(RefreshRateStatsTest, twoConfigsTest) { init(configs); + EXPECT_CALL(*mTimeStats, recordRefreshRate(0, _)).Times(6); + EXPECT_CALL(*mTimeStats, recordRefreshRate(60, _)).Times(4); + EXPECT_CALL(*mTimeStats, recordRefreshRate(90, _)).Times(4); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); ASSERT_EQ(3, times.size()); ASSERT_EQ(0, times["ScreenOff"]); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 3a62c40120..e639b4da62 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -144,9 +144,9 @@ public: return nullptr; } - std::unique_ptr<TimeStats> createTimeStats() override { + std::shared_ptr<TimeStats> createTimeStats() override { // TODO: Use test-fixture controlled factory - return std::make_unique<TimeStats>(); + return std::make_shared<android::impl::TimeStats>(); } using CreateBufferQueueFunction = diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 0f95cf910b..f35758debf 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <log/log.h> @@ -24,6 +25,7 @@ #include <utils/Vector.h> #include <random> +#include <unordered_set> #include "TimeStats/TimeStats.h" @@ -35,6 +37,10 @@ using namespace google::protobuf; namespace android { namespace { +using testing::Contains; +using testing::SizeIs; +using testing::UnorderedElementsAre; + // clang-format off #define FMT_PROTO true #define FMT_STRING false @@ -127,7 +133,7 @@ public: } std::mt19937 mRandomEngine = std::mt19937(std::random_device()()); - std::unique_ptr<TimeStats> mTimeStats = std::make_unique<TimeStats>(); + std::unique_ptr<TimeStats> mTimeStats = std::make_unique<impl::TimeStats>(); }; std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) { @@ -371,6 +377,63 @@ TEST_F(TimeStatsTest, canInsertUnorderedLayerTimeStats) { } } +TEST_F(TimeStatsTest, recordRefreshRateNewConfigs) { + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + uint32_t fpsOne = 30; + uint32_t fpsTwo = 90; + uint64_t millisOne = 5000; + uint64_t millisTwo = 7000; + + mTimeStats->recordRefreshRate(fpsOne, ms2ns(millisOne)); + mTimeStats->recordRefreshRate(fpsTwo, ms2ns(millisTwo)); + + SFTimeStatsGlobalProto globalProto; + ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); + + SFTimeStatsDisplayConfigBucketProto expectedBucketOne; + SFTimeStatsDisplayConfigProto* expectedConfigOne = expectedBucketOne.mutable_config(); + expectedConfigOne->set_fps(fpsOne); + expectedBucketOne.set_duration_millis(millisOne); + + SFTimeStatsDisplayConfigBucketProto expectedBucketTwo; + SFTimeStatsDisplayConfigProto* expectedConfigTwo = expectedBucketTwo.mutable_config(); + expectedConfigTwo->set_fps(fpsTwo); + expectedBucketTwo.set_duration_millis(millisTwo); + + EXPECT_THAT(globalProto.display_config_stats(), SizeIs(2)); + + std::unordered_set<uint32_t> seen_fps; + for (const auto& bucket : globalProto.display_config_stats()) { + seen_fps.emplace(bucket.config().fps()); + if (fpsOne == bucket.config().fps()) { + EXPECT_EQ(millisOne, bucket.duration_millis()); + } else if (fpsTwo == bucket.config().fps()) { + EXPECT_EQ(millisTwo, bucket.duration_millis()); + } else { + FAIL() << "Unknown fps: " << bucket.config().fps(); + } + } + EXPECT_THAT(seen_fps, UnorderedElementsAre(fpsOne, fpsTwo)); +} + +TEST_F(TimeStatsTest, recordRefreshRateUpdatesConfig) { + EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); + + uint32_t fps = 30; + uint64_t millisOne = 5000; + uint64_t millisTwo = 7000; + + mTimeStats->recordRefreshRate(fps, ms2ns(millisOne)); + mTimeStats->recordRefreshRate(fps, ms2ns(millisTwo)); + + SFTimeStatsGlobalProto globalProto; + ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); + EXPECT_THAT(globalProto.display_config_stats(), SizeIs(1)); + EXPECT_EQ(fps, globalProto.display_config_stats().Get(0).config().fps()); + EXPECT_EQ(millisOne + millisTwo, globalProto.display_config_stats().Get(0).duration_millis()); +} + TEST_F(TimeStatsTest, canRemoveTimeRecord) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); @@ -394,7 +457,7 @@ TEST_F(TimeStatsTest, canRecoverFromIncompleteTimeRecordError) { uint64_t frameNumber = 1; nsecs_t ts = 1000000; insertTimeRecord(INCOMPLETE_SEQUENCE, LAYER_ID_0, 1, 1000000); - for (size_t i = 0; i < TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) { + for (size_t i = 0; i < impl::TimeStats::MAX_NUM_TIME_RECORDS + 2; i++) { frameNumber++; ts += 1000000; insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, frameNumber, ts); diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp new file mode 100644 index 0000000000..d686939b3a --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mock/MockTimeStats.h" + +namespace android { +namespace mock { + +// Explicit default instantiation is recommended. +TimeStats::TimeStats() = default; +TimeStats::~TimeStats() = default; + +} // namespace mock +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h new file mode 100644 index 0000000000..08fdb9d137 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h @@ -0,0 +1,51 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "TimeStats/TimeStats.h" + +namespace android { +namespace mock { + +class TimeStats : public android::TimeStats { +public: + TimeStats(); + ~TimeStats() override; + + MOCK_METHOD3(parseArgs, void(bool, const Vector<String16>&, std::string&)); + MOCK_METHOD0(isEnabled, bool()); + MOCK_METHOD0(incrementTotalFrames, void()); + MOCK_METHOD0(incrementMissedFrames, void()); + MOCK_METHOD0(incrementClientCompositionFrames, void()); + MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t)); + MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t)); + MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t)); + MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t)); + MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); + MOCK_METHOD3(setPresentTime, void(int32_t, uint64_t, nsecs_t)); + MOCK_METHOD3(setPresentFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&)); + MOCK_METHOD1(onDestroy, void(int32_t)); + MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t)); + MOCK_METHOD1(setPowerMode, void(int32_t)); + MOCK_METHOD2(recordRefreshRate, void(uint32_t, nsecs_t)); + MOCK_METHOD1(setPresentFenceGlobal, void(const std::shared_ptr<FenceTime>&)); +}; + +} // namespace mock +} // namespace android |