910e62b5创建于 1月15日历史提交
// 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