| Index: cc/surfaces/display_scheduler_unittest.cc
|
| diff --git a/cc/surfaces/display_scheduler_unittest.cc b/cc/surfaces/display_scheduler_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ab526f8fee569d8d428646197e131b5a587aae93
|
| --- /dev/null
|
| +++ b/cc/surfaces/display_scheduler_unittest.cc
|
| @@ -0,0 +1,321 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "cc/surfaces/display_scheduler.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/test/null_task_runner.h"
|
| +#include "base/trace_event/trace_event.h"
|
| +#include "cc/output/begin_frame_args.h"
|
| +#include "cc/surfaces/display.h"
|
| +#include "cc/test/scheduler_test_common.h"
|
| +#include "cc/test/test_now_source.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace cc {
|
| +namespace {
|
| +
|
| +class FakeDisplaySchedulerClient : public DisplaySchedulerClient {
|
| + public:
|
| + FakeDisplaySchedulerClient() : draw_and_swap_count_(0) {}
|
| +
|
| + ~FakeDisplaySchedulerClient() override {}
|
| +
|
| + bool DrawAndSwap() override {
|
| + draw_and_swap_count_++;
|
| + return true;
|
| + }
|
| +
|
| + void Reset() { draw_and_swap_count_ = 0; }
|
| +
|
| + int draw_and_swap_count() const { return draw_and_swap_count_; }
|
| +
|
| + protected:
|
| + int draw_and_swap_count_;
|
| +};
|
| +
|
| +class TestDisplayScheduler : public DisplayScheduler {
|
| + public:
|
| + TestDisplayScheduler(DisplaySchedulerClient* client,
|
| + BeginFrameSource* begin_frame_source,
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
| + int max_pending_swaps)
|
| + : DisplayScheduler(client,
|
| + begin_frame_source,
|
| + task_runner,
|
| + max_pending_swaps),
|
| + scheduler_begin_frame_deadline_count_(0) {}
|
| +
|
| + base::TimeTicks DesiredBeginFrameDeadlineTimeForTest() {
|
| + return DesiredBeginFrameDeadlineTime();
|
| + }
|
| +
|
| + void BeginFrameDeadlineForTest() { OnBeginFrameDeadline(); }
|
| +
|
| + void ScheduleBeginFrameDeadline() override {
|
| + scheduler_begin_frame_deadline_count_++;
|
| + DisplayScheduler::ScheduleBeginFrameDeadline();
|
| + }
|
| +
|
| + int scheduler_begin_frame_deadline_count() {
|
| + return scheduler_begin_frame_deadline_count_;
|
| + }
|
| +
|
| + protected:
|
| + int scheduler_begin_frame_deadline_count_;
|
| +};
|
| +
|
| +class DisplaySchedulerTest : public testing::Test {
|
| + public:
|
| + DisplaySchedulerTest() {
|
| + const int max_pending_swaps = 1;
|
| + now_src_ = TestNowSource::Create();
|
| + null_task_runner_ = make_scoped_refptr(new base::NullTaskRunner);
|
| + client_ = make_scoped_ptr(new FakeDisplaySchedulerClient);
|
| + scheduler_ = make_scoped_ptr(
|
| + new TestDisplayScheduler(client_.get(), &fake_begin_frame_source_,
|
| + null_task_runner_, max_pending_swaps));
|
| + }
|
| +
|
| + ~DisplaySchedulerTest() override {}
|
| +
|
| + void SetUp() override { scheduler_->SetRootSurfaceResourcesLocked(false); }
|
| +
|
| + void BeginFrameForTest() {
|
| + base::TimeTicks frame_time = now_src_->Now();
|
| + base::TimeDelta interval = BeginFrameArgs::DefaultInterval();
|
| + base::TimeTicks deadline = frame_time + interval -
|
| + BeginFrameArgs::DefaultEstimatedParentDrawTime();
|
| + fake_begin_frame_source_.TestOnBeginFrame(
|
| + BeginFrameArgs::Create(BEGINFRAME_FROM_HERE, frame_time, deadline,
|
| + interval, BeginFrameArgs::NORMAL));
|
| + }
|
| +
|
| + protected:
|
| + TestNowSource& now_src() { return *now_src_; }
|
| + FakeDisplaySchedulerClient& client() { return *client_; }
|
| + DisplayScheduler& scheduler() { return *scheduler_; }
|
| +
|
| + scoped_refptr<TestNowSource> now_src_;
|
| + scoped_refptr<base::NullTaskRunner> null_task_runner_;
|
| +
|
| + FakeBeginFrameSource fake_begin_frame_source_;
|
| + scoped_ptr<FakeDisplaySchedulerClient> client_;
|
| + scoped_ptr<TestDisplayScheduler> scheduler_;
|
| +};
|
| +
|
| +TEST_F(DisplaySchedulerTest, EntireDisplayDamagedDrawsImmediately) {
|
| + SurfaceId root_surface_id(1);
|
| + BeginFrameForTest();
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->EntireDisplayDamaged(root_surface_id);
|
| + EXPECT_GE(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| +}
|
| +
|
| +TEST_F(DisplaySchedulerTest, SurfaceDamaged) {
|
| + SurfaceId sid1(1);
|
| + SurfaceId sid2(2);
|
| +
|
| + // Get scheduler to detect surface 1 as active by drawing
|
| + // two frames in a row with damage from surface 1.
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| +
|
| + // Damage only from surface 2 (inactive) does not trigger deadline early.
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| +
|
| + // Damage from surface 1 triggers deadline early.
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + EXPECT_GE(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| +
|
| + // Make both surface 1 and 2 active.
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| +
|
| + // Deadline doesn't trigger early until surface 1 and 2 are both damaged.
|
| + BeginFrameForTest();
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + EXPECT_GE(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| +}
|
| +
|
| +TEST_F(DisplaySchedulerTest, OutputSurfaceLost) {
|
| + SurfaceId sid1(1);
|
| +
|
| + // DrawAndSwap normally.
|
| + BeginFrameForTest();
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + EXPECT_EQ(0, client_->draw_and_swap_count());
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| +
|
| + // Deadline triggers immediately on OutputSurfaceLost.
|
| + BeginFrameForTest();
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->OutputSurfaceLost();
|
| + EXPECT_GE(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| +
|
| + // Deadline does not DrawAndSwap after OutputSurfaceLost.
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| +}
|
| +
|
| +TEST_F(DisplaySchedulerTest, RootSurfaceResourcesLocked) {
|
| + SurfaceId sid1(1);
|
| + base::TimeTicks late_deadline;
|
| +
|
| + // DrawAndSwap normally.
|
| + BeginFrameForTest();
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + EXPECT_EQ(0, client_->draw_and_swap_count());
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| +
|
| + // Deadline triggers late while root resources are locked.
|
| + late_deadline = now_src().Now() + BeginFrameArgs::DefaultInterval();
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + EXPECT_GT(late_deadline, scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->SetRootSurfaceResourcesLocked(true);
|
| + EXPECT_EQ(late_deadline, scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| +
|
| + // Deadline does not DrawAndSwap while root resources are locked.
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| +
|
| + // Deadline triggers normally when root resources are unlocked.
|
| + late_deadline = now_src().Now() + BeginFrameArgs::DefaultInterval();
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + EXPECT_EQ(late_deadline, scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + scheduler_->SetRootSurfaceResourcesLocked(false);
|
| + EXPECT_EQ(base::TimeTicks(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| +
|
| + EXPECT_EQ(1, client_->draw_and_swap_count());
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(2, client_->draw_and_swap_count());
|
| +}
|
| +
|
| +TEST_F(DisplaySchedulerTest, DidSwapBuffers) {
|
| + SurfaceId sid1(1);
|
| + SurfaceId sid2(2);
|
| + base::TimeTicks late_deadline;
|
| +
|
| + // Get scheduler to detect surface 1 and 2 as active.
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + BeginFrameForTest();
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| +
|
| + // DrawAndSwap normally.
|
| + BeginFrameForTest();
|
| + EXPECT_LT(now_src().Now(),
|
| + scheduler_->DesiredBeginFrameDeadlineTimeForTest());
|
| + EXPECT_EQ(2, client_->draw_and_swap_count());
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(3, client_->draw_and_swap_count());
|
| + scheduler_->DidSwapBuffers();
|
| +
|
| + // Deadline triggers early when swap throttled.
|
| + BeginFrameForTest();
|
| + // Damage surface 1, but not surface 2 so we avoid triggering deadline
|
| + // early because all surfaces are ready.
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + EXPECT_EQ(scheduler_->DesiredBeginFrameDeadlineTimeForTest(),
|
| + base::TimeTicks());
|
| +
|
| + // Don't draw and swap in deadline while swap throttled.
|
| + EXPECT_EQ(3, client_->draw_and_swap_count());
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(3, client_->draw_and_swap_count());
|
| +
|
| + // Deadline triggers normally once not swap throttled.
|
| + // Damage from previous BeginFrame should cary over, so don't damage again.
|
| + late_deadline = now_src().Now() + BeginFrameArgs::DefaultInterval();
|
| + scheduler_->DidSwapBuffersComplete();
|
| + BeginFrameForTest();
|
| + EXPECT_GT(scheduler_->DesiredBeginFrameDeadlineTimeForTest(),
|
| + now_src().Now());
|
| + EXPECT_LT(scheduler_->DesiredBeginFrameDeadlineTimeForTest(), late_deadline);
|
| + // Still waiting for surface 2. Once it updates, deadline should trigger
|
| + // immediately again.
|
| + scheduler_->SurfaceDamaged(sid2);
|
| + EXPECT_EQ(scheduler_->DesiredBeginFrameDeadlineTimeForTest(),
|
| + base::TimeTicks());
|
| + // Draw and swap now that we aren't throttled.
|
| + EXPECT_EQ(3, client_->draw_and_swap_count());
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + EXPECT_EQ(4, client_->draw_and_swap_count());
|
| +}
|
| +
|
| +// This test verfies that we try to reschedule the deadline
|
| +// after any event that may change what deadline we want.
|
| +TEST_F(DisplaySchedulerTest, ScheduleBeginFrameDeadline) {
|
| + SurfaceId root_surface_id(1);
|
| + SurfaceId sid1(2);
|
| + int count = 1;
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + BeginFrameForTest();
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + scheduler_->BeginFrameDeadlineForTest();
|
| + scheduler_->DidSwapBuffers();
|
| + BeginFrameForTest();
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + scheduler_->DidSwapBuffersComplete();
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + scheduler_->EntireDisplayDamaged(root_surface_id);
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + scheduler_->SurfaceDamaged(sid1);
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + scheduler_->SetRootSurfaceResourcesLocked(true);
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +
|
| + scheduler_->OutputSurfaceLost();
|
| + EXPECT_EQ(count++, scheduler_->scheduler_begin_frame_deadline_count());
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace cc
|
|
|