| // Copyright 2015 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/synchronous_compositor_host.h" |
| |
| #include <utility> |
| |
| #include "base/containers/hash_tables.h" |
| #include "base/memory/shared_memory.h" |
| #include "base/trace_event/trace_event_argument.h" |
| #include "cc/output/compositor_frame_ack.h" |
| #include "content/browser/android/in_process/synchronous_compositor_factory_impl.h" |
| #include "content/browser/android/in_process/synchronous_compositor_renderer_statics.h" |
| #include "content/browser/renderer_host/render_widget_host_view_android.h" |
| #include "content/browser/web_contents/web_contents_impl.h" |
| #include "content/common/android/sync_compositor_messages.h" |
| #include "content/public/browser/android/synchronous_compositor_client.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "ipc/ipc_sender.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkImageInfo.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| #include "ui/gfx/skia_util.h" |
| |
| namespace content { |
| |
| SynchronousCompositorHost::SynchronousCompositorHost( |
| RenderWidgetHostViewAndroid* rwhva, |
| SynchronousCompositorClient* client, |
| bool use_in_proc_software_draw) |
| : rwhva_(rwhva), |
| client_(client), |
| ui_task_runner_( |
| BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI)), |
| routing_id_(rwhva_->GetRenderWidgetHost()->GetRoutingID()), |
| sender_(rwhva_->GetRenderWidgetHost()), |
| use_in_process_zero_copy_software_draw_(use_in_proc_software_draw), |
| is_active_(false), |
| bytes_limit_(0u), |
| root_scroll_offset_updated_by_browser_(false), |
| renderer_param_version_(0u), |
| need_animate_scroll_(false), |
| need_invalidate_count_(0u), |
| need_begin_frame_(false), |
| did_activate_pending_tree_count_(0u), |
| weak_ptr_factory_(this) { |
| client_->DidInitializeCompositor(this); |
| } |
| |
| SynchronousCompositorHost::~SynchronousCompositorHost() { |
| client_->DidDestroyCompositor(this); |
| if (weak_ptr_factory_.HasWeakPtrs()) |
| UpdateStateTask(); |
| } |
| |
| bool SynchronousCompositorHost::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(SynchronousCompositorHost, message) |
| IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_UpdateState, ProcessCommonParams) |
| IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_OverScroll, OnOverScroll) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| void SynchronousCompositorHost::DidBecomeCurrent() { |
| client_->DidBecomeCurrent(this); |
| } |
| |
| scoped_ptr<cc::CompositorFrame> SynchronousCompositorHost::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) { |
| SyncCompositorDemandDrawHwParams params(surface_size, transform, viewport, |
| clip, viewport_rect_for_tile_priority, |
| transform_for_tile_priority); |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| if (!sender_->Send(new SyncCompositorMsg_DemandDrawHw( |
| routing_id_, common_browser_params, params, &common_renderer_params, |
| frame.get()))) { |
| return nullptr; |
| } |
| ProcessCommonParams(common_renderer_params); |
| if (!frame->delegated_frame_data) { |
| // This can happen if compositor did not swap in this draw. |
| frame.reset(); |
| } |
| if (frame) |
| UpdateFrameMetaData(frame->metadata); |
| return frame; |
| } |
| |
| void SynchronousCompositorHost::UpdateFrameMetaData( |
| const cc::CompositorFrameMetadata& frame_metadata) { |
| rwhva_->SynchronousFrameMetadata(frame_metadata); |
| } |
| |
| namespace { |
| |
| class ScopedSetSkCanvas { |
| public: |
| explicit ScopedSetSkCanvas(SkCanvas* canvas) { |
| SynchronousCompositorSetSkCanvas(canvas); |
| } |
| |
| ~ScopedSetSkCanvas() { |
| SynchronousCompositorSetSkCanvas(nullptr); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ScopedSetSkCanvas); |
| }; |
| |
| } |
| |
| bool SynchronousCompositorHost::DemandDrawSwInProc(SkCanvas* canvas) { |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| bool success = false; |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| ScopedSetSkCanvas set_sk_canvas(canvas); |
| SyncCompositorDemandDrawSwParams params; // Unused. |
| if (!sender_->Send(new SyncCompositorMsg_DemandDrawSw( |
| routing_id_, common_browser_params, params, &success, |
| &common_renderer_params, frame.get()))) { |
| return false; |
| } |
| if (!success) |
| return false; |
| ProcessCommonParams(common_renderer_params); |
| UpdateFrameMetaData(frame->metadata); |
| return true; |
| } |
| |
| class SynchronousCompositorHost::ScopedSendZeroMemory { |
| public: |
| ScopedSendZeroMemory(SynchronousCompositorHost* host) : host_(host) {} |
| ~ScopedSendZeroMemory() { host_->SendZeroMemory(); } |
| |
| private: |
| SynchronousCompositorHost* const host_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScopedSendZeroMemory); |
| }; |
| |
| struct SynchronousCompositorHost::SharedMemoryWithSize { |
| base::SharedMemory shm; |
| const size_t stride; |
| const size_t buffer_size; |
| |
| SharedMemoryWithSize(size_t stride, size_t buffer_size) |
| : stride(stride), buffer_size(buffer_size) {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SharedMemoryWithSize); |
| }; |
| |
| bool SynchronousCompositorHost::DemandDrawSw(SkCanvas* canvas) { |
| if (use_in_process_zero_copy_software_draw_) |
| return DemandDrawSwInProc(canvas); |
| |
| SyncCompositorDemandDrawSwParams params; |
| params.size = gfx::Size(canvas->getBaseLayerSize().width(), |
| canvas->getBaseLayerSize().height()); |
| SkIRect canvas_clip; |
| canvas->getClipDeviceBounds(&canvas_clip); |
| params.clip = gfx::SkIRectToRect(canvas_clip); |
| params.transform.matrix() = canvas->getTotalMatrix(); |
| if (params.size.IsEmpty()) |
| return true; |
| |
| SkImageInfo info = |
| SkImageInfo::MakeN32Premul(params.size.width(), params.size.height()); |
| DCHECK_EQ(kRGBA_8888_SkColorType, info.colorType()); |
| size_t stride = info.minRowBytes(); |
| size_t buffer_size = info.getSafeSize(stride); |
| if (!buffer_size) |
| return false; // Overflow. |
| |
| SetSoftwareDrawSharedMemoryIfNeeded(stride, buffer_size); |
| if (!software_draw_shm_) |
| return false; |
| |
| scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| bool success = false; |
| if (!sender_->Send(new SyncCompositorMsg_DemandDrawSw( |
| routing_id_, common_browser_params, params, &success, |
| &common_renderer_params, frame.get()))) { |
| return false; |
| } |
| ScopedSendZeroMemory send_zero_memory(this); |
| if (!success) |
| return false; |
| |
| ProcessCommonParams(common_renderer_params); |
| UpdateFrameMetaData(frame->metadata); |
| |
| SkBitmap bitmap; |
| if (!bitmap.installPixels(info, software_draw_shm_->shm.memory(), stride)) |
| return false; |
| |
| { |
| TRACE_EVENT0("browser", "DrawBitmap"); |
| canvas->save(); |
| canvas->resetMatrix(); |
| canvas->drawBitmap(bitmap, 0, 0); |
| canvas->restore(); |
| } |
| |
| return true; |
| } |
| |
| void SynchronousCompositorHost::SetSoftwareDrawSharedMemoryIfNeeded( |
| size_t stride, |
| size_t buffer_size) { |
| if (software_draw_shm_ && software_draw_shm_->stride == stride && |
| software_draw_shm_->buffer_size == buffer_size) |
| return; |
| software_draw_shm_.reset(); |
| scoped_ptr<SharedMemoryWithSize> software_draw_shm( |
| new SharedMemoryWithSize(stride, buffer_size)); |
| { |
| TRACE_EVENT1("browser", "AllocateSharedMemory", "buffer_size", buffer_size); |
| if (!software_draw_shm->shm.CreateAndMapAnonymous(buffer_size)) |
| return; |
| } |
| |
| SyncCompositorSetSharedMemoryParams set_shm_params; |
| set_shm_params.buffer_size = buffer_size; |
| base::ProcessHandle renderer_process_handle = |
| rwhva_->GetRenderWidgetHost()->GetProcess()->GetHandle(); |
| if (!software_draw_shm->shm.ShareToProcess(renderer_process_handle, |
| &set_shm_params.shm_handle)) { |
| return; |
| } |
| |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| bool success = false; |
| SyncCompositorCommonRendererParams common_renderer_params; |
| if (!sender_->Send(new SyncCompositorMsg_SetSharedMemory( |
| routing_id_, common_browser_params, set_shm_params, &success, |
| &common_renderer_params)) || |
| !success) { |
| return; |
| } |
| software_draw_shm_ = std::move(software_draw_shm); |
| ProcessCommonParams(common_renderer_params); |
| } |
| |
| void SynchronousCompositorHost::SendZeroMemory() { |
| // No need to check return value. |
| sender_->Send(new SyncCompositorMsg_ZeroSharedMemory(routing_id_)); |
| } |
| |
| void SynchronousCompositorHost::ReturnResources( |
| const cc::CompositorFrameAck& frame_ack) { |
| returned_resources_.insert(returned_resources_.end(), |
| frame_ack.resources.begin(), |
| frame_ack.resources.end()); |
| } |
| |
| void SynchronousCompositorHost::SetMemoryPolicy(size_t bytes_limit) { |
| if (bytes_limit_ == bytes_limit) |
| return; |
| size_t current_bytes_limit = bytes_limit_; |
| bytes_limit_ = bytes_limit; |
| SendAsyncCompositorStateIfNeeded(); |
| |
| if (bytes_limit && !current_bytes_limit) { |
| SynchronousCompositorStreamTextureFactoryImpl::GetInstance() |
| ->CompositorInitializedHardwareDraw(); |
| } else if (!bytes_limit && current_bytes_limit) { |
| SynchronousCompositorStreamTextureFactoryImpl::GetInstance() |
| ->CompositorReleasedHardwareDraw(); |
| } |
| } |
| |
| void SynchronousCompositorHost::DidChangeRootLayerScrollOffset( |
| const gfx::ScrollOffset& root_offset) { |
| if (root_scroll_offset_ == root_offset) |
| return; |
| root_scroll_offset_updated_by_browser_ = true; |
| root_scroll_offset_ = root_offset; |
| SendAsyncCompositorStateIfNeeded(); |
| } |
| |
| void SynchronousCompositorHost::SendAsyncCompositorStateIfNeeded() { |
| if (weak_ptr_factory_.HasWeakPtrs()) |
| return; |
| |
| ui_task_runner_->PostTask( |
| FROM_HERE, base::Bind(&SynchronousCompositorHost::UpdateStateTask, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void SynchronousCompositorHost::UpdateStateTask() { |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| sender_->Send( |
| new SyncCompositorMsg_UpdateState(routing_id_, common_browser_params)); |
| DCHECK(!weak_ptr_factory_.HasWeakPtrs()); |
| } |
| |
| void SynchronousCompositorHost::SetIsActive(bool is_active) { |
| if (is_active_ == is_active) |
| return; |
| is_active_ = is_active; |
| UpdateNeedsBeginFrames(); |
| SendAsyncCompositorStateIfNeeded(); |
| } |
| |
| void SynchronousCompositorHost::OnComputeScroll( |
| base::TimeTicks animation_time) { |
| if (!need_animate_scroll_) |
| return; |
| need_animate_scroll_ = false; |
| |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| sender_->Send(new SyncCompositorMsg_ComputeScroll( |
| routing_id_, common_browser_params, animation_time)); |
| } |
| |
| InputEventAckState SynchronousCompositorHost::HandleInputEvent( |
| const blink::WebInputEvent& input_event) { |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| InputEventAckState ack = INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
| if (!sender_->Send(new SyncCompositorMsg_HandleInputEvent( |
| routing_id_, common_browser_params, &input_event, |
| &common_renderer_params, &ack))) { |
| return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; |
| } |
| ProcessCommonParams(common_renderer_params); |
| return ack; |
| } |
| |
| void SynchronousCompositorHost::BeginFrame(const cc::BeginFrameArgs& args) { |
| if (!is_active_ || !need_begin_frame_) |
| return; |
| |
| SyncCompositorCommonBrowserParams common_browser_params; |
| PopulateCommonParams(&common_browser_params); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| if (!sender_->Send( |
| new SyncCompositorMsg_BeginFrame(routing_id_, common_browser_params, |
| args, &common_renderer_params))) { |
| return; |
| } |
| ProcessCommonParams(common_renderer_params); |
| } |
| |
| void SynchronousCompositorHost::OnOverScroll( |
| const SyncCompositorCommonRendererParams& params, |
| const DidOverscrollParams& over_scroll_params) { |
| ProcessCommonParams(params); |
| client_->DidOverscroll(over_scroll_params.accumulated_overscroll, |
| over_scroll_params.latest_overscroll_delta, |
| over_scroll_params.current_fling_velocity); |
| } |
| |
| void SynchronousCompositorHost::PopulateCommonParams( |
| SyncCompositorCommonBrowserParams* params) { |
| DCHECK(params); |
| DCHECK(params->ack.resources.empty()); |
| params->bytes_limit = bytes_limit_; |
| params->ack.resources.swap(returned_resources_); |
| if (root_scroll_offset_updated_by_browser_) { |
| params->root_scroll_offset = root_scroll_offset_; |
| params->update_root_scroll_offset = root_scroll_offset_updated_by_browser_; |
| root_scroll_offset_updated_by_browser_ = false; |
| } |
| params->begin_frame_source_paused = !is_active_; |
| |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| } |
| |
| void SynchronousCompositorHost::ProcessCommonParams( |
| const SyncCompositorCommonRendererParams& params) { |
| // Ignore if |renderer_param_version_| is newer than |params.version|. This |
| // comparison takes into account when the unsigned int wraps. |
| if ((renderer_param_version_ - params.version) < 0x80000000) { |
| return; |
| } |
| renderer_param_version_ = params.version; |
| need_animate_scroll_ = params.need_animate_scroll; |
| if (need_begin_frame_ != params.need_begin_frame) { |
| need_begin_frame_ = params.need_begin_frame; |
| UpdateNeedsBeginFrames(); |
| } |
| root_scroll_offset_ = params.total_scroll_offset; |
| |
| if (need_invalidate_count_ != params.need_invalidate_count) { |
| need_invalidate_count_ = params.need_invalidate_count; |
| client_->PostInvalidate(); |
| } |
| |
| if (did_activate_pending_tree_count_ != |
| params.did_activate_pending_tree_count) { |
| did_activate_pending_tree_count_ = params.did_activate_pending_tree_count; |
| client_->DidUpdateContent(); |
| } |
| |
| // Ensure only valid values from compositor are sent to client. |
| // Compositor has page_scale_factor set to 0 before initialization, so check |
| // for that case here. |
| if (params.page_scale_factor) { |
| client_->UpdateRootLayerState( |
| gfx::ScrollOffsetToVector2dF(params.total_scroll_offset), |
| gfx::ScrollOffsetToVector2dF(params.max_scroll_offset), |
| params.scrollable_size, params.page_scale_factor, |
| params.min_page_scale_factor, params.max_page_scale_factor); |
| } |
| } |
| |
| void SynchronousCompositorHost::UpdateNeedsBeginFrames() { |
| rwhva_->OnSetNeedsBeginFrames(is_active_ && need_begin_frame_); |
| } |
| |
| } // namespace content |