| Index: gpu/command_buffer/service/scheduler.cc
|
| diff --git a/gpu/command_buffer/service/scheduler.cc b/gpu/command_buffer/service/scheduler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..86ee2c958c4822952b88e8ab05017e5b7f64fec7
|
| --- /dev/null
|
| +++ b/gpu/command_buffer/service/scheduler.cc
|
| @@ -0,0 +1,503 @@
|
| +// 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/callback.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/trace_event/trace_event.h"
|
| +#include "base/trace_event/trace_event_argument.h"
|
| +#include "gpu/command_buffer/service/sync_point_manager.h"
|
| +
|
| +namespace gpu {
|
| +
|
| +class Scheduler::Sequence {
|
| + public:
|
| + Sequence(SequenceId sequence_id,
|
| + GpuStreamPriority priority,
|
| + scoped_refptr<SyncPointOrderData> order_data);
|
| +
|
| + ~Sequence();
|
| +
|
| + void Destroy();
|
| +
|
| + bool destroyed() const { return destroyed_; }
|
| +
|
| + SequenceId sequence_id() const { return sequence_id_; }
|
| +
|
| + const SchedulingState& scheduling_state() const { return scheduling_state_; }
|
| +
|
| + bool enabled() const { return enabled_; }
|
| +
|
| + bool scheduled() const { return running_state_ == SCHEDULED; }
|
| +
|
| + bool running() const { return running_state_ == RUNNING; }
|
| +
|
| + // The sequence is runnable if its enabled and has tasks which are not blocked
|
| + // by wait fences.
|
| + bool IsRunnable() const;
|
| +
|
| + bool NeedsRescheduling() const;
|
| +
|
| + void UpdateSchedulingState();
|
| +
|
| + // If this sequence runs before the other sequence.
|
| + bool RunsBefore(const Sequence* other) const;
|
| +
|
| + void SetEnabled(bool enabled);
|
| +
|
| + // Sets running state to SCHEDULED.
|
| + void SetScheduled();
|
| +
|
| + // Called before running the next task on the sequence. Returns the closure
|
| + // for the task. Sets running state to RUNNING.
|
| + base::OnceClosure BeginTask();
|
| +
|
| + // Called after running the closure returned by BeginTask. Sets running state
|
| + // to IDLE.
|
| + void FinishTask();
|
| +
|
| + // Enqueues a task in the sequence and returns the generated order number.
|
| + uint32_t ScheduleTask(base::OnceClosure closure);
|
| +
|
| + // Continue running the current task with the given closure. Must be called in
|
| + // between |BeginTask| and |FinishTask|.
|
| + void ContinueTask(base::OnceClosure closure);
|
| +
|
| + // Add a sync token fence that this sequence should wait on.
|
| + void AddWaitFence(const SyncToken& sync_token, uint32_t order_num);
|
| +
|
| + // Remove a waiting sync token fence.
|
| + void RemoveWaitFence(const SyncToken& sync_token, uint32_t order_num);
|
| +
|
| + // Add a sync token fence that this sequence is expected to release.
|
| + void AddReleaseFence(const SyncToken& sync_token, uint32_t order_num);
|
| +
|
| + // Remove a release sync token fence.
|
| + void RemoveReleaseFence(const SyncToken& sync_token, uint32_t order_num);
|
| +
|
| + private:
|
| + enum RunningState { IDLE, SCHEDULED, RUNNING };
|
| +
|
| + struct Fence {
|
| + SyncToken sync_token;
|
| + uint32_t order_num;
|
| +
|
| + bool operator==(const Fence& other) const {
|
| + return std::tie(sync_token, order_num) ==
|
| + std::tie(other.sync_token, other.order_num);
|
| + }
|
| + };
|
| +
|
| + struct Task {
|
| + base::OnceClosure closure;
|
| + uint32_t order_num;
|
| + };
|
| +
|
| + GpuStreamPriority GetSchedulingPriority() const;
|
| +
|
| + bool destroyed_ = false;
|
| +
|
| + // If the sequence is enabled. Sequences are disabled/enabled based on when
|
| + // the command buffer is descheduled/sc
|
| + bool enabled_ = true;
|
| +
|
| + RunningState running_state_;
|
| +
|
| + // Cached scheduling state used for comparison with other sequences using
|
| + // |RunsBefore|. Updated in |UpdateSchedulingState|.
|
| + SchedulingState scheduling_state_;
|
| +
|
| + const SequenceId sequence_id_;
|
| +
|
| + const GpuStreamPriority priority_;
|
| +
|
| + scoped_refptr<SyncPointOrderData> order_data_;
|
| +
|
| + // Deque of tasks. Tasks are inserted at the back with increasing order number
|
| + // generated from SyncPointOrderData. If a running task needs to be continued,
|
| + // it is inserted at the front with the same order number.
|
| + std::deque<Task> tasks_;
|
| +
|
| + // List of fences that this sequence is waiting on. Fences are inserted in
|
| + // increasing order number but may be removed out of order. Tasks are blocked
|
| + // if there's a wait fence with order number less than or equal to the task's
|
| + // order number.
|
| + std::vector<Fence> wait_fences_;
|
| +
|
| + // List of fences that this sequence is expected to release. If this list is
|
| + // non-empty, the priority of the sequence is raised.
|
| + std::vector<Fence> release_fences_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Sequence);
|
| +};
|
| +
|
| +Scheduler::SchedulingState::SchedulingState() = default;
|
| +Scheduler::SchedulingState::SchedulingState(const SchedulingState& other) =
|
| + default;
|
| +Scheduler::SchedulingState::~SchedulingState() = default;
|
| +
|
| +std::unique_ptr<base::trace_event::ConvertableToTraceFormat>
|
| +Scheduler::SchedulingState::AsValue() const {
|
| + std::unique_ptr<base::trace_event::TracedValue> state(
|
| + new base::trace_event::TracedValue());
|
| + state->SetInteger("sequence_id", sequence_id.GetUnsafeValue());
|
| + state->SetString("priority", GpuStreamPriorityToString(priority));
|
| + state->SetInteger("order_num", order_num);
|
| + return std::move(state);
|
| +}
|
| +
|
| +Scheduler::Sequence::Sequence(SequenceId sequence_id,
|
| + GpuStreamPriority priority,
|
| + scoped_refptr<SyncPointOrderData> order_data)
|
| + : sequence_id_(sequence_id), priority_(priority), order_data_(order_data) {}
|
| +
|
| +Scheduler::Sequence::~Sequence() = default;
|
| +
|
| +void Scheduler::Sequence::Destroy() {
|
| + DCHECK(!destroyed_);
|
| + destroyed_ = true;
|
| +}
|
| +
|
| +bool Scheduler::Sequence::NeedsRescheduling() const {
|
| + return running_state_ != IDLE &&
|
| + scheduling_state_.priority != GetSchedulingPriority();
|
| +}
|
| +
|
| +bool Scheduler::Sequence::IsRunnable() const {
|
| + return enabled_ && !tasks_.empty() &&
|
| + (wait_fences_.empty() ||
|
| + wait_fences_.front().order_num > tasks_.front().order_num);
|
| +}
|
| +
|
| +GpuStreamPriority Scheduler::Sequence::GetSchedulingPriority() const {
|
| + if (!release_fences_.empty())
|
| + return std::min(priority_, GpuStreamPriority::HIGH);
|
| + return priority_;
|
| +}
|
| +
|
| +bool Scheduler::Sequence::RunsBefore(const Scheduler::Sequence* other) const {
|
| + return other->scheduling_state() < scheduling_state_;
|
| +}
|
| +
|
| +void Scheduler::Sequence::SetEnabled(bool enabled) {
|
| + if (enabled_ == enabled)
|
| + return;
|
| + DCHECK_EQ(running_state_, enabled ? IDLE : RUNNING);
|
| + enabled_ = enabled;
|
| +}
|
| +
|
| +void Scheduler::Sequence::SetScheduled() {
|
| + DCHECK_NE(running_state_, RUNNING);
|
| + running_state_ = SCHEDULED;
|
| + UpdateSchedulingState();
|
| +}
|
| +
|
| +void Scheduler::Sequence::UpdateSchedulingState() {
|
| + scheduling_state_.sequence_id = sequence_id_;
|
| + scheduling_state_.priority = GetSchedulingPriority();
|
| +
|
| + uint32_t order_num = UINT32_MAX; // IDLE
|
| + if (running_state_ == SCHEDULED) {
|
| + DCHECK(!tasks_.empty());
|
| + order_num = tasks_.front().order_num;
|
| + } else if (running_state_ == RUNNING) {
|
| + order_num = order_data_->current_order_num();
|
| + }
|
| + scheduling_state_.order_num = order_num;
|
| +}
|
| +
|
| +void Scheduler::Sequence::ContinueTask(base::OnceClosure closure) {
|
| + DCHECK_EQ(running_state_, RUNNING);
|
| + tasks_.push_front({std::move(closure), order_data_->current_order_num()});
|
| +}
|
| +
|
| +uint32_t Scheduler::Sequence::ScheduleTask(base::OnceClosure closure) {
|
| + uint32_t order_num = order_data_->GenerateUnprocessedOrderNumber();
|
| + tasks_.push_back({std::move(closure), order_num});
|
| + return order_num;
|
| +}
|
| +
|
| +base::OnceClosure Scheduler::Sequence::BeginTask() {
|
| + DCHECK(!tasks_.empty());
|
| +
|
| + DCHECK_EQ(running_state_, SCHEDULED);
|
| + running_state_ = RUNNING;
|
| +
|
| + base::OnceClosure closure = std::move(tasks_.front().closure);
|
| + uint32_t order_num = tasks_.front().order_num;
|
| + tasks_.pop_front();
|
| +
|
| + order_data_->BeginProcessingOrderNumber(order_num);
|
| +
|
| + UpdateSchedulingState();
|
| +
|
| + return closure;
|
| +}
|
| +
|
| +void Scheduler::Sequence::FinishTask() {
|
| + DCHECK_EQ(running_state_, RUNNING);
|
| + running_state_ = IDLE;
|
| + uint32_t order_num = order_data_->current_order_num();
|
| + if (!tasks_.empty() && tasks_.front().order_num == order_num) {
|
| + order_data_->PauseProcessingOrderNumber(order_num);
|
| + } else {
|
| + order_data_->FinishProcessingOrderNumber(order_num);
|
| + }
|
| + UpdateSchedulingState();
|
| +}
|
| +
|
| +void Scheduler::Sequence::AddWaitFence(const SyncToken& sync_token,
|
| + uint32_t order_num) {
|
| + wait_fences_.push_back({sync_token, order_num});
|
| +}
|
| +
|
| +void Scheduler::Sequence::RemoveWaitFence(const SyncToken& sync_token,
|
| + uint32_t order_num) {
|
| + base::Erase(wait_fences_, Fence{sync_token, order_num});
|
| +}
|
| +
|
| +void Scheduler::Sequence::AddReleaseFence(const SyncToken& sync_token,
|
| + uint32_t order_num) {
|
| + release_fences_.push_back({sync_token, order_num});
|
| +}
|
| +
|
| +void Scheduler::Sequence::RemoveReleaseFence(const SyncToken& sync_token,
|
| + uint32_t order_num) {
|
| + base::Erase(release_fences_, Fence{sync_token, order_num});
|
| +}
|
| +
|
| +Scheduler::Scheduler(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
| + SyncPointManager* sync_point_manager)
|
| + : task_runner_(std::move(task_runner)),
|
| + sync_point_manager_(sync_point_manager),
|
| + weak_factory_(this) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| +}
|
| +
|
| +Scheduler::~Scheduler() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| +}
|
| +
|
| +SequenceId Scheduler::CreateSequence(GpuStreamPriority priority) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| + scoped_refptr<SyncPointOrderData> order_data =
|
| + sync_point_manager_->CreateSyncPointOrderData();
|
| + SequenceId sequence_id = order_data->sequence_id();
|
| + std::unique_ptr<Sequence> sequence =
|
| + base::MakeUnique<Sequence>(sequence_id, priority, std::move(order_data));
|
| + sequences_.emplace(sequence_id, std::move(sequence));
|
| + return sequence_id;
|
| +}
|
| +
|
| +void Scheduler::DestroySequence(SequenceId sequence_id) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| +
|
| + Sequence* sequence = GetSequence(sequence_id);
|
| + DCHECK(sequence);
|
| + sequence->Destroy();
|
| +
|
| + if (sequence->running())
|
| + return;
|
| +
|
| + if (sequence->scheduled())
|
| + rebuild_scheduling_queue_ = true;
|
| + sequences_.erase(sequence_id);
|
| +}
|
| +
|
| +Scheduler::Sequence* Scheduler::GetSequence(SequenceId sequence_id) {
|
| + lock_.AssertAcquired();
|
| + auto it = sequences_.find(sequence_id);
|
| + if (it != sequences_.end())
|
| + return it->second.get();
|
| + return nullptr;
|
| +}
|
| +
|
| +void Scheduler::EnableSequence(SequenceId sequence_id) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| + Sequence* sequence = GetSequence(sequence_id);
|
| + DCHECK(sequence);
|
| + sequence->SetEnabled(true);
|
| + TryScheduleSequence(sequence);
|
| +}
|
| +
|
| +void Scheduler::DisableSequence(SequenceId sequence_id) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| + Sequence* sequence = GetSequence(sequence_id);
|
| + DCHECK(sequence);
|
| + sequence->SetEnabled(false);
|
| +}
|
| +
|
| +void Scheduler::ScheduleTask(SequenceId sequence_id,
|
| + base::OnceClosure closure,
|
| + const std::vector<SyncToken>& sync_token_fences) {
|
| + base::AutoLock auto_lock(lock_);
|
| + Sequence* sequence = GetSequence(sequence_id);
|
| + DCHECK(sequence);
|
| +
|
| + uint32_t order_num = sequence->ScheduleTask(std::move(closure));
|
| +
|
| + for (const SyncToken& sync_token : sync_token_fences) {
|
| + SequenceId release_id =
|
| + sync_point_manager_->GetSyncTokenReleaseSequenceId(sync_token);
|
| + Sequence* release_sequence = GetSequence(release_id);
|
| + if (!release_sequence)
|
| + continue;
|
| + if (sync_point_manager_->Wait(
|
| + sync_token, order_num,
|
| + base::Bind(&Scheduler::SyncTokenFenceReleased,
|
| + weak_factory_.GetWeakPtr(), sync_token, order_num,
|
| + release_id, sequence_id))) {
|
| + sequence->AddWaitFence(sync_token, order_num);
|
| + release_sequence->AddReleaseFence(sync_token, order_num);
|
| + TryScheduleSequence(release_sequence);
|
| + }
|
| + }
|
| +
|
| + TryScheduleSequence(sequence);
|
| +}
|
| +
|
| +void Scheduler::ContinueTask(SequenceId sequence_id,
|
| + base::OnceClosure closure) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| + Sequence* sequence = GetSequence(sequence_id);
|
| + DCHECK(sequence);
|
| + sequence->ContinueTask(std::move(closure));
|
| +}
|
| +
|
| +bool Scheduler::ShouldYield(SequenceId sequence_id) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| +
|
| + Sequence* sequence = GetSequence(sequence_id);
|
| + DCHECK(sequence);
|
| + DCHECK(sequence->running());
|
| +
|
| + if (should_yield_)
|
| + return true;
|
| +
|
| + RebuildSchedulingQueue();
|
| +
|
| + sequence->UpdateSchedulingState();
|
| +
|
| + if (!scheduling_queue_.empty()) {
|
| + Sequence* next_sequence = GetSequence(scheduling_queue_.top().sequence_id);
|
| + DCHECK(next_sequence);
|
| + if (next_sequence->RunsBefore(sequence))
|
| + should_yield_ = true;
|
| + }
|
| +
|
| + return should_yield_;
|
| +}
|
| +
|
| +void Scheduler::SyncTokenFenceReleased(const SyncToken& sync_token,
|
| + uint32_t order_num,
|
| + SequenceId release_sequence_id,
|
| + SequenceId waiting_sequence_id) {
|
| + base::AutoLock auto_lock(lock_);
|
| + Sequence* sequence = GetSequence(waiting_sequence_id);
|
| + if (sequence) {
|
| + sequence->RemoveWaitFence(sync_token, order_num);
|
| + TryScheduleSequence(sequence);
|
| + }
|
| + Sequence* release_sequence = GetSequence(release_sequence_id);
|
| + if (release_sequence) {
|
| + release_sequence->RemoveReleaseFence(sync_token, order_num);
|
| + TryScheduleSequence(release_sequence);
|
| + }
|
| +}
|
| +
|
| +void Scheduler::TryScheduleSequence(Sequence* sequence) {
|
| + lock_.AssertAcquired();
|
| +
|
| + if (sequence->running())
|
| + return;
|
| +
|
| + if (sequence->NeedsRescheduling()) {
|
| + DCHECK(sequence->IsRunnable());
|
| + rebuild_scheduling_queue_ = true;
|
| + } else if (!sequence->scheduled() && sequence->IsRunnable()) {
|
| + sequence->SetScheduled();
|
| + scheduling_queue_.push(sequence->scheduling_state());
|
| + }
|
| +
|
| + if (!running_) {
|
| + TRACE_EVENT_ASYNC_BEGIN0("gpu", "Scheduler::Running", this);
|
| + running_ = true;
|
| + task_runner_->PostTask(FROM_HERE, base::Bind(&Scheduler::RunNextTask,
|
| + weak_factory_.GetWeakPtr()));
|
| + }
|
| +}
|
| +
|
| +void Scheduler::RebuildSchedulingQueue() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + lock_.AssertAcquired();
|
| +
|
| + if (!rebuild_scheduling_queue_)
|
| + return;
|
| + rebuild_scheduling_queue_ = false;
|
| +
|
| + std::vector<SchedulingState> states;
|
| + for (const auto& kv : sequences_) {
|
| + Sequence* sequence = kv.second.get();
|
| + if (!sequence->IsRunnable() || sequence->running())
|
| + continue;
|
| + sequence->SetScheduled();
|
| + states.push_back(sequence->scheduling_state());
|
| + }
|
| +
|
| + scheduling_queue_ = SchedulingQueue(states.begin(), states.end());
|
| +}
|
| +
|
| +void Scheduler::RunNextTask() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::AutoLock auto_lock(lock_);
|
| +
|
| + should_yield_ = false;
|
| +
|
| + RebuildSchedulingQueue();
|
| +
|
| + if (scheduling_queue_.empty()) {
|
| + TRACE_EVENT_ASYNC_END0("gpu", "Scheduler::Running", this);
|
| + running_ = false;
|
| + return;
|
| + }
|
| +
|
| + SchedulingState state = scheduling_queue_.top();
|
| + scheduling_queue_.pop();
|
| +
|
| + TRACE_EVENT1("gpu", "Scheduler::RunNextTask", "state", state.AsValue());
|
| +
|
| + Sequence* sequence = GetSequence(state.sequence_id);
|
| + DCHECK(sequence);
|
| +
|
| + base::OnceClosure closure = sequence->BeginTask();
|
| +
|
| + {
|
| + base::AutoUnlock auto_unlock(lock_);
|
| + std::move(closure).Run();
|
| + }
|
| +
|
| + sequence->FinishTask();
|
| +
|
| + if (sequence->destroyed()) {
|
| + sequences_.erase(sequence->sequence_id());
|
| + } else if (sequence->IsRunnable()) {
|
| + sequence->SetScheduled();
|
| + scheduling_queue_.push(sequence->scheduling_state());
|
| + }
|
| +
|
| + task_runner_->PostTask(FROM_HERE, base::Bind(&Scheduler::RunNextTask,
|
| + weak_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +} // namespace gpu
|
|
|