blob: 96d34b6b0c853b423453ca05e4d81e048430aefb [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/test/test_simple_task_runner.h"
#include "gpu/command_buffer/service/sync_point_manager.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gpu {
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(Scheduler::Task(
sequence_id, GetClosure([&] { ran1 = true; }), std::vector<SyncToken>()));
bool ran2 = false;
scheduler()->ScheduleTask(Scheduler::Task(
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(Scheduler::Task(
sequence_id, GetClosure([&] {
scheduler()->ContinueTask(sequence_id,
GetClosure([&] { continued1 = true; }));
ran1 = true;
}),
std::vector<SyncToken>()));
bool ran2 = false;
scheduler()->ScheduleTask(Scheduler::Task(
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);
}
class SchedulerTaskRunOrderTest : public SchedulerTest {
public:
SchedulerTaskRunOrderTest() = default;
~SchedulerTaskRunOrderTest() override {
for (auto info_it : sequence_info_) {
info_it.second.release_state->Destroy();
scheduler()->DestroySequence(info_it.second.sequence_id);
}
}
protected:
void CreateSequence(int sequence_key, SchedulingPriority priority) {
SequenceId sequence_id = scheduler()->CreateSequence(priority);
CommandBufferId command_buffer_id =
CommandBufferId::FromUnsafeValue(sequence_key);
scoped_refptr<SyncPointClientState> release_state =
sync_point_manager()->CreateSyncPointClientState(
kNamespaceId, command_buffer_id, sequence_id);
sequence_info_.emplace(std::make_pair(
sequence_key,
SequenceInfo(sequence_id, command_buffer_id, release_state)));
}
void DestroySequence(int sequence_key) {
auto info_it = sequence_info_.find(sequence_key);
ASSERT_TRUE(info_it != sequence_info_.end());
info_it->second.release_state->Destroy();
scheduler()->DestroySequence(info_it->second.sequence_id);
sequence_info_.erase(info_it);
}
void CreateSyncToken(int sequence_key, int release_sync) {
auto info_it = sequence_info_.find(sequence_key);
ASSERT_TRUE(info_it != sequence_info_.end());
uint64_t release = release_sync + 1;
sync_tokens_.emplace(std::make_pair(
release_sync,
SyncToken(kNamespaceId, info_it->second.command_buffer_id, release)));
}
void ScheduleTask(int sequence_key, int wait_sync, int release_sync) {
const int task_id = num_tasks_scheduled_++;
std::vector<SyncToken> wait;
if (wait_sync >= 0) {
wait.push_back(sync_tokens_[wait_sync]);
}
uint64_t release = 0;
if (release_sync >= 0) {
CreateSyncToken(sequence_key, release_sync);
release = release_sync + 1;
}
auto info_it = sequence_info_.find(sequence_key);
ASSERT_TRUE(info_it != sequence_info_.end());
scheduler()->ScheduleTask(Scheduler::Task(
info_it->second.sequence_id,
GetClosure([this, task_id, sequence_key, release] {
if (release) {
auto info_it = sequence_info_.find(sequence_key);
ASSERT_TRUE(info_it != sequence_info_.end());
info_it->second.release_state->ReleaseFenceSync(release);
}
this->tasks_executed_.push_back(task_id);
}),
wait));
}
void RunAllPendingTasks() {
while (task_runner()->HasPendingTask())
task_runner()->RunPendingTasks();
}
const std::vector<int>& tasks_executed() { return tasks_executed_; }
private:
const CommandBufferNamespace kNamespaceId = CommandBufferNamespace::GPU_IO;
int num_tasks_scheduled_ = 0;
struct SequenceInfo {
SequenceInfo(SequenceId sequence_id,
CommandBufferId command_buffer_id,
scoped_refptr<SyncPointClientState> release_state)
: sequence_id(sequence_id),
command_buffer_id(command_buffer_id),
release_state(release_state) {}
SequenceId sequence_id;
CommandBufferId command_buffer_id;
scoped_refptr<SyncPointClientState> release_state;
};
std::map<int, const SequenceInfo> sequence_info_;
std::map<int, const SyncToken> sync_tokens_;
std::vector<int> tasks_executed_;
};
TEST_F(SchedulerTaskRunOrderTest, SequencesRunInPriorityOrder) {
CreateSequence(0, SchedulingPriority::kLow);
CreateSequence(1, SchedulingPriority::kNormal);
CreateSequence(2, SchedulingPriority::kHigh);
ScheduleTask(0, -1, -1); // task 0: seq 0, no wait, no release
ScheduleTask(1, -1, -1); // task 1: seq 1, no wait, no release
ScheduleTask(2, -1, -1); // task 2: seq 2, no wait, no release
RunAllPendingTasks();
const int expected_task_order[] = {2, 1, 0};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, SequencesOfSamePriorityRunInOrder) {
CreateSequence(0, SchedulingPriority::kNormal);
CreateSequence(1, SchedulingPriority::kNormal);
CreateSequence(2, SchedulingPriority::kNormal);
CreateSequence(3, SchedulingPriority::kNormal);
ScheduleTask(0, -1, -1); // task 0: seq 0, no wait, no release
ScheduleTask(1, -1, -1); // task 1: seq 1, no wait, no release
ScheduleTask(2, -1, -1); // task 2: seq 2, no wait, no release
ScheduleTask(3, -1, -1); // task 3: seq 2, no wait, no release
RunAllPendingTasks();
const int expected_task_order[] = {0, 1, 2, 3};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, SequenceWaitsForFence) {
CreateSequence(0, SchedulingPriority::kHigh);
CreateSequence(1, SchedulingPriority::kNormal);
ScheduleTask(1, -1, 0); // task 0: seq 1, no wait, release 0
ScheduleTask(0, 0, -1); // task 1: seq 0, wait 0, no release
RunAllPendingTasks();
const int expected_task_order[] = {0, 1};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, SequenceDoesNotWaitForInvalidFence) {
CreateSequence(0, SchedulingPriority::kNormal);
CreateSequence(1, SchedulingPriority::kNormal);
CreateSyncToken(1, 0); // declare sync_token 0 on seq 1
ScheduleTask(0, 0, -1); // task 0: seq 0, wait 0, no release
ScheduleTask(1, -1, 0); // task 1: seq 1, no wait, release 0
RunAllPendingTasks();
// Task 0 does not wait on unrelease sync token 0.
const int expected_task_order[] = {0, 1};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, ReleaseSequenceIsPrioritized) {
CreateSequence(0, SchedulingPriority::kNormal);
CreateSequence(1, SchedulingPriority::kLow);
CreateSequence(2, SchedulingPriority::kHigh);
ScheduleTask(0, -1, -1); // task 0: seq 0, no wait, no release
ScheduleTask(1, -1, 0); // task 1: seq 1, no wait, release 0
ScheduleTask(2, 0, -1); // task 2: seq 2, wait 0, no release
RunAllPendingTasks();
const int expected_task_order[] = {1, 2, 0};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, ReleaseSequenceHasPriorityOfWaiter) {
CreateSequence(0, SchedulingPriority::kLow);
CreateSequence(1, SchedulingPriority::kNormal);
CreateSequence(2, SchedulingPriority::kHigh);
ScheduleTask(0, -1, 0); // task 0: seq 0, no wait, release 0
ScheduleTask(1, 0, -1); // task 1: seq 1, wait 0, no release
ScheduleTask(2, -1, -1); // task 2: seq 2, no wait, no release
RunAllPendingTasks();
const int expected_task_order[] = {2, 0, 1};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, ReleaseSequenceRevertsToDefaultPriority) {
CreateSequence(0, SchedulingPriority::kNormal);
CreateSequence(1, SchedulingPriority::kLow);
CreateSequence(2, SchedulingPriority::kHigh);
ScheduleTask(0, -1, -1); // task 0: seq 0, no wait, no release
ScheduleTask(1, -1, 0); // task 1: seq 1, no wait, release 0
ScheduleTask(2, 0, -1); // task 2: seq 2, wait 0, no release
DestroySequence(2);
RunAllPendingTasks();
const int expected_task_order[] = {0, 1};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, ReleaseSequenceCircularRelease) {
CreateSequence(0, SchedulingPriority::kLow);
CreateSequence(1, SchedulingPriority::kNormal);
CreateSequence(2, SchedulingPriority::kHigh);
ScheduleTask(0, -1, -1); // task 0: seq 0, no wait, no release
ScheduleTask(1, -1, -1); // task 1: seq 1, no wait, no release
ScheduleTask(2, -1, -1); // task 2: seq 2, no wait, no release
ScheduleTask(0, -1, 0); // task 3: seq 0, no wait, release 0
ScheduleTask(0, -1, -1); // task 4: seq 0, no wait, no release
ScheduleTask(1, 0, 1); // task 5: seq 1, wait 0, release 1
ScheduleTask(1, -1, -1); // task 6: seq 1, no wait, no release
ScheduleTask(2, 1, 2); // task 7: seq 2, wait 1, release 2
ScheduleTask(2, -1, -1); // task 8: seq 2, no wait, no release
ScheduleTask(0, 2, 3); // task 9: seq 0, wait 2, releases 3
ScheduleTask(1, 3, 4); // task 10: seq 1, wait 3, releases 4
ScheduleTask(2, 4, -1); // task 11: seq 2, wait 4, no release
ScheduleTask(0, -1, -1); // task 12: seq 0, no wait, no release
ScheduleTask(1, -1, -1); // task 13: seq 1, no wait, no release
ScheduleTask(2, -1, -1); // task 14: seq 2, no wait, no release
RunAllPendingTasks();
const int expected_task_order[] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 14, 13, 12};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTaskRunOrderTest, WaitOnSelfShouldNotBlockSequence) {
CreateSequence(0, SchedulingPriority::kHigh);
CreateSyncToken(0, 0); // declare sync_token 0 on seq 1
// Dummy order number to avoid the wait_order_num <= processed_order_num + 1
// check in SyncPointOrderData::ValidateReleaseOrderNum.
sync_point_manager()->GenerateOrderNumber();
ScheduleTask(0, 0, -1); // task 0: seq 0, wait 0, no release
RunAllPendingTasks();
const int expected_task_order[] = {0};
EXPECT_THAT(tasks_executed(), testing::ElementsAreArray(expected_task_order));
}
TEST_F(SchedulerTest, ReleaseSequenceShouldYield) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kLow);
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(
Scheduler::Task(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, command_buffer_id, release);
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kHigh);
scheduler()->ScheduleTask(Scheduler::Task(
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, 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(Scheduler::Task(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(Scheduler::Task(
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, ClientWaitIsPrioritized) {
SequenceId sequence_id1 =
scheduler()->CreateSequence(SchedulingPriority::kNormal);
bool ran1 = false;
scheduler()->ScheduleTask(Scheduler::Task(sequence_id1,
GetClosure([&] { ran1 = true; }),
std::vector<SyncToken>()));
SequenceId sequence_id2 =
scheduler()->CreateSequence(SchedulingPriority::kLow);
CommandBufferId command_buffer_id = CommandBufferId::FromUnsafeValue(1);
bool ran2 = false;
scheduler()->ScheduleTask(Scheduler::Task(sequence_id2,
GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>()));
bool ran3 = false;
SequenceId sequence_id3 =
scheduler()->CreateSequence(SchedulingPriority::kHigh);
scheduler()->ScheduleTask(Scheduler::Task(sequence_id3,
GetClosure([&] { ran3 = true; }),
std::vector<SyncToken>()));
scheduler()->RaisePriorityForClientWait(sequence_id2, command_buffer_id);
task_runner()->RunPendingTasks();
EXPECT_FALSE(ran1);
EXPECT_TRUE(ran2);
EXPECT_FALSE(ran3);
task_runner()->RunPendingTasks();
EXPECT_FALSE(ran1);
EXPECT_TRUE(ran3);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
ran1 = ran2 = ran3 = false;
scheduler()->ScheduleTask(Scheduler::Task(sequence_id1,
GetClosure([&] { ran1 = true; }),
std::vector<SyncToken>()));
scheduler()->ScheduleTask(Scheduler::Task(sequence_id2,
GetClosure([&] { ran2 = true; }),
std::vector<SyncToken>()));
scheduler()->ScheduleTask(Scheduler::Task(sequence_id3,
GetClosure([&] { ran3 = true; }),
std::vector<SyncToken>()));
scheduler()->ResetPriorityForClientWait(sequence_id2, command_buffer_id);
task_runner()->RunPendingTasks();
EXPECT_FALSE(ran1);
EXPECT_FALSE(ran2);
EXPECT_TRUE(ran3);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran1);
EXPECT_FALSE(ran2);
task_runner()->RunPendingTasks();
EXPECT_TRUE(ran2);
}
TEST_F(SchedulerTest, StreamPriorities) {
SequenceId seq_id1 = scheduler()->CreateSequence(SchedulingPriority::kLow);
SequenceId seq_id2 = scheduler()->CreateSequence(SchedulingPriority::kNormal);
SequenceId seq_id3 = scheduler()->CreateSequence(SchedulingPriority::kHigh);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id1 = CommandBufferId::FromUnsafeValue(1);
CommandBufferId command_buffer_id2 = CommandBufferId::FromUnsafeValue(2);
base::AutoLock auto_lock(scheduler()->lock_);
Scheduler::Sequence* seq1 = scheduler()->GetSequence(seq_id1);
Scheduler::Sequence* seq2 = scheduler()->GetSequence(seq_id2);
Scheduler::Sequence* seq3 = scheduler()->GetSequence(seq_id3);
// Initial default priorities.
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
SyncToken sync_token1(namespace_id, command_buffer_id1, 1);
SyncToken sync_token2(namespace_id, command_buffer_id2, 1);
// Wait priorities propagate.
seq2->AddWaitFence(sync_token1, 1, seq_id1, seq1);
EXPECT_EQ(SchedulingPriority::kNormal, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
seq2->AddWaitFence(sync_token1, 1, seq_id1, seq1);
EXPECT_EQ(SchedulingPriority::kNormal, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
seq3->AddWaitFence(sync_token2, 2, seq_id2, seq2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
// Release priority propagate.
seq2->RemoveWaitFence(sync_token1, 1, seq_id1);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
seq2->RemoveWaitFence(sync_token1, 1, seq_id1);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
seq3->RemoveWaitFence(sync_token2, 2, seq_id2);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
{
base::AutoUnlock auto_unlock(scheduler()->lock_);
scheduler()->DestroySequence(seq_id1);
scheduler()->DestroySequence(seq_id2);
scheduler()->DestroySequence(seq_id3);
}
}
TEST_F(SchedulerTest, StreamDestroyRemovesPriorities) {
SequenceId seq_id1 = scheduler()->CreateSequence(SchedulingPriority::kLow);
SequenceId seq_id2 = scheduler()->CreateSequence(SchedulingPriority::kNormal);
SequenceId seq_id3 = scheduler()->CreateSequence(SchedulingPriority::kHigh);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id1 = CommandBufferId::FromUnsafeValue(1);
CommandBufferId command_buffer_id2 = CommandBufferId::FromUnsafeValue(2);
base::AutoLock auto_lock(scheduler()->lock_);
Scheduler::Sequence* seq1 = scheduler()->GetSequence(seq_id1);
Scheduler::Sequence* seq2 = scheduler()->GetSequence(seq_id2);
Scheduler::Sequence* seq3 = scheduler()->GetSequence(seq_id3);
SyncToken sync_token1(namespace_id, command_buffer_id1, 1);
SyncToken sync_token2(namespace_id, command_buffer_id2, 1);
// Wait priorities propagate.
seq2->AddWaitFence(sync_token1, 1, seq_id1, seq1);
seq2->AddWaitFence(sync_token1, 1, seq_id1, seq1);
seq3->AddWaitFence(sync_token2, 2, seq_id2, seq2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
// Deleting waiting sequences removes priorities.
{
base::AutoUnlock auto_unlock(scheduler()->lock_);
scheduler()->DestroySequence(seq_id3);
}
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
{
base::AutoUnlock auto_unlock(scheduler()->lock_);
scheduler()->DestroySequence(seq_id2);
}
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
{
base::AutoUnlock auto_unlock(scheduler()->lock_);
scheduler()->DestroySequence(seq_id1);
}
}
// crbug.com/781585#5: Test RemoveWait/AddWait/RemoveWait sequence.
TEST_F(SchedulerTest, StreamPriorityChangeWhileReleasing) {
SequenceId seq_id1 = scheduler()->CreateSequence(SchedulingPriority::kLow);
SequenceId seq_id2 = scheduler()->CreateSequence(SchedulingPriority::kNormal);
SequenceId seq_id3 = scheduler()->CreateSequence(SchedulingPriority::kHigh);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id1 = CommandBufferId::FromUnsafeValue(1);
CommandBufferId command_buffer_id2 = CommandBufferId::FromUnsafeValue(2);
base::AutoLock auto_lock(scheduler()->lock_);
Scheduler::Sequence* seq1 = scheduler()->GetSequence(seq_id1);
Scheduler::Sequence* seq2 = scheduler()->GetSequence(seq_id2);
Scheduler::Sequence* seq3 = scheduler()->GetSequence(seq_id3);
SyncToken sync_token1(namespace_id, command_buffer_id1, 1);
SyncToken sync_token2(namespace_id, command_buffer_id2, 2);
// Wait on same fence multiple times.
seq2->AddWaitFence(sync_token1, 1, seq_id1, seq1);
seq2->AddWaitFence(sync_token1, 1, seq_id1, seq1);
EXPECT_EQ(SchedulingPriority::kNormal, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
// All matching wait fences are removed together.
seq2->RemoveWaitFence(sync_token1, 1, seq_id1);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
// Add wait fence with higher priority. This replicates a possible race.
seq3->AddWaitFence(sync_token2, 2, seq_id2, seq2);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
// This should be a No-op.
seq2->RemoveWaitFence(sync_token1, 1, seq_id1);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
seq3->RemoveWaitFence(sync_token2, 2, seq_id2);
EXPECT_EQ(SchedulingPriority::kLow, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq3->current_priority());
{
base::AutoUnlock auto_unlock(scheduler()->lock_);
scheduler()->DestroySequence(seq_id1);
scheduler()->DestroySequence(seq_id2);
scheduler()->DestroySequence(seq_id3);
}
}
TEST_F(SchedulerTest, CircularPriorities) {
SequenceId seq_id1 = scheduler()->CreateSequence(SchedulingPriority::kHigh);
SequenceId seq_id2 = scheduler()->CreateSequence(SchedulingPriority::kLow);
SequenceId seq_id3 = scheduler()->CreateSequence(SchedulingPriority::kNormal);
CommandBufferNamespace namespace_id = CommandBufferNamespace::GPU_IO;
CommandBufferId command_buffer_id2 = CommandBufferId::FromUnsafeValue(2);
CommandBufferId command_buffer_id3 = CommandBufferId::FromUnsafeValue(3);
base::AutoLock auto_lock(scheduler()->lock_);
Scheduler::Sequence* seq1 = scheduler()->GetSequence(seq_id1);
Scheduler::Sequence* seq2 = scheduler()->GetSequence(seq_id2);
Scheduler::Sequence* seq3 = scheduler()->GetSequence(seq_id3);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kLow, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
SyncToken sync_token_seq2_1(namespace_id, command_buffer_id2, 1);
SyncToken sync_token_seq2_2(namespace_id, command_buffer_id2, 2);
SyncToken sync_token_seq2_3(namespace_id, command_buffer_id2, 3);
SyncToken sync_token_seq3_1(namespace_id, command_buffer_id3, 1);
seq3->AddWaitFence(sync_token_seq2_1, 1, seq_id2, seq2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
seq1->AddWaitFence(sync_token_seq2_2, 2, seq_id2, seq2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
seq3->AddWaitFence(sync_token_seq2_3, 3, seq_id2, seq2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
seq2->AddWaitFence(sync_token_seq3_1, 4, seq_id3, seq3);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
seq3->RemoveWaitFence(sync_token_seq2_1, 1, seq_id2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kHigh, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
seq1->RemoveWaitFence(sync_token_seq2_2, 2, seq_id2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
seq3->RemoveWaitFence(sync_token_seq2_3, 3, seq_id2);
EXPECT_EQ(SchedulingPriority::kHigh, seq1->current_priority());
EXPECT_EQ(SchedulingPriority::kLow, seq2->current_priority());
EXPECT_EQ(SchedulingPriority::kNormal, seq3->current_priority());
{
base::AutoUnlock auto_unlock(scheduler()->lock_);
scheduler()->DestroySequence(seq_id1);
scheduler()->DestroySequence(seq_id2);
scheduler()->DestroySequence(seq_id3);
}
}
} // namespace gpu