blob: 22e5b6fd963df92abd4ba544c15a649fddafb9fa [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/browser/hardware_renderer.h"
#include <utility>
#include "android_webview/browser/aw_gl_surface.h"
#include "android_webview/browser/aw_render_thread_context_provider.h"
#include "android_webview/browser/parent_compositor_draw_constraints.h"
#include "android_webview/browser/render_thread_manager.h"
#include "android_webview/browser/surfaces_instance.h"
#include "android_webview/public/browser/draw_gl.h"
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "cc/output/compositor_frame.h"
#include "components/viz/common/surfaces/local_surface_id_allocator.h"
#include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "ui/gfx/transform.h"
#include "ui/gl/gl_bindings.h"
namespace android_webview {
HardwareRenderer::HardwareRenderer(RenderThreadManager* state)
: render_thread_manager_(state),
last_egl_context_(eglGetCurrentContext()),
surfaces_(SurfacesInstance::GetOrCreateInstance()),
frame_sink_id_(surfaces_->AllocateFrameSinkId()),
local_surface_id_allocator_(
base::MakeUnique<viz::LocalSurfaceIdAllocator>()),
last_committed_layer_tree_frame_sink_id_(0u),
last_submitted_layer_tree_frame_sink_id_(0u) {
DCHECK(last_egl_context_);
surfaces_->GetFrameSinkManager()->surface_manager()->RegisterFrameSinkId(
frame_sink_id_);
CreateNewCompositorFrameSinkSupport();
}
HardwareRenderer::~HardwareRenderer() {
// Must reset everything before |surface_factory_| to ensure all
// resources are returned before resetting.
if (child_id_.is_valid())
DestroySurface();
support_.reset();
surfaces_->GetFrameSinkManager()->surface_manager()->InvalidateFrameSinkId(
frame_sink_id_);
// Reset draw constraints.
render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
ParentCompositorDrawConstraints());
for (auto& child_frame : child_frame_queue_) {
child_frame->WaitOnFutureIfNeeded();
ReturnChildFrame(std::move(child_frame));
}
}
void HardwareRenderer::CommitFrame() {
TRACE_EVENT0("android_webview", "CommitFrame");
scroll_offset_ = render_thread_manager_->GetScrollOffsetOnRT();
ChildFrameQueue child_frames = render_thread_manager_->PassFramesOnRT();
// |child_frames| should have at most one non-empty frame, and one current
// and unwaited frame, in that order.
DCHECK_LE(child_frames.size(), 2u);
if (child_frames.empty())
return;
// Insert all except last, ie current frame.
while (child_frames.size() > 1u) {
child_frame_queue_.emplace_back(std::move(child_frames.front()));
child_frames.pop_front();
}
for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_))
ReturnChildFrame(std::move(pruned_frame));
DCHECK_LE(child_frame_queue_.size(), 1u);
child_frame_queue_.emplace_back(std::move(child_frames.front()));
}
void HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info) {
TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL");
for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_))
ReturnChildFrame(std::move(pruned_frame));
DCHECK_LE(child_frame_queue_.size(), 1u);
if (!child_frame_queue_.empty()) {
child_frame_ = std::move(child_frame_queue_.front());
child_frame_queue_.clear();
}
if (child_frame_) {
last_committed_layer_tree_frame_sink_id_ =
child_frame_->layer_tree_frame_sink_id;
}
// We need to watch if the current Android context has changed and enforce
// a clean-up in the compositor.
EGLContext current_context = eglGetCurrentContext();
DCHECK(current_context) << "DrawGL called without EGLContext";
// TODO(boliu): Handle context loss.
if (last_egl_context_ != current_context)
DLOG(WARNING) << "EGLContextChanged";
// SurfaceFactory::SubmitCompositorFrame might call glFlush. So calling it
// during "kModeSync" stage (which does not allow GL) might result in extra
// kModeProcess. Instead, submit the frame in "kModeDraw" stage to avoid
// unnecessary kModeProcess.
if (child_frame_.get() && child_frame_->frame.get()) {
if (!compositor_id_.Equals(child_frame_->compositor_id) ||
last_submitted_layer_tree_frame_sink_id_ !=
child_frame_->layer_tree_frame_sink_id) {
if (child_id_.is_valid())
DestroySurface();
CreateNewCompositorFrameSinkSupport();
compositor_id_ = child_frame_->compositor_id;
last_submitted_layer_tree_frame_sink_id_ =
child_frame_->layer_tree_frame_sink_id;
}
std::unique_ptr<cc::CompositorFrame> child_compositor_frame =
std::move(child_frame_->frame);
float device_scale_factor =
child_compositor_frame->metadata.device_scale_factor;
gfx::Size frame_size =
child_compositor_frame->render_pass_list.back()->output_rect.size();
if (!child_id_.is_valid() || surface_size_ != frame_size ||
device_scale_factor_ != device_scale_factor) {
if (child_id_.is_valid())
DestroySurface();
AllocateSurface();
surface_size_ = frame_size;
device_scale_factor_ = device_scale_factor;
}
bool result = support_->SubmitCompositorFrame(
child_id_, std::move(*child_compositor_frame));
DCHECK(result);
}
gfx::Transform transform(gfx::Transform::kSkipInitialization);
transform.matrix().setColMajorf(draw_info->transform);
transform.Translate(scroll_offset_.x(), scroll_offset_.y());
gfx::Size viewport(draw_info->width, draw_info->height);
// Need to post the new transform matrix back to child compositor
// because there is no onDraw during a Render Thread animation, and child
// compositor might not have the tiles rasterized as the animation goes on.
ParentCompositorDrawConstraints draw_constraints(
draw_info->is_layer, transform, viewport.IsEmpty());
if (!child_frame_.get() || draw_constraints.NeedUpdate(*child_frame_)) {
render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
draw_constraints);
}
if (!child_id_.is_valid())
return;
gfx::Rect clip(draw_info->clip_left, draw_info->clip_top,
draw_info->clip_right - draw_info->clip_left,
draw_info->clip_bottom - draw_info->clip_top);
surfaces_->DrawAndSwap(viewport, clip, transform, surface_size_,
viz::SurfaceId(frame_sink_id_, child_id_));
}
void HardwareRenderer::AllocateSurface() {
DCHECK(!child_id_.is_valid());
child_id_ = local_surface_id_allocator_->GenerateId();
surfaces_->AddChildId(viz::SurfaceId(frame_sink_id_, child_id_));
}
void HardwareRenderer::DestroySurface() {
DCHECK(child_id_.is_valid());
surfaces_->RemoveChildId(viz::SurfaceId(frame_sink_id_, child_id_));
support_->EvictCurrentSurface();
child_id_ = viz::LocalSurfaceId();
}
void HardwareRenderer::DidReceiveCompositorFrameAck(
const std::vector<viz::ReturnedResource>& resources) {
ReturnResourcesToCompositor(resources, compositor_id_,
last_submitted_layer_tree_frame_sink_id_);
}
void HardwareRenderer::OnBeginFrame(const viz::BeginFrameArgs& args) {
// TODO(tansell): Hook this up.
}
void HardwareRenderer::ReclaimResources(
const std::vector<viz::ReturnedResource>& resources) {
ReturnResourcesToCompositor(resources, compositor_id_,
last_submitted_layer_tree_frame_sink_id_);
}
void HardwareRenderer::WillDrawSurface(
const viz::LocalSurfaceId& local_surface_id,
const gfx::Rect& damage_rect) {}
void HardwareRenderer::OnBeginFramePausedChanged(bool paused) {}
// static
ChildFrameQueue HardwareRenderer::WaitAndPruneFrameQueue(
ChildFrameQueue* child_frames_ptr) {
ChildFrameQueue& child_frames = *child_frames_ptr;
ChildFrameQueue pruned_frames;
// First find the last non-empty frame.
int last_non_empty_index = -1;
for (size_t i = 0; i < child_frames.size(); ++i) {
auto& child_frame = *child_frames[i];
child_frame.WaitOnFutureIfNeeded();
if (child_frame.frame)
last_non_empty_index = i;
}
if (last_non_empty_index < 0) {
child_frames.clear();
return pruned_frames;
}
// Prune end.
while (child_frames.size() > static_cast<size_t>(last_non_empty_index + 1)) {
std::unique_ptr<ChildFrame> frame = std::move(child_frames.back());
child_frames.pop_back();
if (frame->frame)
pruned_frames.emplace_back(std::move(frame));
}
// Prune front.
while (child_frames.size() > 1) {
std::unique_ptr<ChildFrame> frame = std::move(child_frames.front());
child_frames.pop_front();
if (frame->frame)
pruned_frames.emplace_back(std::move(frame));
}
return pruned_frames;
}
void HardwareRenderer::ReturnChildFrame(
std::unique_ptr<ChildFrame> child_frame) {
if (!child_frame || !child_frame->frame)
return;
std::vector<viz::ReturnedResource> resources_to_return =
viz::TransferableResource::ReturnResources(
child_frame->frame->resource_list);
// The child frame's compositor id is not necessarily same as
// compositor_id_.
ReturnResourcesToCompositor(resources_to_return, child_frame->compositor_id,
child_frame->layer_tree_frame_sink_id);
}
void HardwareRenderer::ReturnResourcesToCompositor(
const std::vector<viz::ReturnedResource>& resources,
const CompositorID& compositor_id,
uint32_t layer_tree_frame_sink_id) {
if (layer_tree_frame_sink_id != last_committed_layer_tree_frame_sink_id_)
return;
render_thread_manager_->InsertReturnedResourcesOnRT(resources, compositor_id,
layer_tree_frame_sink_id);
}
void HardwareRenderer::CreateNewCompositorFrameSinkSupport() {
constexpr bool is_root = false;
constexpr bool handles_frame_sink_id_invalidation = false;
constexpr bool needs_sync_points = true;
support_.reset();
support_ = viz::CompositorFrameSinkSupport::Create(
this, surfaces_->GetFrameSinkManager(), frame_sink_id_, is_root,
handles_frame_sink_id_invalidation, needs_sync_points);
}
} // namespace android_webview