1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/exo/shell_surface_presentation_time_recorder.h"
#include <utility>
#include <vector>
#include "base/auto_reset.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/run_loop.h"
#include "base/time/time.h"
#include "components/exo/test/exo_test_base.h"
#include "components/exo/test/shell_surface_builder.h"
#include "components/exo/test/surface_tree_host_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/presentation_feedback.h"
using testing::ElementsAre;
namespace exo {
namespace {
class TestReporter : public ShellSurfacePresentationTimeRecorder::Reporter {
public:
TestReporter() = default;
~TestReporter() override = default;
// ShellSurfacePresentationTimeRecorder::Reporter:
void ReportTime(base::TimeDelta delta) override { ++report_count_; }
int GetReportCountAndReset() {
int count = report_count_;
report_count_ = 0;
return count;
}
private:
int report_count_ = 0;
};
class TestRecorder : public ShellSurfacePresentationTimeRecorder {
public:
TestRecorder(ShellSurface* shell_surface, std::unique_ptr<Reporter> reporter)
: ShellSurfacePresentationTimeRecorder(shell_surface,
std::move(reporter)) {}
// ShellSurfacePresentationTimeRecorder:
void OnFramePresented(const Request& request,
const gfx::PresentationFeedback& feedback) override {
presented_serials_.push_back(request.serial.value());
ShellSurfacePresentationTimeRecorder::OnFramePresented(request,
fake_feedback_);
if (run_loop_)
run_loop_->Quit();
}
void WaitForFramePresented() {
base::RunLoop run_loop;
base::AutoReset<base::RunLoop*> scoped(&run_loop_, &run_loop);
run_loop.Run();
}
std::vector<uint32_t> TakePresentedSerials() {
return std::move(presented_serials_);
}
void set_fake_feedback(const gfx::PresentationFeedback& fake_feedback) {
fake_feedback_ = fake_feedback;
}
private:
gfx::PresentationFeedback fake_feedback_;
// This field is not a raw_ptr<> because it was filtered by the rewriter
// for: #addr-of
RAW_PTR_EXCLUSION base::RunLoop* run_loop_ = nullptr;
std::vector<uint32_t> presented_serials_;
};
} // namespace
class ShellSurfacePresentationTimeRecorderTest : public test::ExoTestBase {
public:
ShellSurfacePresentationTimeRecorderTest() = default;
~ShellSurfacePresentationTimeRecorderTest() override = default;
// test::ExoTestBase:
void SetUp() override {
test::ExoTestBase::SetUp();
shell_surface_ = test::ShellSurfaceBuilder({32, 32}).BuildShellSurface();
auto reporter = std::make_unique<TestReporter>();
reporter_ = reporter.get();
recorder_ = std::make_unique<TestRecorder>(shell_surface_.get(),
std::move(reporter));
}
void TearDown() override {
shell_surface_.reset();
test::ExoTestBase::TearDown();
}
void FakeFrameSubmitAndPresent() {
base::TimeDelta interval_not_used = base::Milliseconds(0);
// Create feedback with an extra 1s to ensure that presentation timestamp
// is later than the request time on slow bots. Otherwise, the presentation
// would not be reported and fail test expectations.
gfx::PresentationFeedback feedback(
base::TimeTicks().Now() + base::Milliseconds(1000), interval_not_used,
/*flags=*/0);
recorder_->set_fake_feedback(feedback);
// Fake damage so that the committed frame will generate a presentation
// feedback when the next DrawAndSwap happens. Without damage the
// presentation feedback could be delayed till the next frame submission.
root_surface()->Damage(gfx::Rect(0, 0, 32, 32));
root_surface()->Commit();
recorder_->WaitForFramePresented();
}
Surface* root_surface() { return shell_surface_->root_surface(); }
protected:
std::unique_ptr<ShellSurface> shell_surface_;
std::unique_ptr<TestRecorder> recorder_;
raw_ptr<TestReporter, DanglingUntriaged | ExperimentalAsh> reporter_ =
nullptr;
};
TEST_F(ShellSurfacePresentationTimeRecorderTest, Request) {
// Request without "config" fails.
recorder_->PrepareToRecord();
EXPECT_FALSE(recorder_->RequestNext());
// Fake a "Connfigure".
recorder_->OnConfigure(1u);
// Request should succeed.
EXPECT_TRUE(recorder_->RequestNext());
}
TEST_F(ShellSurfacePresentationTimeRecorderTest, AckSkippedOrOutOfOrder) {
// Issue 4 requests with configure serial 1-5.
for (size_t i = 1u; i <= 5u; ++i) {
recorder_->PrepareToRecord();
recorder_->OnConfigure(i);
ASSERT_TRUE(recorder_->RequestNext());
}
// Ack 2 and skip 1.
recorder_->OnAcknowledgeConfigure(2u);
// FramePrsented would be reported for 1 and 2, even though 1 is not acked.
FakeFrameSubmitAndPresent();
EXPECT_THAT(recorder_->TakePresentedSerials(), ElementsAre(1, 2));
EXPECT_EQ(2, reporter_->GetReportCountAndReset());
// Ack 4 and 3 out of order.
recorder_->OnAcknowledgeConfigure(4u);
recorder_->OnAcknowledgeConfigure(3u);
recorder_->OnAcknowledgeConfigure(5u);
// FramePresented would be reported for 3, 4, and 5, even though 3 and 4
// is acked out of order.
FakeFrameSubmitAndPresent();
EXPECT_THAT(recorder_->TakePresentedSerials(), ElementsAre(3, 4, 5));
EXPECT_EQ(3, reporter_->GetReportCountAndReset());
}
TEST_F(ShellSurfacePresentationTimeRecorderTest,
RecorderDestroyedBeforePresent) {
// Create a pending request on the recorder.
recorder_->PrepareToRecord();
recorder_->OnConfigure(1u);
EXPECT_TRUE(recorder_->RequestNext());
recorder_->OnAcknowledgeConfigure(1u);
// `recorder_` is gone before frame submission and presentation. `reporter_`
// is owned by `recorder_` so clear its reference too.
recorder_.reset();
reporter_ = nullptr;
// Fake frame submission. No FakeFrameSubmitAndPresent() because it depends
// on `recorder_`.
root_surface()->Damage(gfx::Rect(0, 0, 32, 32));
root_surface()->Commit();
test::WaitForLastFramePresentation(shell_surface_.get());
}
TEST_F(ShellSurfacePresentationTimeRecorderTest,
ShellSurfaceDestroyedBeforeRecorder) {
// Create a pending request on the recorder.
recorder_->PrepareToRecord();
recorder_->OnConfigure(1u);
EXPECT_TRUE(recorder_->RequestNext());
recorder_->OnAcknowledgeConfigure(1u);
// ShellSurface gets destroyed before recorder.
shell_surface_.reset();
}
} // namespace exo
|