blob: b07e2229bc76acc575faef23d79f1042afc6b97f [file] [log] [blame]
// Copyright 2013 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 "content/browser/android/in_process/synchronous_compositor_impl.h"
#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/lazy_instance.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/android/in_process/synchronous_compositor_factory_impl.h"
#include "content/browser/android/in_process/synchronous_compositor_registry_in_proc.h"
#include "content/browser/android/in_process/synchronous_input_event_filter.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/browser/renderer_host/render_widget_host_view_android.h"
#include "content/common/input/did_overscroll_params.h"
#include "content/common/input_messages.h"
#include "content/public/browser/android/synchronous_compositor_client.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/common/child_process_host.h"
#include "ui/gfx/geometry/scroll_offset.h"
#include "ui/gl/gl_surface.h"
namespace content {
namespace {
int g_process_id = ChildProcessHost::kInvalidUniqueID;
base::LazyInstance<SynchronousCompositorFactoryImpl>::Leaky g_factory =
LAZY_INSTANCE_INITIALIZER;
} // namespace
SynchronousCompositorImpl* SynchronousCompositorImpl::FromRoutingID(
int routing_id) {
if (g_factory == nullptr)
return nullptr;
if (g_process_id == ChildProcessHost::kInvalidUniqueID)
return nullptr;
RenderViewHost* rvh = RenderViewHost::FromID(g_process_id, routing_id);
if (!rvh)
return nullptr;
RenderWidgetHostViewAndroid* rwhva =
static_cast<RenderWidgetHostViewAndroid*>(rvh->GetWidget()->GetView());
if (!rwhva)
return nullptr;
return static_cast<SynchronousCompositorImpl*>(
rwhva->GetSynchronousCompositor());
}
SynchronousCompositorImpl::SynchronousCompositorImpl(
RenderWidgetHostViewAndroid* rwhva,
SynchronousCompositorClient* client)
: rwhva_(rwhva),
routing_id_(rwhva_->GetRenderWidgetHost()->GetRoutingID()),
compositor_client_(client),
output_surface_(nullptr),
begin_frame_source_(nullptr),
synchronous_input_handler_proxy_(nullptr),
registered_with_client_(false),
is_active_(true),
renderer_needs_begin_frames_(false),
need_animate_input_(false),
weak_ptr_factory_(this) {
DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
g_factory.Get(); // Ensure it's initialized.
int process_id = rwhva_->GetRenderWidgetHost()->GetProcess()->GetID();
if (g_process_id == ChildProcessHost::kInvalidUniqueID) {
g_process_id = process_id;
} else {
DCHECK_EQ(g_process_id, process_id); // Not multiprocess compatible.
}
SynchronousCompositorRegistryInProc::GetInstance()->RegisterCompositor(
routing_id_, this);
}
SynchronousCompositorImpl::~SynchronousCompositorImpl() {
SynchronousCompositorRegistryInProc::GetInstance()->UnregisterCompositor(
routing_id_, this);
}
void SynchronousCompositorImpl::RegisterWithClient() {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
DCHECK(synchronous_input_handler_proxy_);
DCHECK(!registered_with_client_);
registered_with_client_ = true;
compositor_client_->DidInitializeCompositor(this);
output_surface_->SetTreeActivationCallback(
base::Bind(&SynchronousCompositorImpl::DidActivatePendingTree,
weak_ptr_factory_.GetWeakPtr()));
// This disables the input system from animating inputs autonomously, instead
// routing all input animations through the SynchronousInputHandler, which is
// |this| class. Calling this causes an UpdateRootLayerState() immediately so,
// do it after setting the client.
synchronous_input_handler_proxy_->SetOnlySynchronouslyAnimateRootFlings(this);
}
// static
void SynchronousCompositorImpl::SetGpuServiceInProc(
scoped_refptr<gpu::InProcessCommandBuffer::Service> service) {
g_factory.Get().SetDeferredGpuService(service);
}
void SynchronousCompositorImpl::DidInitializeRendererObjects(
SynchronousCompositorOutputSurface* output_surface,
SynchronousCompositorExternalBeginFrameSource* begin_frame_source,
SynchronousInputHandlerProxy* synchronous_input_handler_proxy) {
DCHECK(!output_surface_);
DCHECK(!begin_frame_source_);
DCHECK(output_surface);
DCHECK(begin_frame_source);
DCHECK(synchronous_input_handler_proxy);
output_surface_ = output_surface;
begin_frame_source_ = begin_frame_source;
synchronous_input_handler_proxy_ = synchronous_input_handler_proxy;
output_surface_->SetSyncClient(this);
begin_frame_source_->SetClient(this);
}
void SynchronousCompositorImpl::DidDestroyRendererObjects() {
DCHECK(output_surface_);
DCHECK(begin_frame_source_);
if (registered_with_client_) {
output_surface_->SetTreeActivationCallback(base::Closure());
compositor_client_->DidDestroyCompositor(this);
registered_with_client_ = false;
}
// This object is being destroyed, so remove pointers to it.
begin_frame_source_->SetClient(nullptr);
output_surface_->SetSyncClient(nullptr);
synchronous_input_handler_proxy_->SetOnlySynchronouslyAnimateRootFlings(
nullptr);
synchronous_input_handler_proxy_ = nullptr;
begin_frame_source_ = nullptr;
output_surface_ = nullptr;
// Don't propogate this signal from one renderer to the next.
need_animate_input_ = false;
}
scoped_ptr<cc::CompositorFrame> SynchronousCompositorImpl::DemandDrawHw(
const gfx::Size& surface_size,
const gfx::Transform& transform,
const gfx::Rect& viewport,
const gfx::Rect& clip,
const gfx::Rect& viewport_rect_for_tile_priority,
const gfx::Transform& transform_for_tile_priority) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
DCHECK(begin_frame_source_);
scoped_ptr<cc::CompositorFrame> frame =
output_surface_->DemandDrawHw(surface_size,
transform,
viewport,
clip,
viewport_rect_for_tile_priority,
transform_for_tile_priority);
if (frame.get())
UpdateFrameMetaData(frame->metadata);
return frame.Pass();
}
void SynchronousCompositorImpl::ReturnResources(
const cc::CompositorFrameAck& frame_ack) {
DCHECK(CalledOnValidThread());
output_surface_->ReturnResources(frame_ack);
}
bool SynchronousCompositorImpl::DemandDrawSw(SkCanvas* canvas) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
DCHECK(begin_frame_source_);
scoped_ptr<cc::CompositorFrame> frame =
output_surface_->DemandDrawSw(canvas);
if (frame.get())
UpdateFrameMetaData(frame->metadata);
return !!frame.get();
}
void SynchronousCompositorImpl::UpdateFrameMetaData(
const cc::CompositorFrameMetadata& frame_metadata) {
rwhva_->SynchronousFrameMetadata(frame_metadata);
DeliverMessages();
}
void SynchronousCompositorImpl::SetMemoryPolicy(size_t bytes_limit) {
DCHECK(CalledOnValidThread());
DCHECK(output_surface_);
size_t current_bytes_limit = output_surface_->GetMemoryPolicy();
output_surface_->SetMemoryPolicy(bytes_limit);
if (bytes_limit && !current_bytes_limit) {
g_factory.Get().CompositorInitializedHardwareDraw();
} else if (!bytes_limit && current_bytes_limit) {
g_factory.Get().CompositorReleasedHardwareDraw();
}
}
void SynchronousCompositorImpl::Invalidate() {
DCHECK(CalledOnValidThread());
if (registered_with_client_)
compositor_client_->PostInvalidate();
}
void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset(
const gfx::ScrollOffset& root_offset) {
DCHECK(CalledOnValidThread());
if (!synchronous_input_handler_proxy_)
return;
synchronous_input_handler_proxy_->SynchronouslySetRootScrollOffset(
root_offset);
}
void SynchronousCompositorImpl::SetIsActive(bool is_active) {
TRACE_EVENT1("cc", "SynchronousCompositorImpl::SetIsActive", "is_active",
is_active);
is_active_ = is_active;
UpdateNeedsBeginFrames();
}
void SynchronousCompositorImpl::OnComputeScroll(
base::TimeTicks animation_time) {
if (need_animate_input_) {
need_animate_input_ = false;
synchronous_input_handler_proxy_->SynchronouslyAnimate(animation_time);
}
}
void SynchronousCompositorImpl::OnNeedsBeginFramesChange(
bool needs_begin_frames) {
renderer_needs_begin_frames_ = needs_begin_frames;
UpdateNeedsBeginFrames();
}
void SynchronousCompositorImpl::BeginFrame(const cc::BeginFrameArgs& args) {
if (!registered_with_client_ && is_active_ && renderer_needs_begin_frames_) {
// Make sure this is a BeginFrame that renderer side explicitly requested.
// Otherwise it is possible renderer objects not initialized.
RegisterWithClient();
DCHECK(registered_with_client_);
}
if (begin_frame_source_)
begin_frame_source_->BeginFrame(args);
}
void SynchronousCompositorImpl::UpdateNeedsBeginFrames() {
rwhva_->OnSetNeedsBeginFrames(is_active_ && renderer_needs_begin_frames_);
}
void SynchronousCompositorImpl::DidOverscroll(
const DidOverscrollParams& params) {
if (registered_with_client_) {
compositor_client_->DidOverscroll(params.accumulated_overscroll,
params.latest_overscroll_delta,
params.current_fling_velocity);
}
}
void SynchronousCompositorImpl::DidStopFlinging() {
// It's important that the fling-end notification follow the same path as it
// takes on other platforms (using an IPC). This ensures consistent
// bookkeeping at all stages of the input pipeline.
rwhva_->GetRenderWidgetHost()->GetProcess()->OnMessageReceived(
InputHostMsg_DidStopFlinging(routing_id_));
}
InputEventAckState SynchronousCompositorImpl::HandleInputEvent(
const blink::WebInputEvent& input_event) {
DCHECK(CalledOnValidThread());
return g_factory.Get().synchronous_input_event_filter()->HandleInputEvent(
routing_id_, input_event);
}
bool SynchronousCompositorImpl::OnMessageReceived(const IPC::Message& message) {
NOTREACHED();
return false;
}
void SynchronousCompositorImpl::DeliverMessages() {
ScopedVector<IPC::Message> messages;
output_surface_->GetMessagesToDeliver(&messages);
RenderProcessHost* rph = rwhva_->GetRenderWidgetHost()->GetProcess();
for (ScopedVector<IPC::Message>::const_iterator i = messages.begin();
i != messages.end();
++i) {
rph->OnMessageReceived(**i);
}
}
void SynchronousCompositorImpl::DidActivatePendingTree() {
if (registered_with_client_)
compositor_client_->DidUpdateContent();
DeliverMessages();
}
void SynchronousCompositorImpl::SetNeedsSynchronousAnimateInput() {
DCHECK(CalledOnValidThread());
if (!registered_with_client_)
return;
need_animate_input_ = true;
compositor_client_->PostInvalidate();
}
void SynchronousCompositorImpl::UpdateRootLayerState(
const gfx::ScrollOffset& total_scroll_offset,
const gfx::ScrollOffset& max_scroll_offset,
const gfx::SizeF& scrollable_size,
float page_scale_factor,
float min_page_scale_factor,
float max_page_scale_factor) {
DCHECK(CalledOnValidThread());
if (registered_with_client_) {
// TODO(miletus): Pass in ScrollOffset. crbug.com/414283.
compositor_client_->UpdateRootLayerState(
gfx::ScrollOffsetToVector2dF(total_scroll_offset),
gfx::ScrollOffsetToVector2dF(max_scroll_offset),
scrollable_size,
page_scale_factor,
min_page_scale_factor,
max_page_scale_factor);
}
}
// Not using base::NonThreadSafe as we want to enforce a more exacting threading
// requirement: SynchronousCompositorImpl() must only be used on the UI thread.
bool SynchronousCompositorImpl::CalledOnValidThread() const {
return BrowserThread::CurrentlyOn(BrowserThread::UI);
}
} // namespace content