| // Copyright 2024 The Chromium Authors |
| // 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/blocking_sequence_runner.h" |
| |
| #include "base/synchronization/lock.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/trace_event/trace_event.h" |
| #include "gpu/command_buffer/service/scheduler.h" |
| |
| namespace gpu { |
| |
| BlockingSequenceRunner::BlockingSequenceRunner(Scheduler* scheduler) |
| : task_graph_(scheduler->task_graph()) { |
| auto sequence = std::make_unique<Sequence>(scheduler); |
| sequence_ = sequence.get(); |
| |
| task_graph_->AddSequence(std::move(sequence)); |
| } |
| |
| BlockingSequenceRunner::~BlockingSequenceRunner() { |
| SequenceId id = GetSequenceId(); |
| sequence_ = nullptr; |
| task_graph_->DestroySequence(id); |
| } |
| |
| SequenceId BlockingSequenceRunner::GetSequenceId() const { |
| return sequence_->sequence_id(); |
| } |
| |
| bool BlockingSequenceRunner::HasTasks() const { |
| base::AutoLock locker(lock()); |
| return sequence_->HasTasks(); |
| } |
| |
| uint32_t BlockingSequenceRunner::AddTask(TaskCallback task_callback, |
| std::vector<SyncToken> wait_fences, |
| const SyncToken& release, |
| ReportingCallback report_callback) { |
| base::AutoLock auto_lock(lock()); |
| return sequence_->AddTask(std::move(task_callback), std::move(wait_fences), |
| release, std::move(report_callback)); |
| } |
| |
| uint32_t BlockingSequenceRunner::AddTask(base::OnceClosure task_closure, |
| std::vector<SyncToken> wait_fences, |
| const SyncToken& release, |
| ReportingCallback report_callback) { |
| base::AutoLock auto_lock(lock()); |
| return sequence_->AddTask(std::move(task_closure), std::move(wait_fences), |
| release, std::move(report_callback)); |
| } |
| |
| ScopedSyncPointClientState BlockingSequenceRunner::CreateSyncPointClientState( |
| CommandBufferNamespace namespace_id, |
| CommandBufferId command_buffer_id) { |
| base::AutoLock auto_lock(lock()); |
| return sequence_->CreateSyncPointClientState(namespace_id, command_buffer_id); |
| } |
| |
| void BlockingSequenceRunner::RunAllTasks() { |
| base::AutoLock auto_lock(lock()); |
| sequence_->RunAllTasks(); |
| } |
| |
| BlockingSequenceRunner::Sequence::Sequence(Scheduler* scheduler) |
| : TaskGraph::Sequence(scheduler->task_graph(), |
| /*validation_runner=*/{}), |
| scheduler_(scheduler) {} |
| |
| void BlockingSequenceRunner::Sequence::RunAllTasks() { |
| while (!tasks_.empty()) { |
| // Synchronously wait for the fences of the front task. |
| while (!IsFrontTaskUnblocked()) { |
| gpu::SyncToken sync_token = wait_fences_.begin()->sync_token; |
| uint32_t order_num = wait_fences_.begin()->order_num; |
| gpu::SequenceId release_sequence_id = |
| wait_fences_.begin()->release_sequence_id; |
| |
| // Must unlock the task graph lock, otherwise it will deadlock when |
| // calling into scheduler to update sequence priority, or when blocking on |
| // `completion` waiting for other tasks to release fences. |
| // |
| // Manually release and re-acquire the lock, because locking annotation |
| // used on ValidateSequenceTaskFenceDeps() doesn't recognize |
| // base::AutoUnlock. |
| lock().Release(); |
| |
| base::WaitableEvent completion; |
| if (task_graph_->sync_point_manager()->Wait( |
| sync_token, sequence_id_, order_num, |
| base::BindOnce(&base::WaitableEvent::Signal, |
| base::Unretained(&completion)))) { |
| TRACE_EVENT1( |
| "gpu", |
| "BlockingSequenceRunner::Sequence::RunAllTasks::WaitSyncToken", |
| "sequence_id", release_sequence_id.value()); |
| gpu::Scheduler::ScopedSetSequencePriority waiting( |
| scheduler_, release_sequence_id, gpu::SchedulingPriority::kHigh); |
| |
| if (task_graph_->graph_validation_enabled()) { |
| while (!completion.TimedWait(gpu::TaskGraph::kMinValidationDelay)) { |
| task_graph_->ValidateSequenceTaskFenceDeps(this); |
| } |
| } else { |
| completion.Wait(); |
| } |
| } |
| |
| lock().Acquire(); |
| } |
| |
| // Run the front task. |
| base::OnceClosure task_closure; |
| uint32_t order_num = BeginTask(&task_closure); |
| gpu::SyncToken release = current_task_release_; |
| |
| { |
| base::AutoUnlock auto_unlock(lock()); |
| order_data()->BeginProcessingOrderNumber(order_num); |
| |
| std::move(task_closure).Run(); |
| |
| if (order_data()->IsProcessingOrderNumber()) { |
| if (release.HasData()) { |
| task_graph_->sync_point_manager()->EnsureFenceSyncReleased( |
| release, gpu::ReleaseCause::kTaskCompletionRelease); |
| } |
| |
| order_data()->FinishProcessingOrderNumber(order_num); |
| } |
| } |
| |
| FinishTask(); |
| } |
| } |
| |
| } // namespace gpu |