// 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 "cc/input/input_handler.h" #include "content/browser/android/in_process/synchronous_compositor_external_begin_frame_source.h" #include "content/browser/android/in_process/synchronous_compositor_factory_impl.h" #include "content/browser/android/in_process/synchronous_compositor_registry.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 "ui/gl/gl_surface.h" namespace content { namespace { int GetInProcessRendererId() { content::RenderProcessHost::iterator it = content::RenderProcessHost::AllHostsIterator(); if (it.IsAtEnd()) { // There should always be one RPH in single process mode. NOTREACHED(); return 0; } int id = it.GetCurrentValue()->GetID(); it.Advance(); DCHECK(it.IsAtEnd()); // Not multiprocess compatible. return id; } base::LazyInstance<SynchronousCompositorFactoryImpl>::Leaky g_factory = LAZY_INSTANCE_INITIALIZER; base::Thread* CreateInProcessGpuThreadForSynchronousCompositor( const InProcessChildThreadParams& params) { return g_factory.Get().CreateInProcessGpuThread(params); } } // namespace DEFINE_WEB_CONTENTS_USER_DATA_KEY(SynchronousCompositorImpl); // static SynchronousCompositorImpl* SynchronousCompositorImpl::FromID(int process_id, int routing_id) { if (g_factory == nullptr) return nullptr; RenderViewHost* rvh = RenderViewHost::FromID(process_id, routing_id); if (!rvh) return nullptr; WebContents* contents = WebContents::FromRenderViewHost(rvh); if (!contents) return nullptr; return FromWebContents(contents); } SynchronousCompositorImpl* SynchronousCompositorImpl::FromRoutingID( int routing_id) { return FromID(GetInProcessRendererId(), routing_id); } SynchronousCompositorImpl::SynchronousCompositorImpl(WebContents* contents) : compositor_client_(nullptr), output_surface_(nullptr), begin_frame_source_(nullptr), contents_(contents), routing_id_(contents->GetRoutingID()), input_handler_(nullptr), registered_with_client_(false), is_active_(true), renderer_needs_begin_frames_(false), weak_ptr_factory_(this) { DCHECK(contents); DCHECK_NE(routing_id_, MSG_ROUTING_NONE); } SynchronousCompositorImpl::~SynchronousCompositorImpl() { DCHECK(!output_surface_); DCHECK(!begin_frame_source_); DCHECK(!input_handler_); } void SynchronousCompositorImpl::SetClient( SynchronousCompositorClient* compositor_client) { DCHECK(CalledOnValidThread()); DCHECK_IMPLIES(compositor_client, !compositor_client_); DCHECK_IMPLIES(!compositor_client, compositor_client_); if (!compositor_client) { SynchronousCompositorRegistry::GetInstance()->UnregisterCompositor( routing_id_, this); } compositor_client_ = compositor_client; // SetClient is essentially the constructor and destructor of // SynchronousCompositorImpl. if (compositor_client_) { SynchronousCompositorRegistry::GetInstance()->RegisterCompositor( routing_id_, this); } } void SynchronousCompositorImpl::RegisterWithClient() { DCHECK(CalledOnValidThread()); DCHECK(compositor_client_); DCHECK(output_surface_); DCHECK(input_handler_); DCHECK(!registered_with_client_); registered_with_client_ = true; compositor_client_->DidInitializeCompositor(this); output_surface_->SetTreeActivationCallback( base::Bind(&SynchronousCompositorImpl::DidActivatePendingTree, weak_ptr_factory_.GetWeakPtr())); // Setting the delegate causes UpdateRootLayerState immediately so do it // after setting the client. input_handler_->SetRootLayerScrollOffsetDelegate(this); } // static void SynchronousCompositor::SetGpuService( scoped_refptr<gpu::InProcessCommandBuffer::Service> service) { g_factory.Get().SetDeferredGpuService(service); GpuProcessHost::RegisterGpuMainThreadFactory( CreateInProcessGpuThreadForSynchronousCompositor); } // static void SynchronousCompositor::SetRecordFullDocument(bool record_full_document) { g_factory.Get().SetRecordFullDocument(record_full_document); } // static void SynchronousCompositor::SetUseIpcCommandBuffer() { g_factory.Get().SetUseIpcCommandBuffer(); } void SynchronousCompositorImpl::DidInitializeRendererObjects( SynchronousCompositorOutputSurface* output_surface, SynchronousCompositorExternalBeginFrameSource* begin_frame_source, cc::InputHandler* input_handler) { DCHECK(!output_surface_); DCHECK(!begin_frame_source_); DCHECK(output_surface); DCHECK(begin_frame_source); DCHECK(compositor_client_); DCHECK(input_handler); output_surface_ = output_surface; begin_frame_source_ = begin_frame_source; input_handler_ = input_handler; output_surface_->SetCompositor(this); begin_frame_source_->SetCompositor(this); } void SynchronousCompositorImpl::DidDestroyRendererObjects() { DCHECK(output_surface_); DCHECK(begin_frame_source_); DCHECK(compositor_client_); if (registered_with_client_) { input_handler_->SetRootLayerScrollOffsetDelegate(nullptr); output_surface_->SetTreeActivationCallback(base::Closure()); compositor_client_->DidDestroyCompositor(this); registered_with_client_ = false; } begin_frame_source_->SetCompositor(nullptr); output_surface_->SetCompositor(nullptr); input_handler_ = nullptr; begin_frame_source_ = nullptr; output_surface_ = nullptr; } scoped_ptr<cc::CompositorFrame> SynchronousCompositorImpl::DemandDrawHw( gfx::Size surface_size, const gfx::Transform& transform, gfx::Rect viewport, gfx::Rect clip, gfx::Rect viewport_rect_for_tile_priority, const gfx::Transform& transform_for_tile_priority) { DCHECK(CalledOnValidThread()); DCHECK(output_surface_); DCHECK(compositor_client_); 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(compositor_client_); 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) { RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>( contents_->GetRenderWidgetHostView()); if (rwhv) rwhv->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::PostInvalidate() { DCHECK(CalledOnValidThread()); DCHECK(compositor_client_); if (registered_with_client_) compositor_client_->PostInvalidate(); } void SynchronousCompositorImpl::DidChangeRootLayerScrollOffset() { if (input_handler_) input_handler_->OnRootLayerDelegatedScrollOffsetChanged(); } void SynchronousCompositorImpl::SetIsActive(bool is_active) { TRACE_EVENT1("cc", "SynchronousCompositorImpl::SetIsActive", "is_active", is_active); is_active_ = is_active; UpdateNeedsBeginFrames(); } 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() { RenderWidgetHostViewAndroid* rwhv = static_cast<RenderWidgetHostViewAndroid*>( contents_->GetRenderWidgetHostView()); if (rwhv) rwhv->OnSetNeedsBeginFrames(is_active_ && renderer_needs_begin_frames_); } void SynchronousCompositorImpl::DidOverscroll( const DidOverscrollParams& params) { DCHECK(compositor_client_); 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. contents_->GetRenderProcessHost()->OnMessageReceived( InputHostMsg_DidStopFlinging(routing_id_)); } InputEventAckState SynchronousCompositorImpl::HandleInputEvent( const blink::WebInputEvent& input_event) { DCHECK(CalledOnValidThread()); return g_factory.Get().synchronous_input_event_filter()->HandleInputEvent( contents_->GetRoutingID(), input_event); } void SynchronousCompositorImpl::DeliverMessages() { ScopedVector<IPC::Message> messages; output_surface_->GetMessagesToDeliver(&messages); RenderProcessHost* rph = contents_->GetRenderProcessHost(); for (ScopedVector<IPC::Message>::const_iterator i = messages.begin(); i != messages.end(); ++i) { rph->OnMessageReceived(**i); } } void SynchronousCompositorImpl::DidActivatePendingTree() { DCHECK(compositor_client_); if (registered_with_client_) compositor_client_->DidUpdateContent(); DeliverMessages(); } gfx::ScrollOffset SynchronousCompositorImpl::GetTotalScrollOffset() { DCHECK(CalledOnValidThread()); DCHECK(compositor_client_); if (!registered_with_client_) return gfx::ScrollOffset(); // TODO(miletus): Make GetTotalRootLayerScrollOffset return // ScrollOffset. crbug.com/414283. return gfx::ScrollOffset( compositor_client_->GetTotalRootLayerScrollOffset()); } bool SynchronousCompositorImpl::IsExternalScrollActive() const { DCHECK(CalledOnValidThread()); DCHECK(compositor_client_); if (!registered_with_client_) return false; return compositor_client_->IsExternalScrollActive(); } void SynchronousCompositorImpl::SetNeedsAnimate( const AnimationCallback& animation) { DCHECK(CalledOnValidThread()); DCHECK(compositor_client_); if (!registered_with_client_) return; compositor_client_->SetNeedsAnimateScroll(animation); } 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()); DCHECK(compositor_client_); 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); } // static void SynchronousCompositor::SetClientForWebContents( WebContents* contents, SynchronousCompositorClient* client) { DCHECK(contents); if (client) { g_factory.Get(); // Ensure it's initialized. SynchronousCompositorImpl::CreateForWebContents(contents); } SynchronousCompositorImpl* instance = SynchronousCompositorImpl::FromWebContents(contents); DCHECK(instance); instance->SetClient(client); } } // namespace content