summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libhistogram/Android.mk19
-rw-r--r--libhistogram/histogram_collector.cpp152
-rw-r--r--libhistogram/histogram_collector.h6
-rw-r--r--libhistogram/ringbuffer.cpp110
-rw-r--r--libhistogram/ringbuffer.h83
-rw-r--r--libhistogram/ringbuffer_test.cpp318
-rw-r--r--sdm/libs/hwc2/hwc_display_primary.cpp9
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;
}