| // 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/command_line.h" |
| #include "base/containers/hash_tables.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/shared_memory.h" |
| #include "base/trace_event/trace_event_argument.h" |
| #include "content/browser/android/synchronous_compositor_browser_filter.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/common/android/sync_compositor_statics.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 "content/public/common/content_switches.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/events/blink/did_overscroll_params.h" |
| #include "ui/gfx/skia_util.h" |
| |
| namespace content { |
| |
| // static |
| std::unique_ptr<SynchronousCompositorHost> SynchronousCompositorHost::Create( |
| RenderWidgetHostViewAndroid* rwhva) { |
| if (!rwhva->synchronous_compositor_client()) |
| return nullptr; // Not using sync compositing. |
| |
| base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| bool use_in_proc_software_draw = |
| command_line->HasSwitch(switches::kSingleProcess); |
| return base::WrapUnique(new SynchronousCompositorHost( |
| rwhva, use_in_proc_software_draw)); |
| } |
| |
| SynchronousCompositorHost::SynchronousCompositorHost( |
| RenderWidgetHostViewAndroid* rwhva, |
| bool use_in_proc_software_draw) |
| : rwhva_(rwhva), |
| client_(rwhva->synchronous_compositor_client()), |
| ui_task_runner_(BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)), |
| process_id_(rwhva_->GetRenderWidgetHost()->GetProcess()->GetID()), |
| routing_id_(rwhva_->GetRenderWidgetHost()->GetRoutingID()), |
| sender_(rwhva_->GetRenderWidgetHost()), |
| use_in_process_zero_copy_software_draw_(use_in_proc_software_draw), |
| bytes_limit_(0u), |
| renderer_param_version_(0u), |
| need_animate_scroll_(false), |
| need_invalidate_count_(0u), |
| did_activate_pending_tree_count_(0u) { |
| client_->DidInitializeCompositor(this, process_id_, routing_id_); |
| } |
| |
| SynchronousCompositorHost::~SynchronousCompositorHost() { |
| client_->DidDestroyCompositor(this, process_id_, routing_id_); |
| if (registered_with_filter_) { |
| if (SynchronousCompositorBrowserFilter* filter = GetFilter()) |
| filter->UnregisterHost(this); |
| } |
| } |
| |
| bool SynchronousCompositorHost::OnMessageReceived(const IPC::Message& message) { |
| bool handled = true; |
| IPC_BEGIN_MESSAGE_MAP(SynchronousCompositorHost, message) |
| IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_CompositorFrameSinkCreated, |
| CompositorFrameSinkCreated) |
| IPC_MESSAGE_HANDLER(SyncCompositorHostMsg_UpdateState, ProcessCommonParams) |
| IPC_MESSAGE_UNHANDLED(handled = false) |
| IPC_END_MESSAGE_MAP() |
| return handled; |
| } |
| |
| scoped_refptr<SynchronousCompositor::FrameFuture> |
| SynchronousCompositorHost::DemandDrawHwAsync( |
| const gfx::Size& viewport_size, |
| const gfx::Rect& viewport_rect_for_tile_priority, |
| const gfx::Transform& transform_for_tile_priority) { |
| scoped_refptr<FrameFuture> frame_future = new FrameFuture(); |
| if (compute_scroll_needs_synchronous_draw_) { |
| compute_scroll_needs_synchronous_draw_ = false; |
| auto frame_ptr = base::MakeUnique<Frame>(); |
| *frame_ptr = DemandDrawHw(viewport_size, viewport_rect_for_tile_priority, |
| transform_for_tile_priority); |
| frame_future->SetFrame(std::move(frame_ptr)); |
| return frame_future; |
| } |
| |
| SyncCompositorDemandDrawHwParams params(viewport_size, |
| viewport_rect_for_tile_priority, |
| transform_for_tile_priority); |
| if (SynchronousCompositorBrowserFilter* filter = GetFilter()) { |
| if (!registered_with_filter_) { |
| filter->RegisterHost(this); |
| registered_with_filter_ = true; |
| } |
| filter->SetFrameFuture(routing_id_, frame_future); |
| sender_->Send(new SyncCompositorMsg_DemandDrawHwAsync(routing_id_, params)); |
| } else { |
| frame_future->SetFrame(nullptr); |
| } |
| return frame_future; |
| } |
| |
| SynchronousCompositor::Frame SynchronousCompositorHost::DemandDrawHw( |
| const gfx::Size& viewport_size, |
| const gfx::Rect& viewport_rect_for_tile_priority, |
| const gfx::Transform& transform_for_tile_priority) { |
| SyncCompositorDemandDrawHwParams params(viewport_size, |
| viewport_rect_for_tile_priority, |
| transform_for_tile_priority); |
| uint32_t compositor_frame_sink_id; |
| base::Optional<cc::CompositorFrame> compositor_frame; |
| SyncCompositorCommonRendererParams common_renderer_params; |
| |
| if (!sender_->Send(new SyncCompositorMsg_DemandDrawHw( |
| routing_id_, params, &common_renderer_params, |
| &compositor_frame_sink_id, &compositor_frame))) { |
| return SynchronousCompositor::Frame(); |
| } |
| |
| ProcessCommonParams(common_renderer_params); |
| |
| if (!compositor_frame) |
| return SynchronousCompositor::Frame(); |
| |
| SynchronousCompositor::Frame frame; |
| frame.frame.reset(new cc::CompositorFrame); |
| frame.compositor_frame_sink_id = compositor_frame_sink_id; |
| *frame.frame = std::move(*compositor_frame); |
| UpdateFrameMetaData(frame.frame->metadata.Clone()); |
| return frame; |
| } |
| |
| void SynchronousCompositorHost::UpdateFrameMetaData( |
| cc::CompositorFrameMetadata frame_metadata) { |
| rwhva_->SynchronousFrameMetadata(std::move(frame_metadata)); |
| } |
| |
| SynchronousCompositorBrowserFilter* SynchronousCompositorHost::GetFilter() { |
| return static_cast<RenderProcessHostImpl*>( |
| rwhva_->GetRenderWidgetHost()->GetProcess()) |
| ->synchronous_compositor_filter(); |
| } |
| |
| namespace { |
| |
| class ScopedSetSkCanvas { |
| public: |
| explicit ScopedSetSkCanvas(SkCanvas* canvas) { |
| SynchronousCompositorSetSkCanvas(canvas); |
| } |
| |
| ~ScopedSetSkCanvas() { |
| SynchronousCompositorSetSkCanvas(nullptr); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ScopedSetSkCanvas); |
| }; |
| |
| } |
| |
| bool SynchronousCompositorHost::DemandDrawSwInProc(SkCanvas* canvas) { |
| SyncCompositorCommonRendererParams common_renderer_params; |
| bool success = false; |
| std::unique_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| ScopedSetSkCanvas set_sk_canvas(canvas); |
| SyncCompositorDemandDrawSwParams params; // Unused. |
| if (!sender_->Send(new SyncCompositorMsg_DemandDrawSw( |
| routing_id_, params, &success, &common_renderer_params, |
| frame.get()))) { |
| return false; |
| } |
| if (!success) |
| return false; |
| ProcessCommonParams(common_renderer_params); |
| UpdateFrameMetaData(std::move(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->getDeviceClipBounds(); |
| 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; |
| |
| std::unique_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame); |
| SyncCompositorCommonRendererParams common_renderer_params; |
| bool success = false; |
| if (!sender_->Send(new SyncCompositorMsg_DemandDrawSw( |
| routing_id_, params, &success, &common_renderer_params, |
| frame.get()))) { |
| return false; |
| } |
| ScopedSendZeroMemory send_zero_memory(this); |
| if (!success) |
| return false; |
| |
| ProcessCommonParams(common_renderer_params); |
| UpdateFrameMetaData(std::move(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(); |
| std::unique_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; |
| } |
| |
| bool success = false; |
| SyncCompositorCommonRendererParams common_renderer_params; |
| if (!sender_->Send(new SyncCompositorMsg_SetSharedMemory( |
| routing_id_, 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( |
| uint32_t compositor_frame_sink_id, |
| const cc::ReturnedResourceArray& resources) { |
| DCHECK(!resources.empty()); |
| sender_->Send(new SyncCompositorMsg_ReclaimResources( |
| routing_id_, compositor_frame_sink_id, resources)); |
| } |
| |
| void SynchronousCompositorHost::SetMemoryPolicy(size_t bytes_limit) { |
| if (bytes_limit_ == bytes_limit) |
| return; |
| |
| if (sender_->Send( |
| new SyncCompositorMsg_SetMemoryPolicy(routing_id_, bytes_limit))) { |
| bytes_limit_ = bytes_limit; |
| } |
| } |
| |
| void SynchronousCompositorHost::DidChangeRootLayerScrollOffset( |
| const gfx::ScrollOffset& root_offset) { |
| if (root_scroll_offset_ == root_offset) |
| return; |
| root_scroll_offset_ = root_offset; |
| sender_->Send( |
| new SyncCompositorMsg_SetScroll(routing_id_, root_scroll_offset_)); |
| } |
| |
| void SynchronousCompositorHost::SynchronouslyZoomBy(float zoom_delta, |
| const gfx::Point& anchor) { |
| SyncCompositorCommonRendererParams common_renderer_params; |
| if (!sender_->Send(new SyncCompositorMsg_ZoomBy( |
| routing_id_, zoom_delta, anchor, &common_renderer_params))) { |
| return; |
| } |
| ProcessCommonParams(common_renderer_params); |
| } |
| |
| void SynchronousCompositorHost::OnComputeScroll( |
| base::TimeTicks animation_time) { |
| if (!need_animate_scroll_) |
| return; |
| need_animate_scroll_ = false; |
| |
| SyncCompositorCommonRendererParams common_renderer_params; |
| sender_->Send( |
| new SyncCompositorMsg_ComputeScroll(routing_id_, animation_time)); |
| compute_scroll_needs_synchronous_draw_ = true; |
| } |
| |
| void SynchronousCompositorHost::DidOverscroll( |
| const ui::DidOverscrollParams& over_scroll_params) { |
| client_->DidOverscroll(this, over_scroll_params.accumulated_overscroll, |
| over_scroll_params.latest_overscroll_delta, |
| over_scroll_params.current_fling_velocity); |
| } |
| |
| void SynchronousCompositorHost::DidSendBeginFrame( |
| ui::WindowAndroid* window_android) { |
| compute_scroll_needs_synchronous_draw_ = false; |
| if (SynchronousCompositorBrowserFilter* filter = GetFilter()) |
| filter->SyncStateAfterVSync(window_android, this); |
| } |
| |
| void SynchronousCompositorHost::CompositorFrameSinkCreated() { |
| // New CompositorFrameSink is not aware of state from Browser side. So need to |
| // re-send all browser side state here. |
| sender_->Send( |
| new SyncCompositorMsg_SetMemoryPolicy(routing_id_, bytes_limit_)); |
| } |
| |
| 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; |
| root_scroll_offset_ = params.total_scroll_offset; |
| |
| if (need_invalidate_count_ != params.need_invalidate_count) { |
| need_invalidate_count_ = params.need_invalidate_count; |
| client_->PostInvalidate(this); |
| } |
| |
| 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(this); |
| } |
| |
| // 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( |
| this, 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); |
| } |
| } |
| |
| } // namespace content |