blob: 33b1eaec698f2eb99b25e56d08e934ac38f26eca [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/child_frame.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/trace_event/trace_event.h"
#include "cc/output/compositor_frame.h"
#include "cc/surfaces/surface_factory.h"
#include "cc/surfaces/surface_id_allocator.h"
#include "cc/surfaces/surface_manager.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()),
surface_id_allocator_(surfaces_->CreateSurfaceIdAllocator()),
last_committed_output_surface_id_(0u),
last_submitted_output_surface_id_(0u) {
DCHECK(last_egl_context_);
surfaces_->GetSurfaceManager()->RegisterSurfaceFactoryClient(
surface_id_allocator_->id_namespace(), this);
}
HardwareRenderer::~HardwareRenderer() {
// Must reset everything before |surface_factory_| to ensure all
// resources are returned before resetting.
if (!child_id_.is_null())
DestroySurface();
surface_factory_.reset();
surfaces_->GetSurfaceManager()->UnregisterSurfaceFactoryClient(
surface_id_allocator_->id_namespace());
// Reset draw constraints.
render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT(
ParentCompositorDrawConstraints());
ReturnResourcesInChildFrame();
}
void HardwareRenderer::CommitFrame() {
TRACE_EVENT0("android_webview", "CommitFrame");
scroll_offset_ = render_thread_manager_->GetScrollOffsetOnRT();
std::unique_ptr<ChildFrame> child_frame =
render_thread_manager_->PassFrameOnRT();
if (!child_frame.get())
return;
last_committed_output_surface_id_ = child_frame->output_surface_id;
ReturnResourcesInChildFrame();
child_frame_ = std::move(child_frame);
DCHECK(child_frame_->frame.get());
DCHECK(!child_frame_->frame->gl_frame_data);
}
void HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info,
const ScopedAppGLStateRestore& gl_state) {
TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL");
// 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_output_surface_id_ != child_frame_->output_surface_id) {
if (!child_id_.is_null())
DestroySurface();
// This will return all the resources to the previous compositor.
surface_factory_.reset();
compositor_id_ = child_frame_->compositor_id;
last_submitted_output_surface_id_ = child_frame_->output_surface_id;
surface_factory_.reset(
new cc::SurfaceFactory(surfaces_->GetSurfaceManager(), this));
}
std::unique_ptr<cc::CompositorFrame> child_compositor_frame =
std::move(child_frame_->frame);
gfx::Size frame_size =
child_compositor_frame->delegated_frame_data->render_pass_list.back()
->output_rect.size();
bool size_changed = frame_size != frame_size_;
frame_size_ = frame_size;
if (child_id_.is_null() || size_changed) {
if (!child_id_.is_null())
DestroySurface();
AllocateSurface();
}
surface_factory_->SubmitCompositorFrame(child_id_,
std::move(*child_compositor_frame),
cc::SurfaceFactory::DrawCallback());
}
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_null())
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, frame_size_, child_id_,
gl_state);
}
void HardwareRenderer::AllocateSurface() {
DCHECK(child_id_.is_null());
DCHECK(surface_factory_);
child_id_ = surface_id_allocator_->GenerateId();
surface_factory_->Create(child_id_);
surfaces_->AddChildId(child_id_);
}
void HardwareRenderer::DestroySurface() {
DCHECK(!child_id_.is_null());
DCHECK(surface_factory_);
surfaces_->RemoveChildId(child_id_);
surface_factory_->Destroy(child_id_);
child_id_ = cc::SurfaceId();
}
void HardwareRenderer::ReturnResources(
const cc::ReturnedResourceArray& resources) {
ReturnResourcesToCompositor(resources, compositor_id_,
last_submitted_output_surface_id_);
}
void HardwareRenderer::SetBeginFrameSource(
cc::BeginFrameSource* begin_frame_source) {
// TODO(tansell): Hook this up.
}
void HardwareRenderer::SetBackingFrameBufferObject(
int framebuffer_binding_ext) {
surfaces_->SetBackingFrameBufferObject(framebuffer_binding_ext);
}
void HardwareRenderer::ReturnResourcesInChildFrame() {
if (child_frame_.get() && child_frame_->frame.get()) {
cc::ReturnedResourceArray resources_to_return;
cc::TransferableResource::ReturnResources(
child_frame_->frame->delegated_frame_data->resource_list,
&resources_to_return);
// The child frame's compositor id is not necessarily same as
// compositor_id_.
ReturnResourcesToCompositor(resources_to_return,
child_frame_->compositor_id,
child_frame_->output_surface_id);
}
child_frame_.reset();
}
void HardwareRenderer::ReturnResourcesToCompositor(
const cc::ReturnedResourceArray& resources,
const CompositorID& compositor_id,
uint32_t output_surface_id) {
if (output_surface_id != last_committed_output_surface_id_)
return;
render_thread_manager_->InsertReturnedResourcesOnRT(resources, compositor_id,
output_surface_id);
}
} // namespace android_webview