blob: f23d087caf56da69af995009c9a09528375ec0ff [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/gfx/hardware_renderer.h"
#include <algorithm>
#include <iterator>
#include <memory>
#include <utility>
#include "android_webview/browser/gfx/parent_compositor_draw_constraints.h"
#include "android_webview/browser/gfx/render_thread_manager.h"
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "ui/gl/gl_bindings.h"
namespace android_webview {
HardwareRenderer::HardwareRenderer(RenderThreadManager* state)
: render_thread_manager_(state),
last_egl_context_(eglGetCurrentContext()) {}
HardwareRenderer::~HardwareRenderer() {
// Reset draw constraints.
if (child_frame_) {
render_thread_manager_->PostParentDrawDataToChildCompositorOnRT(
ParentCompositorDrawConstraints(), child_frame_->frame_sink_id,
viz::FrameTimingDetailsMap(), 0u);
}
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::Draw(HardwareRendererDrawParams* params) {
TRACE_EVENT0("android_webview", "HardwareRenderer::Draw");
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;
}
if (last_egl_context_) {
// 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) << "Draw called without EGLContext";
// TODO(boliu): Handle context loss.
if (last_egl_context_ != current_context)
DLOG(WARNING) << "EGLContextChanged";
}
DrawAndSwap(params);
}
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 frame_sink_id is not necessarily same as
// |child_frame_sink_id_|.
ReturnResourcesToCompositor(resources_to_return, child_frame->frame_sink_id,
child_frame->layer_tree_frame_sink_id);
}
void HardwareRenderer::ReturnResourcesToCompositor(
const std::vector<viz::ReturnedResource>& resources,
const viz::FrameSinkId& frame_sink_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, frame_sink_id,
layer_tree_frame_sink_id);
}
namespace {
void MoveCopyRequests(CopyOutputRequestQueue* from,
CopyOutputRequestQueue* to) {
std::move(from->begin(), from->end(), std::back_inserter(*to));
from->clear();
}
} // namespace
// static
ChildFrameQueue HardwareRenderer::WaitAndPruneFrameQueue(
ChildFrameQueue* child_frames_ptr) {
ChildFrameQueue& child_frames = *child_frames_ptr;
ChildFrameQueue pruned_frames;
if (child_frames.empty())
return pruned_frames;
// First find the last non-empty frame.
int remaining_frame_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)
remaining_frame_index = i;
}
// If all empty, keep the last one.
if (remaining_frame_index < 0)
remaining_frame_index = child_frames.size() - 1;
// Prune end.
while (child_frames.size() > static_cast<size_t>(remaining_frame_index + 1)) {
std::unique_ptr<ChildFrame> frame = std::move(child_frames.back());
child_frames.pop_back();
MoveCopyRequests(&frame->copy_requests,
&child_frames[remaining_frame_index]->copy_requests);
DCHECK(!frame->frame);
}
DCHECK_EQ(static_cast<size_t>(remaining_frame_index),
child_frames.size() - 1);
// Prune front.
while (child_frames.size() > 1) {
std::unique_ptr<ChildFrame> frame = std::move(child_frames.front());
child_frames.pop_front();
MoveCopyRequests(&frame->copy_requests,
&child_frames.back()->copy_requests);
if (frame->frame)
pruned_frames.emplace_back(std::move(frame));
}
return pruned_frames;
}
} // namespace android_webview