#ifndef GPU_COMMAND_BUFFER_SERVICE_SCHEDULER_H_
#define GPU_COMMAND_BUFFER_SERVICE_SCHEDULER_H_
#include <queue>
#include <vector>
#include "base/containers/circular_deque.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "gpu/command_buffer/common/command_buffer_id.h"
#include "gpu/command_buffer/common/scheduling_priority.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/sequence_id.h"
#include "gpu/gpu_export.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace gpu {
class SyncPointManager;
struct GpuPreferences;
class SchedulerDfs;
class GPU_EXPORT Scheduler {
using ReportingCallback =
base::OnceCallback<void(base::TimeTicks task_ready)>;
public:
struct GPU_EXPORT Task {
Task(SequenceId sequence_id,
base::OnceClosure closure,
std::vector<SyncToken> sync_token_fences,
ReportingCallback report_callback = ReportingCallback());
Task(Task&& other);
~Task();
Task& operator=(Task&& other);
SequenceId sequence_id;
base::OnceClosure closure;
std::vector<SyncToken> sync_token_fences;
ReportingCallback report_callback;
};
struct ScopedAddWaitingPriority {
public:
ScopedAddWaitingPriority(Scheduler* scheduler,
SequenceId sequence_id,
SchedulingPriority priority);
~ScopedAddWaitingPriority();
private:
const raw_ptr<Scheduler> scheduler_;
const SequenceId sequence_id_;
const SchedulingPriority priority_;
};
Scheduler(SyncPointManager* sync_point_manager,
const GpuPreferences& gpu_preferences);
Scheduler(const Scheduler&) = delete;
Scheduler& operator=(const Scheduler&) = delete;
~Scheduler();
SequenceId CreateSequence(
SchedulingPriority priority,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
SequenceId CreateSequenceForTesting(SchedulingPriority priority);
void DestroySequence(SequenceId sequence_id);
void EnableSequence(SequenceId sequence_id);
void DisableSequence(SequenceId sequence_id);
void RaisePriorityForClientWait(SequenceId sequence_id,
CommandBufferId command_buffer_id);
void ResetPriorityForClientWait(SequenceId sequence_id,
CommandBufferId command_buffer_id);
void ScheduleTask(Scheduler::Task task);
void ScheduleTasks(std::vector<Scheduler::Task> tasks);
void ContinueTask(SequenceId sequence_id, base::OnceClosure closure);
bool ShouldYield(SequenceId sequence_id);
base::TimeDelta TakeTotalBlockingTime();
base::SingleThreadTaskRunner* GetTaskRunnerForTesting(SequenceId sequence_id);
SchedulerDfs* GetSchedulerDfsForTesting() { return scheduler_dfs_.get(); }
private:
struct SchedulingState {
static bool Comparator(const SchedulingState& lhs,
const SchedulingState& rhs) {
return rhs.RunsBefore(lhs);
}
SchedulingState();
SchedulingState(const SchedulingState& other);
~SchedulingState();
bool RunsBefore(const SchedulingState& other) const {
return std::tie(priority, order_num) <
std::tie(other.priority, other.order_num);
}
void WriteIntoTrace(perfetto::TracedValue context) const;
SequenceId sequence_id;
SchedulingPriority priority = SchedulingPriority::kLow;
uint32_t order_num = 0;
};
class GPU_EXPORT Sequence {
public:
Sequence(Scheduler* scheduler,
SequenceId sequence_id,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
SchedulingPriority priority,
scoped_refptr<SyncPointOrderData> order_data);
Sequence(const Sequence&) = delete;
Sequence& operator=(const Sequence&) = delete;
~Sequence();
SequenceId sequence_id() const { return sequence_id_; }
const scoped_refptr<SyncPointOrderData>& order_data() const {
return order_data_;
}
base::SingleThreadTaskRunner* task_runner() const {
return task_runner_.get();
}
bool enabled() const { return enabled_; }
bool scheduled() const { return running_state_ == SCHEDULED; }
bool running() const { return running_state_ == RUNNING; }
bool IsRunnable() const;
bool NeedsRescheduling() const;
bool ShouldYieldTo(const Sequence* other) const;
void SetEnabled(bool enabled);
SchedulingState SetScheduled();
void UpdateRunningPriority();
base::TimeDelta FrontTaskWaitingDependencyDelta();
base::TimeDelta FrontTaskSchedulingDelay();
uint32_t BeginTask(base::OnceClosure* closure);
void FinishTask();
uint32_t ScheduleTask(base::OnceClosure closure,
ReportingCallback report_callback);
void ContinueTask(base::OnceClosure closure);
void SetLastTaskFirstDependencyTimeIfNeeded();
void AddWaitFence(const SyncToken& sync_token,
uint32_t order_num,
SequenceId release_sequence_id);
void RemoveWaitFence(const SyncToken& sync_token,
uint32_t order_num,
SequenceId release_sequence_id);
void AddClientWait(CommandBufferId command_buffer_id);
void RemoveClientWait(CommandBufferId command_buffer_id);
SchedulingPriority current_priority() const { return current_priority_; }
private:
friend class Scheduler;
enum RunningState { IDLE, SCHEDULED, RUNNING };
struct WaitFence {
WaitFence(WaitFence&& other);
WaitFence(const SyncToken& sync_token,
uint32_t order_num,
SequenceId release_sequence_id);
~WaitFence();
WaitFence& operator=(WaitFence&& other);
SyncToken sync_token;
uint32_t order_num;
SequenceId release_sequence_id;
bool operator==(const WaitFence& other) const {
return std::tie(order_num, release_sequence_id, sync_token) ==
std::tie(other.order_num, release_sequence_id, other.sync_token);
}
bool operator<(const WaitFence& other) const {
return std::tie(order_num, release_sequence_id, sync_token) <
std::tie(other.order_num, release_sequence_id, other.sync_token);
}
};
struct Task {
Task(Task&& other);
Task(base::OnceClosure closure,
uint32_t order_num,
ReportingCallback report_callback);
~Task();
Task& operator=(Task&& other);
base::OnceClosure closure;
uint32_t order_num;
ReportingCallback report_callback;
base::TimeTicks running_ready = base::TimeTicks::Now();
base::TimeTicks first_dependency_added;
};
void PropagatePriority(SchedulingPriority priority);
void AddWaitingPriority(SchedulingPriority priority);
void RemoveWaitingPriority(SchedulingPriority priority);
void ChangeWaitingPriority(SchedulingPriority old_priority,
SchedulingPriority new_priority);
void UpdateSchedulingPriority();
bool enabled_ = true;
RunningState running_state_ = IDLE;
SchedulingState scheduling_state_;
const raw_ptr<Scheduler> scheduler_;
const SequenceId sequence_id_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
const SchedulingPriority default_priority_;
SchedulingPriority current_priority_;
scoped_refptr<SyncPointOrderData> order_data_;
base::circular_deque<Task> tasks_;
base::flat_map<WaitFence, SchedulingPriority> wait_fences_;
int waiting_priority_counts_[static_cast<int>(SchedulingPriority::kLast) +
1] = {};
base::flat_set<CommandBufferId> client_waits_;
};
void AddWaitingPriority(SequenceId sequence_id, SchedulingPriority priority);
void RemoveWaitingPriority(SequenceId sequence_id,
SchedulingPriority priority);
void SyncTokenFenceReleased(const SyncToken& sync_token,
uint32_t order_num,
SequenceId release_sequence_id,
SequenceId waiting_sequence_id);
void ScheduleTaskHelper(Scheduler::Task task);
void TryScheduleSequence(Sequence* sequence);
std::vector<SchedulingState>& RebuildSchedulingQueueIfNeeded(
base::SingleThreadTaskRunner* task_runner);
Sequence* GetSequence(SequenceId sequence_id);
void RunNextTask();
const raw_ptr<SyncPointManager> sync_point_manager_;
mutable base::Lock lock_;
base::flat_map<SequenceId, std::unique_ptr<Sequence>> sequence_map_
GUARDED_BY(lock_);
struct PerThreadState {
PerThreadState();
PerThreadState(PerThreadState&&);
~PerThreadState();
PerThreadState& operator=(PerThreadState&&);
std::vector<SchedulingState> scheduling_queue;
bool rebuild_scheduling_queue = false;
bool running = false;
base::TimeTicks run_next_task_scheduled;
};
base::flat_map<base::SingleThreadTaskRunner*, PerThreadState>
per_thread_state_map_ GUARDED_BY(lock_);
base::TimeDelta total_blocked_time_ GUARDED_BY(lock_);
const bool blocked_time_collection_enabled_;
std::unique_ptr<SchedulerDfs> scheduler_dfs_;
private:
FRIEND_TEST_ALL_PREFIXES(SchedulerTest, StreamPriorities);
FRIEND_TEST_ALL_PREFIXES(SchedulerTest, StreamDestroyRemovesPriorities);
FRIEND_TEST_ALL_PREFIXES(SchedulerTest, StreamPriorityChangeWhileReleasing);
FRIEND_TEST_ALL_PREFIXES(SchedulerTest, CircularPriorities);
};
}
#endif