Track stats per unique composition attributes
This change adds a new CompositionAttributes struct which tracks information about a given composition as it is validated and subsequently presented. Previously CompositionStats were tracked as totals for each display. This change splits the stats according to the CompositionAttributes for each presented composition. This change only affects the reporting behavior of CompositionStatsTracker. The stats logged by DumpState are totaled so as to not be affected. Change-Id: I050cce7f63891971cc807b9c5150d9b5d30f1f0b
This commit is contained in:
parent
ccbe364628
commit
48f056ff82
8 changed files with 245 additions and 158 deletions
|
|
@ -19,8 +19,13 @@
|
|||
#include "DrmHwc.h"
|
||||
|
||||
#include <cinttypes>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "backend/Backend.h"
|
||||
#include "stats/CompositionStats.h"
|
||||
#include "utils/log.h"
|
||||
#include "utils/properties.h"
|
||||
|
||||
|
|
@ -52,7 +57,8 @@ std::string DumpStats(const CompositionStats &stats) {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
std::string DumpDisplayStats(HwcDisplay *display, const CompositionStats &stats,
|
||||
std::string DumpDisplayStats(const HwcDisplay *display,
|
||||
const CompositionStats &stats,
|
||||
const CompositionStats &delta) {
|
||||
std::stringstream ss;
|
||||
ss << "- Display on: " << display->GetDisplayName() << "\n"
|
||||
|
|
@ -202,10 +208,10 @@ bool DrmHwc::DestroyVirtualDisplay(DisplayHandle display) {
|
|||
}
|
||||
|
||||
auto DrmHwc::PullCompositionStats()
|
||||
-> std::map<DisplayHandle, CompositionStats> {
|
||||
std::map<int64_t, CompositionStats> stats;
|
||||
for (auto &[display_handle, display] : displays_) {
|
||||
stats[static_cast<int64_t>(display_handle)] = display->total_stats();
|
||||
-> std::map<CompositionAttributes, CompositionStats> {
|
||||
std::map<CompositionAttributes, CompositionStats> stats;
|
||||
for (const auto &[display_handle, display] : displays_) {
|
||||
stats.insert(display->comp_stats().begin(), display->comp_stats().end());
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
|
@ -215,17 +221,33 @@ std::string DrmHwc::DumpState() {
|
|||
|
||||
output << "-- drm_hwcomposer --\n\n";
|
||||
|
||||
auto callback = [this, &output](int64_t display_handle,
|
||||
const CompositionStats &stats,
|
||||
const CompositionStats &delta) {
|
||||
auto *display = GetDisplay(display_handle);
|
||||
ALOGE_IF(display == nullptr, "Display %" PRIu64 " not found",
|
||||
display_handle);
|
||||
if (display) {
|
||||
output << DumpDisplayStats(display, stats, delta);
|
||||
std::map<DisplayHandle, std::pair<CompositionStats, CompositionStats>>
|
||||
total_stats;
|
||||
const auto callback = [&total_stats](const CompositionAttributes &attributes,
|
||||
const CompositionStats &cumulative,
|
||||
const CompositionStats &delta) {
|
||||
auto it = total_stats.find(attributes.display_handle);
|
||||
if (it == total_stats.end()) {
|
||||
total_stats.emplace(attributes.display_handle,
|
||||
std::make_pair(CompositionStats{},
|
||||
CompositionStats{}));
|
||||
it = total_stats.find(attributes.display_handle);
|
||||
}
|
||||
auto &[total_cumulative, total_delta] = it->second;
|
||||
total_cumulative += cumulative;
|
||||
total_delta += delta;
|
||||
};
|
||||
dump_stats_tracker_.ReportStats(callback);
|
||||
|
||||
for (const auto &[display_handle, display_stats] : total_stats) {
|
||||
const auto *display = GetDisplay(display_handle);
|
||||
ALOGE_IF(display == nullptr, "Display %" PRIu64 " not found",
|
||||
display_handle);
|
||||
if (display != nullptr) {
|
||||
output << DumpDisplayStats(display, display_stats.first,
|
||||
display_stats.second);
|
||||
}
|
||||
}
|
||||
return output.str();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class DrmHwc : public PipelineToFrontendBindingInterface,
|
|||
|
||||
// CompositionStatsProvider:
|
||||
auto PullCompositionStats()
|
||||
-> std::map<DisplayHandle, CompositionStats> override;
|
||||
-> std::map<CompositionAttributes, CompositionStats> override;
|
||||
|
||||
std::string DumpState();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "drm/DrmConnector.h"
|
||||
#include "drm/DrmDisplayPipeline.h"
|
||||
#include "drm/DrmHwc.h"
|
||||
#include "stats/CompositionStats.h"
|
||||
#include "utils/properties.h"
|
||||
|
||||
using ColorGamut = ::android::ColorSpace;
|
||||
|
|
@ -398,7 +399,9 @@ auto HwcDisplay::PresentStagedComposition(
|
|||
return true;
|
||||
}
|
||||
|
||||
++total_stats_.total_frames;
|
||||
CompositionAttributes attributes{.display_handle = handle_};
|
||||
CompositionStats stats{};
|
||||
++stats.total_frames;
|
||||
|
||||
// With multiple displays configured at different refresh rates,
|
||||
// desired_present_time can be up to almost 2 vsync periods away for the
|
||||
|
|
@ -419,21 +422,27 @@ auto HwcDisplay::PresentStagedComposition(
|
|||
// Check if validation was performed and update related stats. Otherwise
|
||||
// populate the composition types now.
|
||||
if (validated_composition_.has_value()) {
|
||||
attributes.validation_result = validated_composition_->flatten_reason ==
|
||||
FlattenReason::kValidateFailed
|
||||
? ValidationResult::kFailure
|
||||
: ValidationResult::kSuccess;
|
||||
attributes.flatten_reason = validated_composition_->flatten_reason;
|
||||
if (validated_composition_->flatten_reason ==
|
||||
FlattenReason::kValidateFailed) {
|
||||
++total_stats_.failed_kms_validate;
|
||||
++stats.failed_kms_validate;
|
||||
} else if (validated_composition_->flatten_reason ==
|
||||
FlattenReason::kStaticScene) {
|
||||
++total_stats_.frames_flattened;
|
||||
++stats.frames_flattened;
|
||||
}
|
||||
if (validated_composition_->cursor_plane_validated.has_value()) {
|
||||
if (validated_composition_->cursor_plane_validated.value()) {
|
||||
++total_stats_.cursor_plane_frames;
|
||||
++stats.cursor_plane_frames;
|
||||
} else {
|
||||
++total_stats_.failed_kms_cursor_validate;
|
||||
++stats.failed_kms_cursor_validate;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
attributes.validation_result = ValidationResult::kSkip;
|
||||
validated_composition_ = Backend::ValidatedComposition{};
|
||||
for (const auto &[id, layer] : layers_) {
|
||||
validated_composition_->composition_types
|
||||
|
|
@ -442,17 +451,22 @@ auto HwcDisplay::PresentStagedComposition(
|
|||
}
|
||||
|
||||
for (const auto &[id, layer] : layers_) {
|
||||
total_stats_.total_pixops += layer.GetPixOps();
|
||||
stats.total_pixops += layer.GetPixOps();
|
||||
if (layer.GetValidatedType() == CompositionType::kClient) {
|
||||
total_stats_.gpu_pixops += layer.GetPixOps();
|
||||
stats.gpu_pixops += layer.GetPixOps();
|
||||
}
|
||||
}
|
||||
|
||||
if (!CommitStagedComposition(out_present_fence)) {
|
||||
++total_stats_.failed_kms_present;
|
||||
attributes.present_failed = true;
|
||||
++stats.failed_kms_present;
|
||||
comp_stats_[attributes] += stats;
|
||||
return false;
|
||||
}
|
||||
|
||||
attributes.present_failed = false;
|
||||
comp_stats_[attributes] += stats;
|
||||
|
||||
// Reset the hdr output metadata blobs so we don't apply it repeatedly.
|
||||
hdr_metadata_.reset();
|
||||
|
||||
|
|
|
|||
|
|
@ -197,8 +197,8 @@ class HwcDisplay {
|
|||
|
||||
bool ForcedScalingWithGpu() const;
|
||||
|
||||
CompositionStats &total_stats() {
|
||||
return total_stats_;
|
||||
const std::map<CompositionAttributes, CompositionStats> &comp_stats() const {
|
||||
return comp_stats_;
|
||||
}
|
||||
|
||||
/* Headless mode required to keep SurfaceFlinger alive when all display are
|
||||
|
|
@ -270,6 +270,17 @@ class HwcDisplay {
|
|||
// transitions and update the config groups.
|
||||
void SetConfigGroupsForActiveConfig();
|
||||
|
||||
void SetColorMatrixToIdentity();
|
||||
|
||||
bool Init();
|
||||
|
||||
void SetHdrOutputMetadata(ui::Hdr hdrType);
|
||||
void SetOutputType(OutputType hdr_output_type);
|
||||
|
||||
auto GetEdid() const -> const EdidWrapperUnique & {
|
||||
return edid_wrapper_;
|
||||
}
|
||||
|
||||
HwcDisplayConfigs configs_;
|
||||
|
||||
DrmHwc *const hwc_;
|
||||
|
|
@ -311,18 +322,7 @@ class HwcDisplay {
|
|||
SharedFd writeback_complete_fence_;
|
||||
|
||||
uint32_t frame_no_ = 0;
|
||||
CompositionStats total_stats_;
|
||||
|
||||
void SetColorMatrixToIdentity();
|
||||
|
||||
bool Init();
|
||||
|
||||
void SetHdrOutputMetadata(ui::Hdr hdrType);
|
||||
void SetOutputType(OutputType hdr_output_type);
|
||||
|
||||
auto GetEdid() const -> const EdidWrapperUnique & {
|
||||
return edid_wrapper_;
|
||||
}
|
||||
std::map<CompositionAttributes, CompositionStats> comp_stats_{};
|
||||
|
||||
std::shared_ptr<FrontendDisplayBase> frontend_private_data_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,25 @@
|
|||
|
||||
namespace android::drm_hwcomposer {
|
||||
|
||||
bool operator<(const CompositionAttributes& a, const CompositionAttributes& b) {
|
||||
return std::make_tuple(a.display_handle, a.present_failed,
|
||||
a.validation_result, a.flatten_reason) <
|
||||
std::make_tuple(b.display_handle, b.present_failed,
|
||||
b.validation_result, b.flatten_reason);
|
||||
}
|
||||
|
||||
CompositionStats& CompositionStats::operator+=(const CompositionStats& other) {
|
||||
total_frames += other.total_frames;
|
||||
total_pixops += other.total_pixops;
|
||||
gpu_pixops += other.gpu_pixops;
|
||||
failed_kms_validate += other.failed_kms_validate;
|
||||
failed_kms_present += other.failed_kms_present;
|
||||
frames_flattened += other.frames_flattened;
|
||||
cursor_plane_frames += other.cursor_plane_frames;
|
||||
failed_kms_cursor_validate += other.failed_kms_cursor_validate;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CompositionStats operator-(const CompositionStats& a,
|
||||
const CompositionStats& b) {
|
||||
return {a.total_frames - b.total_frames,
|
||||
|
|
@ -32,9 +51,12 @@ CompositionStats operator-(const CompositionStats& a,
|
|||
|
||||
void CompositionStatsTracker::ReportStats(const Callback& callback) {
|
||||
auto new_stats = provider_->PullCompositionStats();
|
||||
for (auto& [display_handle, cumulative_stats] : new_stats) {
|
||||
auto delta = cumulative_stats - previous_stats_[display_handle];
|
||||
callback(display_handle, cumulative_stats, delta);
|
||||
for (const auto& [attributes, cumulative_stats] : new_stats) {
|
||||
const auto it = previous_stats_.find(attributes);
|
||||
const auto prev = it == previous_stats_.end() ? CompositionStats{}
|
||||
: it->second;
|
||||
const auto delta = cumulative_stats - prev;
|
||||
callback(attributes, cumulative_stats, delta);
|
||||
}
|
||||
previous_stats_ = new_stats;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,8 +20,29 @@
|
|||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include "backend/Backend.h"
|
||||
|
||||
namespace android::drm_hwcomposer {
|
||||
|
||||
enum class ValidationResult {
|
||||
kUnspecified = 0,
|
||||
kSuccess,
|
||||
kFailure,
|
||||
kSkip,
|
||||
};
|
||||
|
||||
struct CompositionAttributes {
|
||||
int64_t display_handle = 0;
|
||||
bool present_failed = false;
|
||||
ValidationResult validation_result = ValidationResult::kSkip;
|
||||
Backend::FlattenReason flatten_reason = Backend::FlattenReason::kNone;
|
||||
|
||||
// When adding new attributes, update the operator< below as well as
|
||||
// operator== which is implemented in the unit test file.
|
||||
};
|
||||
|
||||
bool operator<(const CompositionAttributes& a, const CompositionAttributes& b);
|
||||
|
||||
struct CompositionStats {
|
||||
uint32_t total_frames = 0;
|
||||
uint64_t total_pixops = 0;
|
||||
|
|
@ -32,20 +53,23 @@ struct CompositionStats {
|
|||
uint32_t cursor_plane_frames = 0;
|
||||
uint32_t failed_kms_cursor_validate = 0;
|
||||
|
||||
// When adding new stats, update the operator- below as well as
|
||||
// When adding new stats, update the operator+= and operator- below as well as
|
||||
// operator== and operator<< which are implemented in the unit test file.
|
||||
|
||||
CompositionStats& operator+=(const CompositionStats& other);
|
||||
};
|
||||
|
||||
// Used for calculating the delta between two CompositionStats.
|
||||
CompositionStats operator-(const CompositionStats& a,
|
||||
const CompositionStats& b);
|
||||
|
||||
// Interface for a reporter of per-display CompositionStats.
|
||||
// Interface for a reporter which pulls CompositionStats bucketed by
|
||||
// CompositionAttributes.
|
||||
class CompositionStatsProvider {
|
||||
public:
|
||||
// Get cumulative stats per display.
|
||||
// Get cumulative stats per unique attributes.
|
||||
virtual auto PullCompositionStats()
|
||||
-> std::map<int64_t, CompositionStats> = 0;
|
||||
-> std::map<CompositionAttributes, CompositionStats> = 0;
|
||||
virtual ~CompositionStatsProvider() = default;
|
||||
};
|
||||
|
||||
|
|
@ -53,21 +77,22 @@ class CompositionStatsProvider {
|
|||
// and keeps track of the previous stats state in order to calculate the deltas.
|
||||
class CompositionStatsTracker {
|
||||
public:
|
||||
// Arguments are the display ID, the cumulative stats, and the stats delta.
|
||||
using Callback = std::function<void(int64_t display_handle,
|
||||
// Arguments are the attributes, the cumulative stats, and the stats delta.
|
||||
using Callback = std::function<void(const CompositionAttributes& attributes,
|
||||
const CompositionStats& cumulative,
|
||||
const CompositionStats& delta)>;
|
||||
explicit CompositionStatsTracker(CompositionStatsProvider* provider)
|
||||
: provider_(provider) {
|
||||
}
|
||||
|
||||
// Callback will be called for each display, with the cumulative
|
||||
// stats and the stats delta from the previous invocation.
|
||||
// Callback will be called for each unique attribute (empty entries are
|
||||
// skipped), with the cumulative stats and the stats delta from the previous
|
||||
// invocation.
|
||||
void ReportStats(const Callback& callback);
|
||||
|
||||
private:
|
||||
CompositionStatsProvider* provider_;
|
||||
std::map<int64_t, CompositionStats> previous_stats_;
|
||||
std::map<CompositionAttributes, CompositionStats> previous_stats_;
|
||||
};
|
||||
|
||||
} // namespace android::drm_hwcomposer
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include <chrono>
|
||||
|
||||
#include "hwc/HwcDisplay.h"
|
||||
#include "stats/CompositionStats.h"
|
||||
#include "stats/CompositionStatsAtomReporter.h"
|
||||
|
||||
namespace android::drm_hwcomposer {
|
||||
|
|
@ -42,13 +42,13 @@ CompositionStatsPoller::~CompositionStatsPoller() {
|
|||
void CompositionStatsPoller::PollFunc() {
|
||||
bool thread_exit = false;
|
||||
while (!thread_exit) {
|
||||
tracker_.ReportStats([this](DisplayHandle display_handle,
|
||||
tracker_.ReportStats([this](const CompositionAttributes& attributes,
|
||||
const CompositionStats& /*cumulative*/,
|
||||
const CompositionStats& delta) {
|
||||
if (delta.total_frames == 0) {
|
||||
return;
|
||||
}
|
||||
reporter_->PushAtom(display_handle, delta.total_frames,
|
||||
reporter_->PushAtom(attributes.display_handle, delta.total_frames,
|
||||
delta.failed_kms_present, delta.failed_kms_validate);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,14 @@ using ::testing::StrictMock;
|
|||
|
||||
namespace android::drm_hwcomposer {
|
||||
|
||||
static bool operator==(const CompositionAttributes& lhs,
|
||||
const CompositionAttributes& rhs) {
|
||||
return lhs.display_handle == rhs.display_handle &&
|
||||
lhs.present_failed == rhs.present_failed &&
|
||||
lhs.validation_result == rhs.validation_result &&
|
||||
lhs.flatten_reason == rhs.flatten_reason;
|
||||
}
|
||||
|
||||
// Equality operator to be used by Eq, ASSERT_EQ, etc. Needs to be static rather
|
||||
// than in anonymous namespace to ensure gmock can find it.
|
||||
static bool operator==(const CompositionStats& lhs,
|
||||
|
|
@ -47,8 +55,7 @@ static bool operator==(const CompositionStats& lhs,
|
|||
// Stream insertion operator for better gtest failure messages.
|
||||
static std::ostream& operator<<(std::ostream& os,
|
||||
const CompositionStats& stats) {
|
||||
os << "CompositionStats { "
|
||||
<< "total_frames: " << stats.total_frames
|
||||
os << "CompositionStats { " << "total_frames: " << stats.total_frames
|
||||
<< ", total_pixops: " << stats.total_pixops
|
||||
<< ", gpu_pixops: " << stats.gpu_pixops
|
||||
<< ", failed_kms_validate: " << stats.failed_kms_validate
|
||||
|
|
@ -62,8 +69,8 @@ static std::ostream& operator<<(std::ostream& os,
|
|||
|
||||
class MockCompositionStatsProvider : public CompositionStatsProvider {
|
||||
public:
|
||||
MOCK_METHOD((std::map<int64_t, CompositionStats>), PullCompositionStats, (),
|
||||
(override));
|
||||
MOCK_METHOD((std::map<CompositionAttributes, CompositionStats>),
|
||||
PullCompositionStats, (), (override));
|
||||
};
|
||||
|
||||
// Helper class to facilitate passing std::function to the
|
||||
|
|
@ -72,12 +79,14 @@ class MockStatsCallback {
|
|||
public:
|
||||
// Set expectations on the Invoke mock method.
|
||||
MOCK_METHOD(void, Invoke,
|
||||
(int64_t, const CompositionStats&, const CompositionStats&), ());
|
||||
(const CompositionAttributes&, const CompositionStats&,
|
||||
const CompositionStats&),
|
||||
());
|
||||
|
||||
// Pass this to CompositionStatsTracker::ReportStats.
|
||||
CompositionStatsTracker::Callback AsStdFunction() {
|
||||
return [this](int64_t id, const CompositionStats& c,
|
||||
const CompositionStats& d) { this->Invoke(id, c, d); };
|
||||
return [this](const CompositionAttributes& a, const CompositionStats& c,
|
||||
const CompositionStats& d) { this->Invoke(a, c, d); };
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -106,10 +115,10 @@ class CompositionStatsTrackerTest : public ::testing::Test {
|
|||
|
||||
// Initial call to ReportStats reports the same stats for cumulative and delta.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsInitialCall) {
|
||||
const int64_t display_handle = 1;
|
||||
CompositionStats current_stats = CreateStats(100);
|
||||
std::map<int64_t, CompositionStats> provider_result = {
|
||||
{display_handle, current_stats}};
|
||||
const CompositionAttributes attr{.display_handle = 1};
|
||||
const CompositionStats current_stats = CreateStats(100);
|
||||
const std::map<CompositionAttributes, CompositionStats> provider_result{
|
||||
{attr, current_stats}};
|
||||
|
||||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
||||
|
|
@ -118,18 +127,18 @@ TEST_F(CompositionStatsTrackerTest, ReportStatsInitialCall) {
|
|||
|
||||
// Expect that delta is same as cumulative.
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(display_handle), Eq(current_stats), Eq(current_stats)));
|
||||
Invoke(Eq(attr), Eq(current_stats), Eq(current_stats)));
|
||||
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
// Subsequent calls to ReportStats with no change in cumulative stats.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsSubsequentCallNoChange) {
|
||||
const int64_t display_handle = 1;
|
||||
CompositionStats stats = CreateStats(100);
|
||||
std::map<int64_t, CompositionStats> provider_result = {
|
||||
{display_handle, stats}};
|
||||
CompositionStats zero_delta = {};
|
||||
const CompositionAttributes attr{.display_handle = 1};
|
||||
const CompositionStats stats = CreateStats(100);
|
||||
const std::map<CompositionAttributes, CompositionStats> provider_result{
|
||||
{attr, stats}};
|
||||
const CompositionStats zero_delta{};
|
||||
|
||||
// Same provider result for both calls.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
|
|
@ -138,86 +147,83 @@ TEST_F(CompositionStatsTrackerTest, ReportStatsSubsequentCallNoChange) {
|
|||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
||||
// Initial call.
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle), Eq(stats), Eq(stats)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(attr), Eq(stats), Eq(stats)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
|
||||
// Second call. Delta should be zero.
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(display_handle), Eq(stats), Eq(zero_delta)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(attr), Eq(stats), Eq(zero_delta)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
// Test that the delta is reported as expected.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsSubsequentCallWithChange) {
|
||||
const int64_t display_handle = 1;
|
||||
CompositionStats stats1 = CreateStats(100);
|
||||
CompositionStats stats2 = CreateStats(150);
|
||||
std::map<int64_t, CompositionStats> provider_result1 = {
|
||||
{display_handle, stats1}};
|
||||
std::map<int64_t, CompositionStats> provider_result2 = {
|
||||
{display_handle, stats2}};
|
||||
CompositionStats expected_delta = stats2 - stats1;
|
||||
const CompositionAttributes attr{.display_handle = 1};
|
||||
const CompositionStats stats1 = CreateStats(100);
|
||||
const CompositionStats stats2 = CreateStats(150);
|
||||
const std::map<CompositionAttributes, CompositionStats> provider_result1{
|
||||
{attr, stats1}};
|
||||
const std::map<CompositionAttributes, CompositionStats> provider_result2{
|
||||
{attr, stats2}};
|
||||
const CompositionStats expected_delta = stats2 - stats1;
|
||||
|
||||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
||||
// First call with the initial stats.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result1));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(display_handle), Eq(stats1), Eq(stats1)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(attr), Eq(stats1), Eq(stats1)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
|
||||
// Second call with updated stats and non-trivial delta.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result2));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(display_handle), Eq(stats2), Eq(expected_delta)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(attr), Eq(stats2), Eq(expected_delta)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
// Test that stats for multiple displays are reported correctly.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsMultipleDisplays) {
|
||||
const int64_t display_handle1 = 10;
|
||||
const int64_t display_handle2 = 20;
|
||||
CompositionStats display1_stats1 = CreateStats(100);
|
||||
CompositionStats display2_stats1 = CreateStats(200);
|
||||
CompositionStats display1_stats2 = CreateStats(110);
|
||||
CompositionStats display2_stats2 = CreateStats(250);
|
||||
// Test that stats for multiple attributes are reported correctly.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsMultipleAttributes) {
|
||||
const CompositionAttributes attr1{.display_handle = 10};
|
||||
const CompositionAttributes attr2{.display_handle = 20};
|
||||
const CompositionStats attr1_stats1 = CreateStats(100);
|
||||
const CompositionStats attr2_stats1 = CreateStats(200);
|
||||
const CompositionStats attr1_stats2 = CreateStats(110);
|
||||
const CompositionStats attr2_stats2 = CreateStats(250);
|
||||
|
||||
std::map<int64_t, CompositionStats> provider_result1 =
|
||||
{{display_handle1, display1_stats1}, {display_handle2, display2_stats1}};
|
||||
std::map<int64_t, CompositionStats> provider_result2 =
|
||||
{{display_handle1, display1_stats2}, {display_handle2, display2_stats2}};
|
||||
const std::map<CompositionAttributes, CompositionStats>
|
||||
provider_result1{{attr1, attr1_stats1}, {attr2, attr2_stats1}};
|
||||
const std::map<CompositionAttributes, CompositionStats>
|
||||
provider_result2{{attr1, attr1_stats2}, {attr2, attr2_stats2}};
|
||||
|
||||
CompositionStats display1_expected_delta1 = display1_stats1;
|
||||
CompositionStats display1_expected_delta2 = display1_stats2 - display1_stats1;
|
||||
CompositionStats display2_expected_delta1 = display2_stats1;
|
||||
CompositionStats display2_expected_delta2 = display2_stats2 - display2_stats1;
|
||||
const CompositionStats attr1_expected_delta1 = attr1_stats1;
|
||||
const CompositionStats attr1_expected_delta2 = attr1_stats2 - attr1_stats1;
|
||||
const CompositionStats attr2_expected_delta1 = attr2_stats1;
|
||||
const CompositionStats attr2_expected_delta2 = attr2_stats2 - attr2_stats1;
|
||||
|
||||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
||||
// Initial call. Ordering between displays doesn't matter.
|
||||
// Initial call. Ordering between attributes doesn't matter.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result1));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle1), Eq(display1_stats1),
|
||||
Eq(display1_expected_delta1)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle2), Eq(display2_stats1),
|
||||
Eq(display2_expected_delta1)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr1), Eq(attr1_stats1), Eq(attr1_expected_delta1)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr2), Eq(attr2_stats1), Eq(attr2_expected_delta1)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
|
||||
// Updated call. Ordering between displays doesn't matter.
|
||||
// Updated call. Ordering between attributes doesn't matter.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result2));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle1), Eq(display1_stats2),
|
||||
Eq(display1_expected_delta2)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle2), Eq(display2_stats2),
|
||||
Eq(display2_expected_delta2)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr1), Eq(attr1_stats2), Eq(attr1_expected_delta2)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr2), Eq(attr2_stats2), Eq(attr2_expected_delta2)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
// No displays in the provider result.
|
||||
// No entries in the provider result.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsEmptyResult) {
|
||||
std::map<int64_t, CompositionStats> empty_result = {};
|
||||
const std::map<CompositionAttributes, CompositionStats> empty_result{};
|
||||
|
||||
// StrictMock will fail if there are any unexpected calls to Invoke.
|
||||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
|
@ -226,78 +232,76 @@ TEST_F(CompositionStatsTrackerTest, ReportStatsEmptyResult) {
|
|||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
// Display added in between calls to ReportStats.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsDisplayAdded) {
|
||||
const int64_t display_handle1 = 10;
|
||||
const int64_t display_handle2 = 20;
|
||||
const CompositionStats display1_stats1 = CreateStats(100);
|
||||
const CompositionStats display1_stats2 = CreateStats(110);
|
||||
const CompositionStats display2_stats = CreateStats(50);
|
||||
// Attributes added in between calls to ReportStats.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsAttributesAdded) {
|
||||
const CompositionAttributes attr1{.display_handle = 10};
|
||||
const CompositionAttributes attr2{.display_handle = 20};
|
||||
const CompositionStats attr1_stats1 = CreateStats(100);
|
||||
const CompositionStats attr1_stats2 = CreateStats(110);
|
||||
const CompositionStats attr2_stats = CreateStats(50);
|
||||
|
||||
std::map<int64_t, CompositionStats> provider_result1 = {
|
||||
{display_handle1, display1_stats1}};
|
||||
std::map<int64_t, CompositionStats> provider_result2 =
|
||||
{{display_handle1, display1_stats2}, {display_handle2, display2_stats}};
|
||||
const std::map<CompositionAttributes, CompositionStats> provider_result1{
|
||||
{attr1, attr1_stats1}};
|
||||
const std::map<CompositionAttributes, CompositionStats>
|
||||
provider_result2{{attr1, attr1_stats2}, {attr2, attr2_stats}};
|
||||
|
||||
const CompositionStats display1_expected_delta1 = display1_stats1;
|
||||
const CompositionStats display1_expected_delta2 = display1_stats2 -
|
||||
display1_stats1;
|
||||
const CompositionStats display2_expected_delta = display2_stats;
|
||||
const CompositionStats attr1_expected_delta1 = attr1_stats1;
|
||||
const CompositionStats attr1_expected_delta2 = attr1_stats2 - attr1_stats1;
|
||||
const CompositionStats attr2_expected_delta = attr2_stats;
|
||||
|
||||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
||||
// First call only contains display 1.
|
||||
// First call only contains attr1.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result1));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle1), Eq(display1_stats1),
|
||||
Eq(display1_expected_delta1)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr1), Eq(attr1_stats1), Eq(attr1_expected_delta1)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
|
||||
// Second call has both displays.
|
||||
// Second call has both attributes.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result2));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle1), Eq(display1_stats2),
|
||||
Eq(display1_expected_delta2)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle2), Eq(display2_stats),
|
||||
Eq(display2_expected_delta)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr1), Eq(attr1_stats2), Eq(attr1_expected_delta2)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr2), Eq(attr2_stats), Eq(attr2_expected_delta)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
// Display removed in between calls to ReportStats.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsDisplayRemoved) {
|
||||
const int64_t display_handle1 = 10;
|
||||
const int64_t display_handle2 = 20;
|
||||
const CompositionStats display1_stats1 = CreateStats(100);
|
||||
const CompositionStats display2_stats = CreateStats(200);
|
||||
const CompositionStats display1_stats2 = CreateStats(110);
|
||||
// Attributes removed in between calls to ReportStats.
|
||||
TEST_F(CompositionStatsTrackerTest, ReportStatsAttributesRemoved) {
|
||||
const CompositionAttributes attr1{.display_handle = 10};
|
||||
const CompositionAttributes attr2{.display_handle = 20};
|
||||
const CompositionStats attr1_stats1 = CreateStats(100);
|
||||
const CompositionStats attr2_stats = CreateStats(200);
|
||||
const CompositionStats attr1_stats2 = CreateStats(110);
|
||||
|
||||
std::map<int64_t, CompositionStats> provider_result1 =
|
||||
{{display_handle1, display1_stats1}, {display_handle2, display2_stats}};
|
||||
std::map<int64_t, CompositionStats> provider_result2 = {
|
||||
{display_handle1, display1_stats2}};
|
||||
const std::map<CompositionAttributes, CompositionStats>
|
||||
provider_result1{{attr1, attr1_stats1}, {attr2, attr2_stats}};
|
||||
const std::map<CompositionAttributes, CompositionStats> provider_result2{
|
||||
{attr1, attr1_stats2}};
|
||||
|
||||
const CompositionStats display1_expected_delta1 = display1_stats1;
|
||||
const CompositionStats display2_expected_delta = display2_stats;
|
||||
const CompositionStats display1_expected_delta2 = display1_stats2 -
|
||||
display1_stats1;
|
||||
const CompositionStats attr1_expected_delta1 = attr1_stats1;
|
||||
const CompositionStats attr2_expected_delta = attr2_stats;
|
||||
const CompositionStats attr1_expected_delta2 = attr1_stats2 - attr1_stats1;
|
||||
|
||||
StrictMock<MockStatsCallback> mock_callback;
|
||||
|
||||
// Initial call has both displays.
|
||||
// Initial call has both attributes.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result1));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle1), Eq(display1_stats1),
|
||||
Eq(display1_expected_delta1)));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle2), Eq(display2_stats),
|
||||
Eq(display2_expected_delta)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr1), Eq(attr1_stats1), Eq(attr1_expected_delta1)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr2), Eq(attr2_stats), Eq(attr2_expected_delta)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
|
||||
// Second call has only display 1. StrictMock will fail if Invoke is called
|
||||
// for display_handle2.
|
||||
// Second call has only attr1. StrictMock will fail if Invoke is called
|
||||
// for attr2.
|
||||
EXPECT_CALL(*mock_provider_, PullCompositionStats())
|
||||
.WillOnce(Return(provider_result2));
|
||||
EXPECT_CALL(mock_callback, Invoke(Eq(display_handle1), Eq(display1_stats2),
|
||||
Eq(display1_expected_delta2)));
|
||||
EXPECT_CALL(mock_callback,
|
||||
Invoke(Eq(attr1), Eq(attr1_stats2), Eq(attr1_expected_delta2)));
|
||||
tracker_->ReportStats(mock_callback.AsStdFunction());
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue