chromium/android_webview/browser/gfx/render_thread_manager.cc

// Copyright 2014 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/render_thread_manager.h"

#include <memory>
#include <optional>
#include <utility>

#include "android_webview/browser/gfx/compositor_frame_producer.h"
#include "android_webview/browser/gfx/gpu_service_webview.h"
#include "android_webview/browser/gfx/hardware_renderer.h"
#include "android_webview/browser/gfx/scoped_app_gl_state_restore.h"
#include "android_webview/browser/gfx/task_queue_webview.h"
#include "android_webview/common/aw_features.h"
#include "android_webview/public/browser/draw_gl.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "components/viz/common/features.h"
#include "components/viz/common/quads/compositor_frame.h"

namespace android_webview {

RenderThreadManager::RenderThreadManager(
    const scoped_refptr<base::SingleThreadTaskRunner>& ui_loop)
    : ui_loop_(ui_loop), mark_hardware_release_(false) {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  ui_thread_weak_ptr_ = weak_factory_on_ui_thread_.GetWeakPtr();
}

RenderThreadManager::~RenderThreadManager() {
  DCHECK(!hardware_renderer_.get());
  DCHECK(child_frames_.empty());
}

void RenderThreadManager::UpdateParentDrawDataOnUI() {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  if (producer_weak_ptr_) {
    producer_weak_ptr_->OnParentDrawDataUpdated(this);
  }
}

void RenderThreadManager::ViewTreeForceDarkStateChangedOnUI(
    bool view_tree_force_dark_state) {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  if (producer_weak_ptr_) {
    producer_weak_ptr_->OnViewTreeForceDarkStateChanged(
        view_tree_force_dark_state);
  }
}

void RenderThreadManager::SetScrollOffsetOnUI(gfx::Point scroll_offset) {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  base::AutoLock lock(lock_);
  scroll_offset_ = scroll_offset;
}

gfx::Point RenderThreadManager::GetScrollOffsetOnRT() {
  base::AutoLock lock(lock_);
  return scroll_offset_;
}

std::unique_ptr<ChildFrame> RenderThreadManager::SetFrameOnUI(
    std::unique_ptr<ChildFrame> new_frame) {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  DCHECK(new_frame);

  base::AutoLock lock(lock_);
  if (child_frames_.empty()) {
    child_frames_.emplace_back(std::move(new_frame));
    return nullptr;
  }
  std::unique_ptr<ChildFrame> uncommitted_frame;
  DCHECK_LE(child_frames_.size(), 2u);
  ChildFrameQueue pruned_frames =
      HardwareRenderer::WaitAndPruneFrameQueue(&child_frames_);
  DCHECK_LE(pruned_frames.size(), 1u);
  if (pruned_frames.size())
    uncommitted_frame = std::move(pruned_frames.front());
  child_frames_.emplace_back(std::move(new_frame));
  return uncommitted_frame;
}

ChildFrameQueue RenderThreadManager::PassFramesOnRT() {
  base::AutoLock lock(lock_);
  ChildFrameQueue returned_frames;
  returned_frames.swap(child_frames_);
  return returned_frames;
}

ChildFrameQueue RenderThreadManager::PassUncommittedFrameOnUI() {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  base::AutoLock lock(lock_);
  for (auto& frame_ptr : child_frames_)
    frame_ptr->WaitOnFutureIfNeeded();
  ChildFrameQueue returned_frames;
  returned_frames.swap(child_frames_);
  return returned_frames;
}

void RenderThreadManager::PostParentDrawDataToChildCompositorOnRT(
    const ParentCompositorDrawConstraints& parent_draw_constraints,
    const viz::FrameSinkId& frame_sink_id,
    viz::FrameTimingDetailsMap timing_details,
    uint32_t frame_token,
    base::TimeDelta preferred_frame_interval) {
  {
    base::AutoLock lock(lock_);
    parent_draw_constraints_ = parent_draw_constraints;
    timing_details_.insert(timing_details.begin(), timing_details.end());
    presented_frame_token_ = frame_token;
    frame_sink_id_for_presentation_feedbacks_ = frame_sink_id;
    preferred_frame_interval_ = preferred_frame_interval;
  }

  // No need to hold the lock_ during the post task.
  ui_loop_->PostTask(
      FROM_HERE, base::BindOnce(&RenderThreadManager::UpdateParentDrawDataOnUI,
                                ui_thread_weak_ptr_));
}

void RenderThreadManager::TakeParentDrawDataOnUI(
    ParentCompositorDrawConstraints* constraints,
    viz::FrameSinkId* frame_sink_id,
    viz::FrameTimingDetailsMap* timing_details,
    uint32_t* frame_token,
    base::TimeDelta* preferred_frame_interval) {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  DCHECK(timing_details->empty());
  CheckUiCallsAllowed();
  base::AutoLock lock(lock_);
  *constraints = parent_draw_constraints_;
  *frame_sink_id = frame_sink_id_for_presentation_feedbacks_;
  timing_details_.swap(*timing_details);
  *frame_token = presented_frame_token_;
  *preferred_frame_interval = preferred_frame_interval_;
}

void RenderThreadManager::SetInsideHardwareRelease(bool inside) {
  base::AutoLock lock(lock_);
  mark_hardware_release_ = inside;
}

bool RenderThreadManager::IsInsideHardwareRelease() const {
  base::AutoLock lock(lock_);
  return mark_hardware_release_;
}

void RenderThreadManager::InsertReturnedResourcesOnRT(
    std::vector<viz::ReturnedResource> resources,
    const viz::FrameSinkId& frame_sink_id,
    uint32_t layer_tree_frame_sink_id) {
  if (resources.empty())
    return;
  ui_loop_->PostTask(
      FROM_HERE, base::BindOnce(&CompositorFrameProducer::ReturnUsedResources,
                                producer_weak_ptr_, std::move(resources),
                                frame_sink_id, layer_tree_frame_sink_id));
}

void RenderThreadManager::CommitFrameOnRT() {
  if (hardware_renderer_)
    hardware_renderer_->CommitFrame();
}

void RenderThreadManager::SetVulkanContextProviderOnRT(
    AwVulkanContextProvider* context_provider) {
  DCHECK(!hardware_renderer_);
  vulkan_context_provider_ = context_provider;
}

void RenderThreadManager::UpdateViewTreeForceDarkStateOnRT(
    bool view_tree_force_dark_state) {
  if (view_tree_force_dark_state_ == view_tree_force_dark_state)
    return;
  view_tree_force_dark_state_ = view_tree_force_dark_state;
  ui_loop_->PostTask(
      FROM_HERE,
      base::BindOnce(&RenderThreadManager::ViewTreeForceDarkStateChangedOnUI,
                     ui_thread_weak_ptr_, view_tree_force_dark_state_));
}

void RenderThreadManager::DrawOnRT(
    bool save_restore,
    const HardwareRendererDrawParams& params,
    const OverlaysParams& overlays_params,
    ReportRenderingThreadsCallback report_rendering_threads) {
  // Force GL binding init if it's not yet initialized.
  GpuServiceWebView::GetInstance();

  std::optional<ScopedAppGLStateRestore> state_restore;
  if (!vulkan_context_provider_) {
    state_restore.emplace(ScopedAppGLStateRestore::MODE_DRAW, save_restore);
    if (state_restore->skip_draw()) {
      return;
    }
  }

  if (!hardware_renderer_ && !IsInsideHardwareRelease() &&
      HasFrameForHardwareRendererOnRT()) {
    RootFrameSinkGetter getter;
    {
      base::AutoLock lock(lock_);
      getter = root_frame_sink_getter_;
    }
    DCHECK(getter);
    hardware_renderer_ = std::make_unique<HardwareRenderer>(
        this, std::move(getter), vulkan_context_provider_);
    hardware_renderer_->CommitFrame();
  }

  if (hardware_renderer_)
    hardware_renderer_->Draw(params, overlays_params,
                             std::move(report_rendering_threads));
}

void RenderThreadManager::RemoveOverlaysOnRT(
    OverlaysParams::MergeTransactionFn merge_transaction) {
  if (hardware_renderer_)
    hardware_renderer_->RemoveOverlays(merge_transaction);
}

void RenderThreadManager::DestroyHardwareRendererOnRT(bool save_restore,
                                                      bool abandon_context) {
  GpuServiceWebView::GetInstance();

  std::optional<ScopedAppGLStateRestore> state_restore;
  if (!vulkan_context_provider_ && !abandon_context) {
    state_restore.emplace(ScopedAppGLStateRestore::MODE_RESOURCE_MANAGEMENT,
                          save_restore);
  }
  if (abandon_context && hardware_renderer_)
    hardware_renderer_->AbandonContext();

  hardware_renderer_.reset();

  ui_loop_->PostTask(
      FROM_HERE,
      base::BindOnce(&CompositorFrameProducer::ChildSurfaceWasEvicted,
                     producer_weak_ptr_));
}

void RenderThreadManager::RemoveFromCompositorFrameProducerOnUI() {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  if (producer_weak_ptr_)
    producer_weak_ptr_->RemoveCompositorFrameConsumer(this);
  weak_factory_on_ui_thread_.InvalidateWeakPtrs();
#if DCHECK_IS_ON()
  ui_calls_allowed_ = false;
#endif  // DCHECK_IS_ON()
}

void RenderThreadManager::SetCompositorFrameProducer(
    CompositorFrameProducer* compositor_frame_producer,
    RootFrameSinkGetter root_frame_sink_getter) {
  DCHECK(ui_loop_->BelongsToCurrentThread());
  CheckUiCallsAllowed();
  producer_weak_ptr_ = compositor_frame_producer->GetWeakPtr();

  base::AutoLock lock(lock_);
  root_frame_sink_getter_ = std::move(root_frame_sink_getter);
}

void RenderThreadManager::SetRootFrameSinkGetterForTesting(
    RootFrameSinkGetter root_frame_sink_getter) {
  root_frame_sink_getter_ = std::move(root_frame_sink_getter);
}

bool RenderThreadManager::HasFrameForHardwareRendererOnRT() const {
  base::AutoLock lock(lock_);
  return !child_frames_.empty();
}

RenderThreadManager::InsideHardwareReleaseReset::InsideHardwareReleaseReset(
    RenderThreadManager* render_thread_manager)
    : render_thread_manager_(render_thread_manager) {
  DCHECK(!render_thread_manager_->IsInsideHardwareRelease());
  render_thread_manager_->SetInsideHardwareRelease(true);
}

RenderThreadManager::InsideHardwareReleaseReset::~InsideHardwareReleaseReset() {
  render_thread_manager_->SetInsideHardwareRelease(false);
}

}  // namespace android_webview