diff options
-rw-r--r-- | libhistogram/Android.mk | 19 | ||||
-rw-r--r-- | libhistogram/histogram_collector.cpp | 152 | ||||
-rw-r--r-- | libhistogram/histogram_collector.h | 6 | ||||
-rw-r--r-- | libhistogram/ringbuffer.cpp | 110 | ||||
-rw-r--r-- | libhistogram/ringbuffer.h | 83 | ||||
-rw-r--r-- | libhistogram/ringbuffer_test.cpp | 318 | ||||
-rw-r--r-- | sdm/libs/hwc2/hwc_display_primary.cpp | 9 |
7 files changed, 612 insertions, 85 deletions
diff --git a/libhistogram/Android.mk b/libhistogram/Android.mk index 2117b3c4..d67b2285 100644 --- a/libhistogram/Android.mk +++ b/libhistogram/Android.mk @@ -17,8 +17,8 @@ include $(CLEAR_VARS) LOCAL_HEADER_LIBRARIES := display_headers LOCAL_MODULE := libhistogram -LOCAL_SRC_FILES := histogram_collector.cpp -LOCAL_SHARED_LIBRARIES := libdrm.vendor liblog libcutils +LOCAL_SRC_FILES := histogram_collector.cpp ringbuffer.cpp +LOCAL_SHARED_LIBRARIES := libdrm.vendor liblog libcutils libutils LOCAL_CFLAGS := -DLOG_TAG=\"SDM-histogram\" -Wall -std=c++14 -Werror -fno-operator-names LOCAL_CLANG := true LOCAL_MODULE_TAGS := optional @@ -41,3 +41,18 @@ LOCAL_MODULE_TAGS := optional LOCAL_VENDOR_MODULE := true include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) + +LOCAL_HEADER_LIBRARIES := display_headers +LOCAL_MODULE := color_sampling_test +LOCAL_SRC_FILES := ringbuffer_test.cpp + +LOCAL_STATIC_LIBRARIES := libhistogram libgtest libgmock +LOCAL_SHARED_LIBRARIES := libdrm.vendor liblog libcutils libutils libbase +LOCAL_CFLAGS := -DLOG_TAG=\"SDM-histogram\" -Wall -std=c++14 -Werror -fno-operator-names +LOCAL_CLANG := true +LOCAL_MODULE_TAGS := optional +LOCAL_VENDOR_MODULE := true + +include $(BUILD_EXECUTABLE) diff --git a/libhistogram/histogram_collector.cpp b/libhistogram/histogram_collector.cpp index 1d7fb118..9123eef3 100644 --- a/libhistogram/histogram_collector.cpp +++ b/libhistogram/histogram_collector.cpp @@ -37,6 +37,7 @@ #include <xf86drmMode.h> #include "histogram_collector.h" +#include "ringbuffer.h" namespace { @@ -274,97 +275,89 @@ enum class CrtcPowerState UNKNOWN }; +constexpr static auto implementation_defined_max_frame_ringbuffer = 300; } -namespace histogram { -struct VHistogram -{ - VHistogram() : - frame_count(0), - bins(desired_buckets, 0) { - } - - std::string dump() const { - std::unique_lock<decltype(mutex)> lk(mutex); - std::stringstream ss; - ss << "Color Sampling, dark (0.0) to light (1.0): sampled frames: " << frame_count << '\n'; - if (frame_count == 0) { - ss << "\tno color statistics collected\n"; - return ss.str(); - } - - ss << std::fixed << std::setprecision(3); - ss << "\tbucket\t\t: # of displayed pixels at bucket value\n"; - for (auto i = 0u; i < bins.size(); i++) { - ss << "\t" << i / static_cast<float>(bins.size()) << - " to " << ( i + 1 ) / static_cast<float>(bins.size()) << "\t: " << - bins[i] << '\n'; - } - return ss.str(); - } +histogram::HistogramCollector::HistogramCollector() : + histogram(histogram::Ringbuffer::create( + implementation_defined_max_frame_ringbuffer, std::make_unique<histogram::DefaultTimeKeeper>())) { +} - void insert_frame(drm_msm_hist* hist) { - static_assert((HIST_V_SIZE % desired_buckets) == 0, - "histogram cannot be rebucketed to smaller number of buckets"); +histogram::HistogramCollector::~HistogramCollector() { + stop(); +} - std::unique_lock<decltype(mutex)> lk(mutex); - for (auto i = 0u; i < HIST_V_SIZE; i++) - bins[i / bucket_compression] += hist->data[i]; - frame_count++; +std::string histogram::HistogramCollector::Dump() const { + uint64_t num_frames; + std::array<uint64_t, HIST_V_SIZE> samples; + std::tie(num_frames, samples) = histogram->collect_cumulative(); + std::stringstream ss; + ss << "Color Sampling, dark (0.0) to light (1.0): sampled frames: " << num_frames << '\n'; + if (num_frames == 0) { + ss << "\tno color statistics collected\n"; + return ss.str(); } - HWC2::Error collect(uint64_t /*max_frames*/, - uint64_t /*timestamp*/, - int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS], - uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS], - uint64_t* numFrames) const { - static constexpr int supportedComponentIdx = 2; - static_assert(supportedComponentIdx < NUM_HISTOGRAM_COLOR_COMPONENTS, - "supported component is out of range"); - - //TODO: add filtering by max_frames and timestamps. - if (!samples_size || !numFrames) - return HWC2::Error::BadParameter; - - *numFrames = frame_count; - std::fill(samples_size, samples_size + NUM_HISTOGRAM_COLOR_COMPONENTS, 0); - samples_size[supportedComponentIdx] = static_cast<int32_t>(bins.size()); - - if (samples && samples[supportedComponentIdx]) - std::copy(bins.begin(), bins.end(), samples[supportedComponentIdx]); - return HWC2::Error::None; + ss << std::fixed << std::setprecision(3); + ss << "\tbucket\t\t: # of displayed pixels at bucket value\n"; + for (auto i = 0u; i < samples.size(); i++) { + ss << "\t" << i / static_cast<float>(samples.size()) << + " to " << ( i + 1 ) / static_cast<float>(samples.size()) << "\t: " << + samples[i] << '\n'; } -private: - VHistogram(VHistogram const&) = delete; - VHistogram& operator=(VHistogram const&) = delete; - std::mutex mutable mutex; - uint64_t frame_count; - static constexpr int desired_buckets = 8; - static constexpr int bucket_compression = HIST_V_SIZE / desired_buckets; - std::vector<uint64_t> bins; -}; -} - -histogram::HistogramCollector::HistogramCollector() : - histogram(std::make_unique<histogram::VHistogram>()) { + return ss.str(); } -histogram::HistogramCollector::~HistogramCollector() { - stop(); +namespace { +static constexpr size_t numBuckets = 8; +static_assert((HIST_V_SIZE % numBuckets) == 0, + "histogram cannot be rebucketed to smaller number of buckets"); +static constexpr int bucket_compression = HIST_V_SIZE / numBuckets; + +std::array<uint64_t, numBuckets> rebucketTo8Buckets(std::array<uint64_t, HIST_V_SIZE> const& frame) { + std::array<uint64_t, numBuckets> bins; + bins.fill(0); + for (auto i = 0u; i < HIST_V_SIZE; i++) + bins[i / bucket_compression] += frame[i]; + return bins; } - -std::string histogram::HistogramCollector::Dump() const { - return histogram->dump(); } HWC2::Error histogram::HistogramCollector::collect( uint64_t max_frames, uint64_t timestamp, - int32_t samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS], - uint64_t* samples[NUM_HISTOGRAM_COLOR_COMPONENTS], - uint64_t* numFrames) const { - return histogram->collect(max_frames, timestamp, samples_size, samples, numFrames); + int32_t out_samples_size[NUM_HISTOGRAM_COLOR_COMPONENTS], + uint64_t* out_samples[NUM_HISTOGRAM_COLOR_COMPONENTS], + uint64_t* out_num_frames) const { + + if (!out_samples_size || !out_num_frames) + return HWC2::Error::BadParameter; + + out_samples_size[0] = 0; + out_samples_size[1] = 0; + out_samples_size[2] = numBuckets; + out_samples_size[3] = 0; + + uint64_t num_frames; + std::array<uint64_t, HIST_V_SIZE> samples; + + if (max_frames == 0 && timestamp == 0) { + std::tie(num_frames, samples) = histogram->collect_cumulative(); + } else if (max_frames == 0) { + std::tie(num_frames, samples) = histogram->collect_after(timestamp); + } else if (timestamp == 0) { + std::tie(num_frames, samples) = histogram->collect_max(max_frames); + } else { + std::tie(num_frames, samples) = histogram->collect_max_after(timestamp, max_frames); + } + + auto samples_rebucketed = rebucketTo8Buckets(samples); + *out_num_frames = num_frames; + if (out_samples && out_samples[2]) + memcpy(out_samples[2], samples_rebucketed.data(), sizeof(uint64_t) * samples_rebucketed.size()); + + return HWC2::Error::None; } HWC2::Error histogram::HistogramCollector::getAttributes(int32_t* format, @@ -380,8 +373,11 @@ HWC2::Error histogram::HistogramCollector::getAttributes(int32_t* format, } void histogram::HistogramCollector::start() { - std::unique_lock<decltype(thread_control)> lk(thread_control); + start(implementation_defined_max_frame_ringbuffer); +} +void histogram::HistogramCollector::start(uint64_t max_frames) { + std::unique_lock<decltype(thread_control)> lk(thread_control); if (started) { return; } @@ -390,7 +386,7 @@ void histogram::HistogramCollector::start() { ALOGE("histogram thread not started, could not create control pipe."); return; } - histogram = std::make_unique<histogram::VHistogram>(); + histogram = histogram::Ringbuffer::create(max_frames, std::make_unique<histogram::DefaultTimeKeeper>()); monitoring_thread = std::thread(&HistogramCollector::collecting_thread, this, selfpipe[0]); started = true; } @@ -544,7 +540,7 @@ void histogram::HistogramCollector::collecting_thread(int selfpipe) { if (response->base.type == DRM_EVENT_HISTOGRAM) { uint32_t blob_id = *reinterpret_cast<uint32_t*>(response->data); drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(*drm, blob_id); - histogram->insert_frame(static_cast<struct drm_msm_hist*>(blob->data)); + histogram->insert(*static_cast<struct drm_msm_hist*>(blob->data)); drmModeFreePropertyBlob(blob); } diff --git a/libhistogram/histogram_collector.h b/libhistogram/histogram_collector.h index 2e16bbfa..d2527ddb 100644 --- a/libhistogram/histogram_collector.h +++ b/libhistogram/histogram_collector.h @@ -29,7 +29,7 @@ namespace histogram { -struct VHistogram; +class Ringbuffer; class HistogramCollector { public: @@ -37,6 +37,7 @@ public: ~HistogramCollector(); void start(); + void start(uint64_t max_frames); void stop(); std::string Dump() const; @@ -60,7 +61,8 @@ private: std::thread monitoring_thread; int selfpipe[2]; - std::unique_ptr<VHistogram> histogram; + std::unique_ptr<histogram::Ringbuffer> histogram; + }; } // namespace histogram diff --git a/libhistogram/ringbuffer.cpp b/libhistogram/ringbuffer.cpp new file mode 100644 index 00000000..5b4c2fe2 --- /dev/null +++ b/libhistogram/ringbuffer.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 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 <algorithm> + +#include "ringbuffer.h" + +nsecs_t histogram::DefaultTimeKeeper::current_time() const { + return systemTime(SYSTEM_TIME_MONOTONIC); +} + +histogram::Ringbuffer::Ringbuffer(size_t ringbuffer_size, std::unique_ptr<histogram::TimeKeeper> tk) : + rb_max_size(ringbuffer_size), + timekeeper(std::move(tk)), + cumulative_frame_count(0) { + cumulative_bins.fill(0); +} + +std::unique_ptr<histogram::Ringbuffer> histogram::Ringbuffer::create(size_t ringbuffer_size, std::unique_ptr<histogram::TimeKeeper> tk) { + if ((ringbuffer_size == 0) || !tk) + return nullptr; + return std::unique_ptr<histogram::Ringbuffer>(new histogram::Ringbuffer(ringbuffer_size, std::move(tk))); +} + +void histogram::Ringbuffer::insert(drm_msm_hist const& frame) { + std::unique_lock<decltype(mutex)> lk(mutex); + + if (ringbuffer.size() == rb_max_size) + ringbuffer.pop_back(); + ringbuffer.push_front({frame, timekeeper->current_time()}); + + cumulative_frame_count++; + for (auto i = 0u; i < cumulative_bins.size(); i++) + cumulative_bins[i] += frame.data[i]; +} + +bool histogram::Ringbuffer::resize(size_t ringbuffer_size) { + std::unique_lock<decltype(mutex)> lk(mutex); + if (ringbuffer_size == 0) + return false; + rb_max_size = ringbuffer_size; + if (ringbuffer.size() > rb_max_size) + ringbuffer.resize(rb_max_size); + return true; +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_cumulative() const { + std::unique_lock<decltype(mutex)> lk(mutex); + return {cumulative_frame_count, cumulative_bins}; +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_ringbuffer_all() const { + std::unique_lock<decltype(mutex)> lk(mutex); + return collect_max(ringbuffer.size(), lk); +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_after( + nsecs_t timestamp) const { + std::unique_lock<decltype(mutex)> lk(mutex); + return collect_max_after(timestamp, ringbuffer.size(), lk); +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max(uint32_t max_frames) const { + std::unique_lock<decltype(mutex)> lk(mutex); + return collect_max(max_frames, lk); +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max_after( + nsecs_t timestamp, uint32_t max_frames) const { + std::unique_lock<decltype(mutex)> lk(mutex); + return collect_max_after(timestamp, max_frames, lk); +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max( + uint32_t max_frames, std::unique_lock<std::mutex> const&) const { + auto collect_first = std::min(static_cast<size_t>(max_frames), ringbuffer.size()); + if (collect_first == 0) + return {0, {}}; + std::array<uint64_t, HIST_V_SIZE> bins; + bins.fill(0); + for (auto it = ringbuffer.begin(); it != ringbuffer.begin() + collect_first; it++) { + for (auto i = 0u; i < HIST_V_SIZE; i++) { + bins[i] += it->histogram.data[i]; + } + } + return { collect_first, bins }; +} + +histogram::Ringbuffer::Sample histogram::Ringbuffer::collect_max_after( + nsecs_t timestamp, uint32_t max_frames, std::unique_lock<std::mutex> const& lk) const { + auto ts_filter_begin = std::lower_bound(ringbuffer.begin(), ringbuffer.end(), + HistogramEntry{ {}, timestamp}, + [](auto const& a, auto const& b) { return a.timestamp >= b.timestamp; }); + + auto collect_last = std::min( + std::distance(ringbuffer.begin(), ts_filter_begin), static_cast<std::ptrdiff_t>(max_frames)); + return collect_max(collect_last, lk); +} diff --git a/libhistogram/ringbuffer.h b/libhistogram/ringbuffer.h new file mode 100644 index 00000000..424fc5c9 --- /dev/null +++ b/libhistogram/ringbuffer.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 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 <sys/types.h> +#include <tuple> +#include <unistd.h> +#include <memory> +#include <drm/msm_drm.h> +#include <drm/msm_drm_pp.h> +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <array> +#include <deque> +#include <mutex> +#include <utils/Timers.h> + +namespace histogram { + +struct TimeKeeper { + virtual nsecs_t current_time() const = 0; + virtual ~TimeKeeper() = default; +protected: + TimeKeeper() = default; + TimeKeeper& operator=(TimeKeeper const&) = delete; + TimeKeeper(TimeKeeper const&) = delete; +}; + +struct DefaultTimeKeeper final : TimeKeeper +{ + nsecs_t current_time() const final; +}; + +class Ringbuffer +{ +public: + static std::unique_ptr<Ringbuffer> create(size_t ringbuffer_size, std::unique_ptr<TimeKeeper> tk); + void insert(drm_msm_hist const& frame); + bool resize(size_t ringbuffer_size); + + using Sample = std::tuple<uint64_t /* numFrames */, std::array<uint64_t, HIST_V_SIZE> /* bins */>; + Sample collect_cumulative() const; + Sample collect_ringbuffer_all() const; + Sample collect_after(nsecs_t timestamp) const; + Sample collect_max(uint32_t max_frames) const; + Sample collect_max_after(nsecs_t timestamp, uint32_t max_frames) const; + ~Ringbuffer() = default; + +private: + Ringbuffer(size_t ringbuffer_size, std::unique_ptr<TimeKeeper> tk); + Ringbuffer(Ringbuffer const&) = delete; + Ringbuffer& operator=(Ringbuffer const&) = delete; + + Sample collect_max(uint32_t max_frames, std::unique_lock<std::mutex> const&) const; + Sample collect_max_after(nsecs_t timestamp, uint32_t max_frames, std::unique_lock<std::mutex> const&) const; + + std::mutex mutable mutex; + struct HistogramEntry { + drm_msm_hist histogram; + nsecs_t timestamp; + }; + std::deque<HistogramEntry> ringbuffer; + size_t rb_max_size; + std::unique_ptr<TimeKeeper> const timekeeper; + + uint64_t cumulative_frame_count; + std::array<uint64_t, HIST_V_SIZE> cumulative_bins; +}; + +} diff --git a/libhistogram/ringbuffer_test.cpp b/libhistogram/ringbuffer_test.cpp new file mode 100644 index 00000000..6c272e2d --- /dev/null +++ b/libhistogram/ringbuffer_test.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2018 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 <numeric> + +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include "ringbuffer.h" +using namespace testing; + +struct StubTimeKeeper : histogram::TimeKeeper { + StubTimeKeeper() {} + StubTimeKeeper(std::vector<nsecs_t> const& sequence) : time_sequence(sequence) {} + + nsecs_t current_time() const final { + if (time_sequence.empty()) + return fake_time++; + if (last_returned_in_sequence == time_sequence.size()) { + return time_sequence.back() + ++fake_time; + } else { + return time_sequence[last_returned_in_sequence++]; + } + } + std::vector<nsecs_t> time_sequence = {}; + int mutable fake_time = 0; + int mutable last_returned_in_sequence = 0; +}; + +class RingbufferTestCases : public ::testing::Test { + void SetUp() { + for (auto i = 0u; i < HIST_V_SIZE; i++) { + frame0.data[i] = fill_frame0; + frame1.data[i] = fill_frame1; + frame2.data[i] = fill_frame2; + frame3.data[i] = fill_frame3; + frame4.data[i] = fill_frame4; + } + } + +protected: + std::unique_ptr<histogram::Ringbuffer> createFilledRingbuffer() { + auto rb = histogram::Ringbuffer::create(fake_timestamps.size(), std::make_unique<StubTimeKeeper>(fake_timestamps)); + rb->insert(frame0); + rb->insert(frame1); + rb->insert(frame2); + rb->insert(frame3); + return rb; + } + + int fill_frame0 = 9; + int fill_frame1 = 11; + int fill_frame2 = 303; + int fill_frame3 = 1030; + int fill_frame4 = 112200; + drm_msm_hist frame0; + drm_msm_hist frame1; + drm_msm_hist frame2; + drm_msm_hist frame3; + drm_msm_hist frame4; + std::vector<nsecs_t> fake_timestamps { 1, 3, 5, 7 }; + + int numFrames = 0; + std::array<uint64_t, HIST_V_SIZE> bins; +}; + +TEST_F(RingbufferTestCases, ZeroSizedRingbufferReturnsNull) { + EXPECT_THAT(histogram::Ringbuffer::create(0, std::make_unique<StubTimeKeeper>()), Eq(nullptr)); +} + +TEST_F(RingbufferTestCases, NullTimekeeperReturnsNull) { + EXPECT_THAT(histogram::Ringbuffer::create(10, nullptr), Eq(nullptr)); +} + +TEST_F(RingbufferTestCases, CollectionWithNoFrames) { + auto rb = histogram::Ringbuffer::create(1, std::make_unique<StubTimeKeeper>()); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(0)); + EXPECT_THAT(bins, Each(0)); +} + +TEST_F(RingbufferTestCases, SimpleTest) { + static constexpr int numInsertions = 3u; + auto rb = histogram::Ringbuffer::create(numInsertions, std::make_unique<StubTimeKeeper>()); + + drm_msm_hist frame; + for (auto i = 0u; i < HIST_V_SIZE; i++) { + frame.data[i] = i; + } + + rb->insert(frame); + rb->insert(frame); + rb->insert(frame); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + + ASSERT_THAT(bins.size(), Eq(HIST_V_SIZE)); + for (auto i = 0u; i < bins.size(); i++) { + EXPECT_THAT(bins[i], Eq(numInsertions * i)); + } +} + +TEST_F(RingbufferTestCases, TestEvictionSingle) { + int fill_frame0 = 9; + int fill_frame1 = 111; + drm_msm_hist frame0; + drm_msm_hist frame1; + for (auto i = 0u; i < HIST_V_SIZE; i++) { + frame0.data[i] = fill_frame0; + frame1.data[i] = fill_frame1; + } + + auto rb = histogram::Ringbuffer::create(1, std::make_unique<StubTimeKeeper>()); + + rb->insert(frame0); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(1)); + EXPECT_THAT(bins, Each(fill_frame0)); + + rb->insert(frame1); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(1)); + EXPECT_THAT(bins, Each(fill_frame1)); +} + +TEST_F(RingbufferTestCases, TestEvictionMultiple) { + auto rb = histogram::Ringbuffer::create(3, std::make_unique<StubTimeKeeper>()); + rb->insert(frame0); + rb->insert(frame1); + rb->insert(frame2); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2)); + + rb->insert(frame3); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3)); + + rb->insert(frame0); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3 + fill_frame0)); +} + +TEST_F(RingbufferTestCases, TestResizeToZero) { + auto rb = histogram::Ringbuffer::create(4, std::make_unique<StubTimeKeeper>()); + EXPECT_FALSE(rb->resize(0)); +} + +TEST_F(RingbufferTestCases, TestResizeDown) { + auto rb = histogram::Ringbuffer::create(4, std::make_unique<StubTimeKeeper>()); + rb->insert(frame0); + rb->insert(frame1); + rb->insert(frame2); + rb->insert(frame3); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(4)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3)); + + auto rc = rb->resize(2); + EXPECT_THAT(rc, Eq(true)); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3)); + + rb->insert(frame0); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame3)); +} + +TEST_F(RingbufferTestCases, TestResizeUp) { + auto rb = histogram::Ringbuffer::create(2, std::make_unique<StubTimeKeeper>()); + rb->insert(frame0); + rb->insert(frame1); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1)); + + auto rc = rb->resize(3); + EXPECT_THAT(rc, Eq(true)); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1)); + + rb->insert(frame2); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2)); + + rb->insert(frame3); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3)); +} + +TEST_F(RingbufferTestCases, TestTimestampFiltering) { + auto rb = createFilledRingbuffer(); + + std::tie(numFrames, bins) = rb->collect_after(4); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3)); + + std::tie(numFrames, bins) = rb->collect_after(fake_timestamps.back() + 1); + EXPECT_THAT(numFrames, Eq(0)); + + std::tie(numFrames, bins) = rb->collect_after(fake_timestamps.front() - 1); + EXPECT_THAT(numFrames, Eq(4)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3)); +} + +TEST_F(RingbufferTestCases, TestTimestampFilteringSameTimestamp) { + auto rb = createFilledRingbuffer(); + auto ts = fake_timestamps.back(); + rb->insert(frame4); + std::tie(numFrames, bins) = rb->collect_after(ts); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame3 + fill_frame4)); +} + +TEST_F(RingbufferTestCases, TestFrameFiltering) { + auto rb = createFilledRingbuffer(); + + std::tie(numFrames, bins) = rb->collect_max(2); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3)); + + std::tie(numFrames, bins) = rb->collect_max(0); + EXPECT_THAT(numFrames, Eq(0)); + EXPECT_THAT(bins, Each(0)); + + std::tie(numFrames, bins) = rb->collect_max(3); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3)); + + std::tie(numFrames, bins) = rb->collect_max( 2 * fake_timestamps.size()); + EXPECT_THAT(numFrames, Eq(4)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3)); +} + +TEST_F(RingbufferTestCases, TestTimestampAndFrameFiltering) { + auto rb = createFilledRingbuffer(); + + std::tie(numFrames, bins) = rb->collect_max_after(2, 1); + EXPECT_THAT(numFrames, Eq(1)); + EXPECT_THAT(bins, Each(fill_frame3)); + + std::tie(numFrames, bins) = rb->collect_max_after(4, 0); + EXPECT_THAT(numFrames, Eq(0)); + EXPECT_THAT(bins, Each(0)); + + std::tie(numFrames, bins) = rb->collect_max_after(10, 100); + EXPECT_THAT(numFrames, Eq(0)); + EXPECT_THAT(bins, Each(0)); + + std::tie(numFrames, bins) = rb->collect_max_after(0, 10); + EXPECT_THAT(numFrames, Eq(4)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1 + fill_frame2 + fill_frame3)); +} + +TEST_F(RingbufferTestCases, TestTimestampAndFrameFilteringAndResize) { + auto rb = createFilledRingbuffer(); + + std::tie(numFrames, bins) = rb->collect_max_after(2, 1); + EXPECT_THAT(numFrames, Eq(1)); + EXPECT_THAT(bins, Each(fill_frame3)); + + std::tie(numFrames, bins) = rb->collect_max_after(2, 10); + EXPECT_THAT(numFrames, Eq(3)); + EXPECT_THAT(bins, Each(fill_frame1 + fill_frame2 + fill_frame3)); + + rb->resize(2); + std::tie(numFrames, bins) = rb->collect_max_after(2, 10); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame2 + fill_frame3)); +} + +TEST_F(RingbufferTestCases, TestCumulativeCounts) { + auto rb = histogram::Ringbuffer::create(1, std::make_unique<StubTimeKeeper>()); + + rb->insert(frame0); + + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(1)); + EXPECT_THAT(bins, Each(fill_frame0)); + + rb->insert(frame1); + std::tie(numFrames, bins) = rb->collect_ringbuffer_all(); + EXPECT_THAT(numFrames, Eq(1)); + EXPECT_THAT(bins, Each(fill_frame1)); + + std::tie(numFrames, bins) = rb->collect_cumulative(); + EXPECT_THAT(numFrames, Eq(2)); + EXPECT_THAT(bins, Each(fill_frame0 + fill_frame1)); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/sdm/libs/hwc2/hwc_display_primary.cpp b/sdm/libs/hwc2/hwc_display_primary.cpp index 830549aa..360b5d87 100644 --- a/sdm/libs/hwc2/hwc_display_primary.cpp +++ b/sdm/libs/hwc2/hwc_display_primary.cpp @@ -741,10 +741,13 @@ HWC2::Error HWCDisplayPrimary::SetDisplayedContentSamplingEnabled(int32_t enable api_sampling_vote = false; } - if (api_sampling_vote || vndservice_sampling_vote) { - histogram.start(); + auto start = api_sampling_vote || vndservice_sampling_vote; + if (start && max_frames == 0) { + histogram.start(); + } else if (start) { + histogram.start(max_frames); } else { - histogram.stop(); + histogram.stop(); } return HWC2::Error::None; } |