blob: 5e62274f4310c455f9648ff814464cd348b09301 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/browser/gfx/task_queue_webview.h"
#include <memory>
#include <utility>
#include "android_webview/browser/gfx/gpu_service_webview.h"
#include "base/auto_reset.h"
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/thread_annotations.h"
#include "base/trace_event/trace_event.h"
#include "gpu/command_buffer/service/blocking_sequence_runner.h"
#include "gpu/command_buffer/service/scheduler.h"
namespace android_webview {
// static
TaskQueueWebView* TaskQueueWebView::GetInstance() {
static TaskQueueWebView* task_queue = new TaskQueueWebView;
return task_queue;
}
TaskQueueWebView::~TaskQueueWebView() {
base::AutoLock lock(lock_);
blocking_sequence_runner_.reset();
}
void TaskQueueWebView::InitializeVizThread(
const scoped_refptr<base::SingleThreadTaskRunner>& viz_task_runner) {
DCHECK(!viz_task_runner_);
viz_task_runner_ = viz_task_runner;
}
void TaskQueueWebView::ScheduleOnVizAndBlock(VizTask viz_task) {
TRACE_EVENT0("android_webview", "ScheduleOnVizAndBlock");
DCHECK_CALLED_ON_VALID_THREAD(render_thread_checker_);
SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
"Android.WebView.ScheduleOnVizAndBlockTime");
// Expected behavior is |viz_task| on the viz thread. From |viz_task| until
// the done closure is called (which may not be in the viz_task), viz thread
// is allowed to call ScheduleTask.
//
// Implementation uses a normal run-loop like logic. The done closure marks
// |done_| true, and run loop exists when |done_| is true *and* the task queue
// is empty. A condition variable is signaled when |done_| is set or when
// something is appended to the task queue.
{
base::AutoLock lock(lock_);
DCHECK(done_);
done_ = false;
}
// Unretained safe because this object is never deleted.
viz_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&TaskQueueWebView::RunOnViz,
base::Unretained(this), std::move(viz_task)));
{
DCHECK(!inside_schedule_on_viz_and_block_);
base::AutoReset<bool> inside_bf(&inside_schedule_on_viz_and_block_, true);
base::AutoLock lock(lock_);
while (!done_ || (blocking_sequence_runner_ &&
blocking_sequence_runner_->HasTasks())) {
while (!done_ && (!blocking_sequence_runner_ ||
!blocking_sequence_runner_->HasTasks())) {
condvar_.Wait();
}
if (blocking_sequence_runner_) {
base::AutoUnlock unlock(lock_);
TRACE_EVENT0("android_webview", "RunTasks");
blocking_sequence_runner_->RunAllTasks();
}
}
DCHECK(done_);
}
}
scoped_refptr<base::SingleThreadTaskRunner>
TaskQueueWebView::GetClientTaskRunner() {
DCHECK(viz_task_runner_);
return viz_task_runner_;
}
void TaskQueueWebView::EnsureSequenceInitialized() {
base::AutoLock lock(lock_);
if (blocking_sequence_runner_) {
return;
}
blocking_sequence_runner_ = std::make_unique<gpu::BlockingSequenceRunner>(
GpuServiceWebView::GetInstance()->scheduler());
}
gpu::SequenceId TaskQueueWebView::GetSequenceId() {
base::AutoLock lock(lock_);
return blocking_sequence_runner_->GetSequenceId();
}
void TaskQueueWebView::ScheduleTask(
gpu::TaskCallback task,
std::vector<gpu::SyncToken> sync_token_fences,
const gpu::SyncToken& release,
gpu::ReportingCallback report_callback) {
TRACE_EVENT0("android_webview", "ScheduleTask");
DCHECK(viz_task_runner_->BelongsToCurrentThread());
DCHECK(allow_schedule_task_);
base::AutoLock lock(lock_);
blocking_sequence_runner_->AddTask(std::move(task),
std::move(sync_token_fences), release,
std::move(report_callback));
condvar_.Signal();
}
void TaskQueueWebView::ScheduleTask(
base::OnceClosure task,
std::vector<gpu::SyncToken> sync_token_fences,
const gpu::SyncToken& release,
gpu::ReportingCallback report_callback) {
TRACE_EVENT0("android_webview", "ScheduleTask");
DCHECK(viz_task_runner_->BelongsToCurrentThread());
DCHECK(allow_schedule_task_);
// The two branches end up doing the exact same thing only because retain
// can use the same task queue. The code says the intention which is
// |ScheduleOrRetainTask| behaves the same as |ScheduleTask| if
// |allow_schedule_task_| is true.
// Sharing the queue makes it clear |ScheduleTask| and
// |ScheduleOrRetainTask| but however has a non-practical risk of
// live-locking the render thread.
ScheduleOrRetainTask(std::move(task), std::move(sync_token_fences), release,
std::move(report_callback));
}
void TaskQueueWebView::ScheduleOrRetainTask(
base::OnceClosure task,
std::vector<gpu::SyncToken> sync_token_fences,
const gpu::SyncToken& release,
gpu::ReportingCallback report_callback) {
DCHECK(viz_task_runner_->BelongsToCurrentThread());
base::AutoLock lock(lock_);
blocking_sequence_runner_->AddTask(std::move(task),
std::move(sync_token_fences), release,
std::move(report_callback));
condvar_.Signal();
}
void TaskQueueWebView::ScheduleIdleTask(base::OnceClosure task) {
DCHECK_CALLED_ON_VALID_THREAD(render_thread_checker_);
DCHECK(inside_schedule_on_viz_and_block_);
base::AutoLock lock(lock_);
blocking_sequence_runner_->AddTask(std::move(task), /*wait_fences=*/{},
gpu::SyncToken(),
/*report_callback=*/{});
condvar_.Signal();
}
gpu::ScopedSyncPointClientState TaskQueueWebView::CreateSyncPointClientState(
gpu::CommandBufferNamespace namespace_id,
gpu::CommandBufferId command_buffer_id) {
base::AutoLock lock(lock_);
return blocking_sequence_runner_->CreateSyncPointClientState(
namespace_id, command_buffer_id);
}
TaskQueueWebView::TaskQueueWebView() {
DETACH_FROM_THREAD(render_thread_checker_);
}
void TaskQueueWebView::RunOnViz(VizTask viz_task) {
DCHECK(viz_task_runner_->BelongsToCurrentThread());
DCHECK(!allow_schedule_task_);
allow_schedule_task_ = true;
// Unretained safe because this object is never deleted.
std::move(viz_task).Run(
base::BindOnce(&TaskQueueWebView::SignalDone, base::Unretained(this)));
}
void TaskQueueWebView::SignalDone() {
DCHECK(viz_task_runner_->BelongsToCurrentThread());
DCHECK(allow_schedule_task_);
allow_schedule_task_ = false;
base::AutoLock lock(lock_);
DCHECK(!done_);
done_ = true;
condvar_.Signal();
}
} // namespace android_webview