| Index: gpu/ipc/service/gpu_scheduler_unittest.cc
|
| diff --git a/gpu/ipc/service/gpu_scheduler_unittest.cc b/gpu/ipc/service/gpu_scheduler_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0744c3c34f29918cf44fe7b5f18f0cee33e3fbf7
|
| --- /dev/null
|
| +++ b/gpu/ipc/service/gpu_scheduler_unittest.cc
|
| @@ -0,0 +1,309 @@
|
| +// Copyright (c) 2016 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 "gpu/ipc/service/gpu_scheduler.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/test/test_mock_time_task_runner.h"
|
| +#include "gpu/ipc/service/gpu_command_stream.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gmock_mutant.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace gpu {
|
| +namespace {
|
| +
|
| +using ::testing::_;
|
| +using ::testing::CreateFunctor;
|
| +using ::testing::Invoke;
|
| +using ::testing::Return;
|
| +using ::testing::StrictMock;
|
| +
|
| +base::TimeDelta kSchedulingGranularity =
|
| + base::TimeDelta::FromMicroseconds(1000);
|
| +
|
| +base::TimeDelta kTimeSliceRealTime = base::TimeDelta::FromMicroseconds(16000);
|
| +base::TimeDelta kTimeSliceHigh = base::TimeDelta::FromMicroseconds(8000);
|
| +base::TimeDelta kTimeSliceNormal = base::TimeDelta::FromMicroseconds(4000);
|
| +base::TimeDelta kTimeSliceLow = base::TimeDelta::FromMicroseconds(2000);
|
| +
|
| +base::TimeDelta kRealTimeMaxWaitDuration =
|
| + base::TimeDelta::FromMicroseconds(4000);
|
| +
|
| +base::TimeDelta kSchedulerPollingInterval =
|
| + base::TimeDelta::FromMicroseconds(100);
|
| +
|
| +class TestTaskRunner : public base::TestMockTimeTaskRunner {
|
| + public:
|
| + TestTaskRunner()
|
| + : TestMockTimeTaskRunner(), run_pending_only_(false), pending_tasks_(0) {}
|
| +
|
| + void RunPendingTasksUntil(base::TimeTicks target) {
|
| + run_pending_only_ = true;
|
| + pending_tasks_ = TestMockTimeTaskRunner::GetPendingTaskCount();
|
| + base::TimeDelta delta = target - TestMockTimeTaskRunner::NowTicks();
|
| + TestMockTimeTaskRunner::FastForwardBy(delta);
|
| + run_pending_only_ = false;
|
| + }
|
| +
|
| + protected:
|
| + ~TestTaskRunner() override {}
|
| +
|
| + bool IsElapsingStopped() override {
|
| + if (run_pending_only_)
|
| + return pending_tasks_ == 0;
|
| + return TestMockTimeTaskRunner::IsElapsingStopped();
|
| + }
|
| +
|
| + void OnAfterTaskRun() override {
|
| + if (run_pending_only_ && pending_tasks_ > 0)
|
| + pending_tasks_--;
|
| + }
|
| +
|
| + private:
|
| + bool run_pending_only_;
|
| + size_t pending_tasks_;
|
| +};
|
| +
|
| +class TestGpuScheduler : public GpuScheduler {
|
| + public:
|
| + static std::unique_ptr<TestGpuScheduler> Create(
|
| + scoped_refptr<TestTaskRunner> task_runner) {
|
| + GpuSchedulerSettings settings;
|
| + base::TimeDelta priority_time_slice[kNumGpuStreamPriorities] = {
|
| + kTimeSliceRealTime, kTimeSliceHigh, kTimeSliceNormal, kTimeSliceLow};
|
| + std::copy(priority_time_slice,
|
| + priority_time_slice + kNumGpuStreamPriorities,
|
| + settings.priority_time_slice);
|
| + settings.scheduling_granularity = kSchedulingGranularity;
|
| + settings.real_time_max_wait_duration = kRealTimeMaxWaitDuration;
|
| +
|
| + return base::WrapUnique(new TestGpuScheduler(settings, task_runner));
|
| + }
|
| +
|
| + TestGpuScheduler(const GpuSchedulerSettings& settings,
|
| + scoped_refptr<TestTaskRunner> task_runner)
|
| + : GpuScheduler(settings, task_runner, task_runner),
|
| + task_runner_(task_runner) {}
|
| + ~TestGpuScheduler() override {}
|
| +
|
| + void RunPendingTasks() { task_runner_->RunPendingTasksUntil(now_); }
|
| +
|
| + void AdvanceSchedulingTick() { AdvanceNow(kSchedulingGranularity); }
|
| +
|
| + void AdvanceNow(base::TimeDelta delta) { now_ += delta; }
|
| +
|
| + protected:
|
| + base::TimeTicks Now() const override { return task_runner_->NowTicks(); }
|
| +
|
| + private:
|
| + scoped_refptr<TestTaskRunner> task_runner_;
|
| + base::TimeTicks now_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestGpuScheduler);
|
| +};
|
| +
|
| +class MockGpuCommandStream : public GpuCommandStream {
|
| + public:
|
| + MockGpuCommandStream(TestGpuScheduler* scheduler, GpuStreamPriority priority)
|
| + : scheduler_(scheduler), priority_(priority), can_run_(false) {}
|
| + ~MockGpuCommandStream() override {}
|
| +
|
| + bool CanRun() const override { return can_run_; }
|
| + void set_can_run(bool can_run) { can_run_ = can_run; }
|
| +
|
| + GpuStreamPriority Priority() const override { return priority_; }
|
| +
|
| + void EnablePolling() {
|
| + ON_CALL(*this, Run())
|
| + .WillByDefault(Invoke(this, &MockGpuCommandStream::PollScheduler));
|
| + }
|
| +
|
| + void DisablePolling() { ON_CALL(*this, Run()).WillByDefault(Return()); }
|
| +
|
| + base::TimeDelta poll_duration() const { return poll_duration_; }
|
| +
|
| + void PollScheduler() {
|
| + poll_duration_ = base::TimeDelta();
|
| + while (!scheduler_->NeedsRescheduling()) {
|
| + poll_duration_ += kSchedulerPollingInterval;
|
| + scheduler_->AdvanceNow(kSchedulerPollingInterval);
|
| + scheduler_->RunPendingTasks();
|
| + }
|
| + }
|
| +
|
| + MOCK_METHOD0(Run, void());
|
| +
|
| + private:
|
| + TestGpuScheduler* scheduler_;
|
| + GpuStreamPriority priority_;
|
| + bool can_run_;
|
| + base::TimeDelta poll_duration_;
|
| +};
|
| +
|
| +class GpuSchedulerTest : public testing::Test {
|
| + public:
|
| + GpuSchedulerTest()
|
| + : task_runner_(new TestTaskRunner),
|
| + scheduler_(TestGpuScheduler::Create(task_runner_)) {}
|
| + ~GpuSchedulerTest() override {}
|
| +
|
| + protected:
|
| + scoped_refptr<TestTaskRunner> task_runner_;
|
| + std::unique_ptr<TestGpuScheduler> scheduler_;
|
| +};
|
| +
|
| +TEST_F(GpuSchedulerTest, SingleStream) {
|
| + StrictMock<MockGpuCommandStream> stream(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream);
|
| + stream.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream);
|
| + for (int i = 0; i < 5; ++i) {
|
| + EXPECT_CALL(stream, Run());
|
| + scheduler_->RunPendingTasks();
|
| + }
|
| +}
|
| +
|
| +TEST_F(GpuSchedulerTest, CanRun) {
|
| + StrictMock<MockGpuCommandStream> stream(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream);
|
| + stream.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream);
|
| + for (int i = 0; i < 5; ++i) {
|
| + EXPECT_CALL(stream, Run());
|
| + scheduler_->RunPendingTasks();
|
| + }
|
| + EXPECT_CALL(stream, Run())
|
| + .WillOnce(Invoke(CreateFunctor(&MockGpuCommandStream::set_can_run,
|
| + base::Unretained(&stream), false)));
|
| + scheduler_->RunPendingTasks();
|
| + // No more Run.
|
| + scheduler_->RunPendingTasks();
|
| +}
|
| +
|
| +TEST_F(GpuSchedulerTest, MultipleStreams) {
|
| + StrictMock<MockGpuCommandStream> stream1(scheduler_.get(),
|
| + GpuStreamPriority::LOW);
|
| + scheduler_->AddStream(&stream1);
|
| + stream1.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream1);
|
| +
|
| + StrictMock<MockGpuCommandStream> stream2(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream2);
|
| + stream2.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream2);
|
| +
|
| + for (int i = 0; i < 5; ++i) {
|
| + EXPECT_CALL(stream2, Run());
|
| + scheduler_->RunPendingTasks();
|
| + EXPECT_CALL(stream1, Run());
|
| + scheduler_->RunPendingTasks();
|
| + }
|
| +}
|
| +
|
| +TEST_F(GpuSchedulerTest, Timeslice) {
|
| + StrictMock<MockGpuCommandStream> stream(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream);
|
| + stream.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream);
|
| +
|
| + stream.EnablePolling();
|
| + EXPECT_CALL(stream, Run());
|
| + scheduler_->RunPendingTasks();
|
| + EXPECT_EQ(kTimeSliceNormal, stream.poll_duration());
|
| +}
|
| +
|
| +TEST_F(GpuSchedulerTest, Preemption) {
|
| + StrictMock<MockGpuCommandStream> stream1(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream1);
|
| + stream1.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream1);
|
| +
|
| + StrictMock<MockGpuCommandStream> stream2(scheduler_.get(),
|
| + GpuStreamPriority::REAL_TIME);
|
| + scheduler_->AddStream(&stream2);
|
| + stream2.set_can_run(true);
|
| +
|
| + auto kRealTimeRunnableDelay = base::TimeDelta::FromMicroseconds(2500);
|
| + task_runner_->PostDelayedTask(FROM_HERE,
|
| + base::Bind(&TestGpuScheduler::OnStreamCanRun,
|
| + base::Unretained(scheduler_.get()),
|
| + base::Unretained(&stream2)),
|
| + kRealTimeRunnableDelay);
|
| +
|
| + stream1.EnablePolling();
|
| + EXPECT_CALL(stream1, Run());
|
| + scheduler_->RunPendingTasks();
|
| + EXPECT_EQ(kRealTimeRunnableDelay + kSchedulerPollingInterval,
|
| + stream1.poll_duration());
|
| +}
|
| +
|
| +TEST_F(GpuSchedulerTest, RemoveWhileRunning) {
|
| + StrictMock<MockGpuCommandStream> stream(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream);
|
| + stream.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream);
|
| +
|
| + EXPECT_CALL(stream, Run())
|
| + .WillOnce(Invoke(CreateFunctor(&TestGpuScheduler::RemoveStream,
|
| + base::Unretained(scheduler_.get()),
|
| + base::Unretained(&stream))));
|
| + scheduler_->RunPendingTasks();
|
| + // No more Run.
|
| + scheduler_->RunPendingTasks();
|
| +}
|
| +
|
| +TEST_F(GpuSchedulerTest, PriorityInversion) {
|
| + StrictMock<MockGpuCommandStream> stream1(scheduler_.get(),
|
| + GpuStreamPriority::REAL_TIME);
|
| + scheduler_->AddStream(&stream1);
|
| + stream1.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream1);
|
| +
|
| + StrictMock<MockGpuCommandStream> stream2(scheduler_.get(),
|
| + GpuStreamPriority::NORMAL);
|
| + scheduler_->AddStream(&stream2);
|
| + stream2.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream2);
|
| +
|
| + StrictMock<MockGpuCommandStream> stream3(scheduler_.get(),
|
| + GpuStreamPriority::LOW);
|
| + scheduler_->AddStream(&stream3);
|
| + stream3.set_can_run(true);
|
| + scheduler_->OnStreamCanRun(&stream3);
|
| +
|
| + EXPECT_CALL(stream1, Run())
|
| + .WillOnce(Invoke(CreateFunctor(&TestGpuScheduler::AddStreamDependency,
|
| + base::Unretained(scheduler_.get()),
|
| + base::Unretained(&stream3),
|
| + base::Unretained(&stream1))));
|
| + scheduler_->RunPendingTasks();
|
| +
|
| + EXPECT_CALL(stream3, Run())
|
| + .WillOnce(Invoke(CreateFunctor(&TestGpuScheduler::RemoveStreamDependency,
|
| + base::Unretained(scheduler_.get()),
|
| + base::Unretained(&stream3),
|
| + base::Unretained(&stream1))));
|
| + scheduler_->RunPendingTasks();
|
| +
|
| + EXPECT_CALL(stream2, Run());
|
| + scheduler_->RunPendingTasks();
|
| +
|
| + EXPECT_CALL(stream3, Run());
|
| + scheduler_->RunPendingTasks();
|
| +
|
| + EXPECT_CALL(stream1, Run());
|
| + scheduler_->RunPendingTasks();
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace gpu
|
|
|