blob: b2921a1cc41894b0e8c07dfebef130074b2fc973 [file] [log] [blame]
// Copyright (c) 2017 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/command_buffer/service/scheduler.h"
#include <algorithm>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/test/test_simple_task_runner.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gpu {
namespace {
template <typename T>
void RunFunctor(T functor) {
functor();
}
template <typename T>
base::OnceClosure GetClosure(T functor) {
return base::BindOnce(&RunFunctor<T>, functor);
}
class SchedulerTest : public testing::Test {
public:
SchedulerTest()
: task_runner_(new base::TestSimpleTaskRunner()),
sync_point_manager_(new SyncPointManager),
scheduler_(new Scheduler(task_runner_, sync_point_manager_.get())) {}
protected:
base::TestSimpleTaskRunner* task_runner() const { return task_runner_.get(); }
SyncPointManager* sync_point_manager() const {
return sync_point_manager_.get();
}
Scheduler* scheduler() const { return scheduler_.get(); }
private:
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
std::unique_ptr<SyncPointManager> sync_point_manager_;
std::unique_ptr<Scheduler> scheduler_;
};
TEST_F(SchedulerTest, ScheduledTasksRunInOrder) {
SequenceId sequence_id =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran1 = false;
scheduler()->ScheduleTask(sequence_id, GetClosure([&] { ran1 = true; }),
std::vector<SyncToken>());
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id, GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>());
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
}
TEST_F(SchedulerTest, ContinuedTasksRunFirst) {
SequenceId sequence_id =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran1 = false;
bool continued1 = false;
scheduler()->ScheduleTask(sequence_id, GetClosure([&] {
scheduler()->ContinueTask(
sequence_id,
GetClosure([&] { continued1 = true; }));
ran1 = true;
}),
std::vector<SyncToken>());
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id, GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>());
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
EXPECT_FALSE(continued1);
EXPECT_FALSE(ran2);
task_runner()->RunPendingTasks();
EXPECT_TRUE(continued1);
EXPECT_FALSE(ran2);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
}
TEST_F(SchedulerTest, SequencesRunInPriorityOrder) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kLowest);
bool ran1 = false;
scheduler()->ScheduleTask(sequence_id1, GetClosure([&] { ran1 = true; }),
std::vector<SyncToken>());
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>());
SequenceId sequence_id3 =
scheduler()->CreateSequence(SchedulingPriority::kHighest);
bool ran3 = false;
scheduler()->ScheduleTask(sequence_id3, GetClosure([&] { ran3 = true; }),
std::vector<SyncToken>());
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran3);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
}
TEST_F(SchedulerTest, SequencesOfSamePriorityRunInOrder) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran1 = false;
scheduler()->ScheduleTask(sequence_id1, GetClosure([&] { ran1 = true; }),
std::vector<SyncToken>());
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>());
SequenceId sequence_id3 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran3 = false;
scheduler()->ScheduleTask(sequence_id3, GetClosure([&] { ran3 = true; }),
std::vector<SyncToken>());
SequenceId sequence_id4 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran4 = false;
scheduler()->ScheduleTask(sequence_id4, GetClosure([&] { ran4 = true; }),
std::vector<SyncToken>());
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran3);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran4);
}
TEST_F(SchedulerTest, SequenceWaitsForFence) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kHighest);
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id = CommandBufferId::FromUnsafeValue(1);
scoped_refptr<SyncPointClientState> release_state =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id, sequence_id2);
uint64_t release = 1;
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] {
release_state->ReleaseFenceSync(release);
ran2 = true;
}),
std::vector<SyncToken>());
SyncToken sync_token(namespace_id, 0 /* extra_data_field */,
command_buffer_id, release);
bool ran1 = false;
scheduler()->ScheduleTask(sequence_id1, GetClosure([&] { ran1 = true; }),
{sync_token});
task_runner()->RunPendingTasks();
EXPECT_FALSE(ran1);
EXPECT_TRUE(ran2);
EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(sync_token));
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
release_state->Destroy();
}
TEST_F(SchedulerTest, SequenceDoesNotWaitForInvalidFence) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id = CommandBufferId::FromUnsafeValue(1);
scoped_refptr<SyncPointClientState> release_state =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id, sequence_id2);
uint64_t release = 1;
SyncToken sync_token(namespace_id, 0 /* extra_data_field */,
command_buffer_id, release);
bool ran1 = false;
scheduler()->ScheduleTask(sequence_id1, GetClosure([&] { ran1 = true; }),
{sync_token});
// Release task is scheduled after wait task so release is treated as non-
// existent.
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] {
release_state->ReleaseFenceSync(release);
ran2 = true;
}),
std::vector<SyncToken>());
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
EXPECT_FALSE(ran2);
EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(sync_token));
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(sync_token));
release_state->Destroy();
}
TEST_F(SchedulerTest, ReleaseSequenceIsPrioritized) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran1 = false;
scheduler()->ScheduleTask(sequence_id1, GetClosure([&] { ran1 = true; }),
std::vector<SyncToken>());
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kLowest);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id = CommandBufferId::FromUnsafeValue(1);
scoped_refptr<SyncPointClientState> release_state =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id, sequence_id2);
uint64_t release = 1;
bool ran2 = false;
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] {
release_state->ReleaseFenceSync(release);
ran2 = true;
}),
std::vector<SyncToken>());
bool ran3 = false;
SyncToken sync_token(namespace_id, 0 /* extra_data_field */,
command_buffer_id, release);
SequenceId sequence_id3 =
scheduler()->CreateSequence(SchedulingPriority::kHighest);
scheduler()->ScheduleTask(sequence_id3, GetClosure([&] { ran3 = true; }),
{sync_token});
task_runner()->RunPendingTasks();
EXPECT_FALSE(ran1);
EXPECT_TRUE(ran2);
EXPECT_FALSE(ran3);
EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(sync_token));
task_runner()->RunPendingTasks();
EXPECT_FALSE(ran1);
EXPECT_TRUE(ran3);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
release_state->Destroy();
}
TEST_F(SchedulerTest, ReleaseSequenceShouldYield) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kLowest);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id = CommandBufferId::FromUnsafeValue(1);
scoped_refptr<SyncPointClientState> release_state =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id, sequence_id1);
uint64_t release = 1;
bool ran1 = false;
scheduler()->ScheduleTask(
sequence_id1, GetClosure([&] {
EXPECT_FALSE(scheduler()->ShouldYield(sequence_id1));
release_state->ReleaseFenceSync(release);
EXPECT_TRUE(scheduler()->ShouldYield(sequence_id1));
ran1 = true;
}),
std::vector<SyncToken>());
bool ran2 = false;
SyncToken sync_token(namespace_id, 0 /* extra_data_field */,
command_buffer_id, release);
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kHighest);
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] { ran2 = true; }),
{sync_token});
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
EXPECT_FALSE(ran2);
EXPECT_TRUE(sync_point_manager()->IsSyncTokenReleased(sync_token));
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
release_state->Destroy();
}
TEST_F(SchedulerTest, ReentrantEnableSequenceShouldNotDeadlock) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kHigh);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id1 = CommandBufferId::FromUnsafeValue(1);
scoped_refptr<SyncPointClientState> release_state1 =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id1, sequence_id1);
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
CommandBufferId command_buffer_id2 = CommandBufferId::FromUnsafeValue(2);
scoped_refptr<SyncPointClientState> release_state2 =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id2, sequence_id2);
uint64_t release = 1;
SyncToken sync_token(namespace_id, 0 /* extra_data_field */,
command_buffer_id2, release);
bool ran1, ran2 = false;
// Schedule task on sequence 2 first so that the sync token wait isn't a nop.
// BeginProcessingOrderNumber for this task will run the EnableSequence
// callback. This should not deadlock.
scheduler()->ScheduleTask(sequence_id2, GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>());
// This will run first because of the higher priority and no scheduling sync
// token dependencies.
scheduler()->ScheduleTask(
sequence_id1, GetClosure([&] {
ran1 = true;
release_state1->Wait(
sync_token,
base::Bind(&Scheduler::EnableSequence,
base::Unretained(scheduler()), sequence_id1));
scheduler()->DisableSequence(sequence_id1);
}),
std::vector<SyncToken>());
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
EXPECT_FALSE(ran2);
EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(sync_token));
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(sync_token));
release_state1->Destroy();
release_state2->Destroy();
}
TEST_F(SchedulerTest, WaitOnSelfShouldNotBlockSequence) {
SequenceId sequence_id =
scheduler()->CreateSequence(SchedulingPriority::kHigh);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id = CommandBufferId::FromUnsafeValue(1);
scoped_refptr<SyncPointClientState> release_state =
sync_point_manager()->CreateSyncPointClientState(
namespace_id, command_buffer_id, sequence_id);
// Dummy order number to avoid the wait_order_num <= processed_order_num + 1
// check in SyncPointOrderData::ValidateReleaseOrderNum.
sync_point_manager()->GenerateOrderNumber();
uint64_t release = 1;
SyncToken sync_token(namespace_id, 0 /* extra_data_field */,
command_buffer_id, release);
bool ran = false;
scheduler()->ScheduleTask(sequence_id, GetClosure([&]() { ran = true; }),
{sync_token});
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran);
EXPECT_FALSE(sync_point_manager()->IsSyncTokenReleased(sync_token));
release_state->Destroy();
}
} // namespace
} // namespace gpu