| // Copyright 2015 The Chromium Authors |
| // 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 <algorithm> |
| #include <atomic> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/shared_memory_mapping.h" |
| #include "base/memory/writable_shared_memory_region.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/trace_event/traced_value.h" |
| #include "content/browser/android/synchronous_compositor_sync_call_bridge.h" |
| #include "content/browser/bad_message.h" |
| #include "content/browser/browser_main_loop.h" |
| #include "content/browser/renderer_host/compositor_dependencies_android.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_statics.h" |
| #include "content/common/features.h" |
| #include "content/public/browser/android/synchronous_compositor_client.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/common/content_switches.h" |
| #include "gpu/ipc/client/gpu_channel_host.h" |
| #include "ipc/ipc_sender.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "mojo/public/cpp/bindings/sync_call_restrictions.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkImage.h" |
| #include "third_party/skia/include/core/SkImageInfo.h" |
| #include "third_party/skia/include/core/SkPixmap.h" |
| #include "third_party/skia/include/core/SkRect.h" |
| #include "ui/events/blink/did_overscroll_params.h" |
| #include "ui/gfx/geometry/skia_conversions.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| bool g_viz_established = false; |
| |
| // TODO(vasilyt): Create BrowserCompositor for webview and move it there? |
| void EstablishVizConnection( |
| scoped_refptr<gpu::GpuChannelHost> gpu_channel_host) { |
| content::CompositorDependenciesAndroid::Get() |
| .TryEstablishVizConnectionIfNeeded(); |
| g_viz_established = true; |
| } |
| |
| void EstablishGpuChannelToEstablishVizConnection() { |
| if (g_viz_established) |
| return; |
| |
| content::BrowserMainLoop::GetInstance() |
| ->gpu_channel_establish_factory() |
| ->EstablishGpuChannel(base::BindOnce(&EstablishVizConnection)); |
| } |
| |
| } // namespace |
| |
| // This class runs on the IO thread and is destroyed when the renderer |
| // side closes the mojo channel. |
| class SynchronousCompositorControlHost |
| : public blink::mojom::SynchronousCompositorControlHost { |
| public: |
| SynchronousCompositorControlHost( |
| scoped_refptr<SynchronousCompositorSyncCallBridge> bridge, |
| int process_id) |
| : bridge_(std::move(bridge)), process_id_(process_id) {} |
| |
| ~SynchronousCompositorControlHost() override { |
| bridge_->RemoteClosedOnIOThread(); |
| } |
| |
| static void Create( |
| mojo::PendingReceiver<blink::mojom::SynchronousCompositorControlHost> |
| receiver, |
| scoped_refptr<SynchronousCompositorSyncCallBridge> bridge, |
| int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| GetIOThreadTaskRunner({})->PostTask( |
| FROM_HERE, base::BindOnce(&CreateOnIOThread, std::move(receiver), |
| std::move(bridge), process_id)); |
| } |
| |
| static void CreateOnIOThread( |
| mojo::PendingReceiver<blink::mojom::SynchronousCompositorControlHost> |
| receiver, |
| scoped_refptr<SynchronousCompositorSyncCallBridge> bridge, |
| int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| auto host_control_receiver = mojo::MakeSelfOwnedReceiver( |
| std::make_unique<SynchronousCompositorControlHost>(bridge, process_id), |
| std::move(receiver)); |
| bridge->SetHostControlReceiverOnIOThread(host_control_receiver); |
| } |
| |
| // SynchronousCompositorControlHost overrides. |
| void ReturnFrame( |
| uint32_t layer_tree_frame_sink_id, |
| uint32_t metadata_version, |
| const std::optional<viz::LocalSurfaceId>& local_surface_id, |
| std::optional<viz::CompositorFrame> frame, |
| std::optional<viz::HitTestRegionList> hit_test_region_list) override { |
| if (frame && (!local_surface_id || !local_surface_id->is_valid())) { |
| bad_message::ReceivedBadMessage( |
| process_id_, bad_message::SYNC_COMPOSITOR_NO_LOCAL_SURFACE_ID); |
| return; |
| } |
| if (!bridge_->ReceiveFrameOnIOThread( |
| layer_tree_frame_sink_id, metadata_version, local_surface_id, |
| std::move(frame), std::move(hit_test_region_list))) { |
| bad_message::ReceivedBadMessage( |
| process_id_, bad_message::SYNC_COMPOSITOR_NO_FUTURE_FRAME); |
| } |
| } |
| |
| void BeginFrameResponse( |
| blink::mojom::SyncCompositorCommonRendererParamsPtr params) override { |
| if (!bridge_->BeginFrameResponseOnIOThread(std::move(params))) { |
| bad_message::ReceivedBadMessage( |
| process_id_, bad_message::SYNC_COMPOSITOR_NO_BEGIN_FRAME); |
| } |
| } |
| |
| private: |
| scoped_refptr<SynchronousCompositorSyncCallBridge> bridge_; |
| const int process_id_; |
| }; |
| |
| // static |
| std::unique_ptr<SynchronousCompositorHost> SynchronousCompositorHost::Create( |
| RenderWidgetHostViewAndroid* rwhva, |
| const viz::FrameSinkId& frame_sink_id, |
| viz::HostFrameSinkManager* host_frame_sink_manager) { |
| 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, frame_sink_id, host_frame_sink_manager, |
| use_in_proc_software_draw)); |
| } |
| |
| SynchronousCompositorHost::SynchronousCompositorHost( |
| RenderWidgetHostViewAndroid* rwhva, |
| const viz::FrameSinkId& frame_sink_id, |
| viz::HostFrameSinkManager* host_frame_sink_manager, |
| bool use_in_proc_software_draw) |
| : rwhva_(rwhva), |
| client_(rwhva->synchronous_compositor_client()), |
| frame_sink_id_(frame_sink_id), |
| host_frame_sink_manager_(host_frame_sink_manager), |
| use_in_process_zero_copy_software_draw_(use_in_proc_software_draw), |
| bytes_limit_(0u), |
| renderer_param_version_(0u), |
| need_invalidate_count_(0u), |
| invalidate_needs_draw_(false), |
| did_activate_pending_tree_count_(0u) { |
| EstablishGpuChannelToEstablishVizConnection(); |
| client_->DidInitializeCompositor(this, frame_sink_id_); |
| host_frame_sink_manager_->CreateHitTestQueryForSynchronousCompositor( |
| frame_sink_id_); |
| bridge_ = new SynchronousCompositorSyncCallBridge(this); |
| } |
| |
| SynchronousCompositorHost::~SynchronousCompositorHost() { |
| if (outstanding_begin_frame_requests_ && begin_frame_source_) |
| begin_frame_source_->RemoveObserver(this); |
| host_frame_sink_manager_->EraseHitTestQueryForSynchronousCompositor( |
| frame_sink_id_); |
| client_->DidDestroyCompositor(this, frame_sink_id_); |
| bridge_->HostDestroyedOnUIThread(); |
| } |
| |
| void SynchronousCompositorHost::InitMojo() { |
| mojo::PendingRemote<blink::mojom::SynchronousCompositorControlHost> |
| host_control; |
| |
| SynchronousCompositorControlHost::Create( |
| host_control.InitWithNewPipeAndPassReceiver(), bridge_, |
| rwhva_->GetRenderWidgetHost()->GetProcess()->GetID()); |
| rwhva_->host()->GetWidgetInputHandler()->AttachSynchronousCompositor( |
| std::move(host_control), host_receiver_.BindNewEndpointAndPassRemote(), |
| sync_compositor_.BindNewEndpointAndPassReceiver()); |
| } |
| |
| bool SynchronousCompositorHost::IsReadyForSynchronousCall() { |
| bool res = bridge_->IsRemoteReadyOnUIThread(); |
| DCHECK(!res || GetSynchronousCompositor()); |
| return res; |
| } |
| |
| void SynchronousCompositorHost::OnCompositorVisible() { |
| CompositorDependenciesAndroid::Get().OnSynchronousCompositorVisible(); |
| } |
| |
| void SynchronousCompositorHost::OnCompositorHidden() { |
| CompositorDependenciesAndroid::Get().OnSynchronousCompositorHidden(); |
| } |
| |
| 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) { |
| draw_hw_called_ = true; |
| invalidate_needs_draw_ = false; |
| num_invalidates_since_last_draw_ = 0u; |
| scoped_refptr<FrameFuture> frame_future = new FrameFuture(); |
| if (!allow_async_draw_) { |
| allow_async_draw_ = allow_async_draw_ || IsReadyForSynchronousCall(); |
| auto frame_ptr = std::make_unique<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; |
| } |
| |
| blink::mojom::SyncCompositorDemandDrawHwParamsPtr params = |
| blink::mojom::SyncCompositorDemandDrawHwParams::New( |
| viewport_size, viewport_rect_for_tile_priority, |
| transform_for_tile_priority, |
| /*need_new_local_surface_id=*/was_evicted_); |
| |
| was_evicted_ = false; |
| |
| blink::mojom::SynchronousCompositor* compositor = GetSynchronousCompositor(); |
| if (!bridge_->SetFrameFutureOnUIThread(frame_future)) { |
| frame_future->SetFrame(nullptr); |
| } else { |
| DCHECK(compositor); |
| compositor->DemandDrawHwAsync(std::move(params)); |
| } |
| 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) { |
| blink::mojom::SyncCompositorDemandDrawHwParamsPtr params = |
| blink::mojom::SyncCompositorDemandDrawHwParams::New( |
| viewport_size, viewport_rect_for_tile_priority, |
| transform_for_tile_priority, |
| /*need_new_local_surface_id=*/was_evicted_); |
| |
| was_evicted_ = false; |
| |
| uint32_t layer_tree_frame_sink_id; |
| uint32_t metadata_version = 0u; |
| std::optional<viz::LocalSurfaceId> local_surface_id; |
| std::optional<viz::CompositorFrame> compositor_frame; |
| std::optional<viz::HitTestRegionList> hit_test_region_list; |
| blink::mojom::SyncCompositorCommonRendererParamsPtr common_renderer_params; |
| |
| { |
| mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope |
| allow_base_sync_primitives; |
| if (!IsReadyForSynchronousCall() || |
| !GetSynchronousCompositor()->DemandDrawHw( |
| std::move(params), &common_renderer_params, |
| &layer_tree_frame_sink_id, &metadata_version, &local_surface_id, |
| &compositor_frame, &hit_test_region_list)) { |
| return SynchronousCompositor::Frame(); |
| } |
| } |
| |
| UpdateState(std::move(common_renderer_params)); |
| |
| if (compositor_frame) { |
| if (!local_surface_id || !local_surface_id->is_valid()) { |
| bad_message::ReceivedBadMessage( |
| rwhva_->GetRenderWidgetHost()->GetProcess()->GetID(), |
| bad_message::SYNC_COMPOSITOR_NO_LOCAL_SURFACE_ID); |
| return SynchronousCompositor::Frame(); |
| } |
| } else { |
| return SynchronousCompositor::Frame(); |
| } |
| |
| SynchronousCompositor::Frame frame; |
| frame.frame = std::make_unique<viz::CompositorFrame>(); |
| frame.layer_tree_frame_sink_id = layer_tree_frame_sink_id; |
| if (local_surface_id) |
| frame.local_surface_id = local_surface_id.value(); |
| *frame.frame = std::move(*compositor_frame); |
| frame.hit_test_region_list = std::move(hit_test_region_list); |
| UpdateFrameMetaData(metadata_version, frame.frame->metadata.Clone(), |
| std::move(local_surface_id)); |
| return frame; |
| } |
| |
| void SynchronousCompositorHost::UpdateFrameMetaData( |
| uint32_t version, |
| viz::CompositorFrameMetadata frame_metadata, |
| std::optional<viz::LocalSurfaceId> new_local_surface_id) { |
| // Ignore if |frame_metadata_version_| is newer than |version|. This |
| // comparison takes into account when the unsigned int wraps. |
| if ((frame_metadata_version_ - version) < 0x80000000) { |
| return; |
| } |
| frame_metadata_version_ = version; |
| if (new_local_surface_id) |
| local_surface_id_ = new_local_surface_id.value(); |
| } |
| |
| namespace { |
| |
| class ScopedSetSkCanvas { |
| public: |
| explicit ScopedSetSkCanvas(SkCanvas* canvas) { |
| SynchronousCompositorSetSkCanvas(canvas); |
| } |
| |
| ScopedSetSkCanvas(const ScopedSetSkCanvas&) = delete; |
| ScopedSetSkCanvas& operator=(const ScopedSetSkCanvas&) = delete; |
| |
| ~ScopedSetSkCanvas() { |
| SynchronousCompositorSetSkCanvas(nullptr); |
| } |
| }; |
| |
| } |
| |
| bool SynchronousCompositorHost::DemandDrawSwInProc(SkCanvas* canvas) { |
| mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope |
| allow_base_sync_primitives; |
| blink::mojom::SyncCompositorCommonRendererParamsPtr common_renderer_params; |
| std::optional<viz::CompositorFrameMetadata> metadata; |
| ScopedSetSkCanvas set_sk_canvas(canvas); |
| blink::mojom::SyncCompositorDemandDrawSwParamsPtr params = |
| blink::mojom::SyncCompositorDemandDrawSwParams::New(); // Unused. |
| uint32_t metadata_version = 0u; |
| invalidate_needs_draw_ = false; |
| if (!IsReadyForSynchronousCall() || |
| !GetSynchronousCompositor()->DemandDrawSw(std::move(params), |
| &common_renderer_params, |
| &metadata_version, &metadata)) |
| return false; |
| if (!metadata) |
| return false; |
| UpdateState(std::move(common_renderer_params)); |
| UpdateFrameMetaData(metadata_version, std::move(*metadata), std::nullopt); |
| return true; |
| } |
| |
| class SynchronousCompositorHost::ScopedSendZeroMemory { |
| public: |
| ScopedSendZeroMemory(SynchronousCompositorHost* host) : host_(host) {} |
| |
| ScopedSendZeroMemory(const ScopedSendZeroMemory&) = delete; |
| ScopedSendZeroMemory& operator=(const ScopedSendZeroMemory&) = delete; |
| |
| ~ScopedSendZeroMemory() { host_->SendZeroMemory(); } |
| |
| private: |
| const raw_ptr<SynchronousCompositorHost> host_; |
| }; |
| |
| struct SynchronousCompositorHost::SharedMemoryWithSize { |
| base::WritableSharedMemoryMapping shared_memory; |
| const size_t stride; |
| const size_t buffer_size; |
| |
| SharedMemoryWithSize(size_t stride, size_t buffer_size) |
| : stride(stride), buffer_size(buffer_size) {} |
| |
| SharedMemoryWithSize(const SharedMemoryWithSize&) = delete; |
| SharedMemoryWithSize& operator=(const SharedMemoryWithSize&) = delete; |
| }; |
| |
| bool SynchronousCompositorHost::DemandDrawSw(SkCanvas* canvas, |
| bool software_canvas) { |
| num_invalidates_since_last_draw_ = 0u; |
| if (use_in_process_zero_copy_software_draw_) |
| return DemandDrawSwInProc(canvas); |
| |
| blink::mojom::SyncCompositorDemandDrawSwParamsPtr params = |
| blink::mojom::SyncCompositorDemandDrawSwParams::New(); |
| params->size = gfx::Size(canvas->getBaseLayerSize().width(), |
| canvas->getBaseLayerSize().height()); |
| SkIRect canvas_clip = canvas->getDeviceClipBounds(); |
| params->clip = gfx::SkIRectToRect(canvas_clip); |
| params->transform = gfx::SkMatrixToTransform(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.computeByteSize(stride); |
| if (SkImageInfo::ByteSizeOverflowed(buffer_size)) |
| return false; |
| |
| SetSoftwareDrawSharedMemoryIfNeeded(stride, buffer_size); |
| if (!software_draw_shm_) |
| return false; |
| |
| std::optional<viz::CompositorFrameMetadata> metadata; |
| uint32_t metadata_version = 0u; |
| blink::mojom::SyncCompositorCommonRendererParamsPtr common_renderer_params; |
| { |
| mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope |
| allow_base_sync_primitives; |
| if (!IsReadyForSynchronousCall() || |
| !GetSynchronousCompositor()->DemandDrawSw( |
| std::move(params), &common_renderer_params, &metadata_version, |
| &metadata)) { |
| return false; |
| } |
| } |
| ScopedSendZeroMemory send_zero_memory(this); |
| if (!metadata) |
| return false; |
| |
| UpdateState(std::move(common_renderer_params)); |
| UpdateFrameMetaData(metadata_version, std::move(*metadata), std::nullopt); |
| |
| SkBitmap bitmap; |
| SkPixmap pixmap(info, software_draw_shm_->shared_memory.memory(), stride); |
| |
| bool pixels_released = false; |
| { |
| TRACE_EVENT0("browser", "DrawBitmap"); |
| canvas->save(); |
| canvas->resetMatrix(); |
| sk_sp<SkImage> image; |
| // Software canvas will draw immediately, so it's safe to avoid this copy. |
| if (software_canvas) { |
| auto mark_bool = [](const void* pixels, void* context) { |
| *static_cast<bool*>(context) = true; |
| }; |
| image = SkImages::RasterFromPixmap(pixmap, mark_bool, &pixels_released); |
| } else { |
| image = SkImages::RasterFromPixmapCopy(pixmap); |
| } |
| canvas->drawImage(image, 0, 0); |
| canvas->restore(); |
| } |
| |
| if (software_canvas) { |
| // It could lead to UAF if this CHECK fails, hence it's a release CHECK. |
| CHECK(pixels_released); |
| } |
| |
| 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(); |
| auto software_draw_shm = |
| std::make_unique<SharedMemoryWithSize>(stride, buffer_size); |
| base::WritableSharedMemoryRegion shm_region; |
| { |
| TRACE_EVENT1("browser", "AllocateSharedMemory", "buffer_size", buffer_size); |
| shm_region = base::WritableSharedMemoryRegion::Create(buffer_size); |
| if (!shm_region.IsValid()) |
| return; |
| |
| software_draw_shm->shared_memory = shm_region.Map(); |
| if (!software_draw_shm->shared_memory.IsValid()) |
| return; |
| } |
| |
| bool success = false; |
| blink::mojom::SyncCompositorCommonRendererParamsPtr common_renderer_params; |
| { |
| mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope |
| allow_base_sync_primitives; |
| if (!IsReadyForSynchronousCall() || |
| !GetSynchronousCompositor()->SetSharedMemory( |
| std::move(shm_region), &success, &common_renderer_params) || |
| !success) { |
| return; |
| } |
| } |
| software_draw_shm_ = std::move(software_draw_shm); |
| UpdateState(std::move(common_renderer_params)); |
| } |
| |
| void SynchronousCompositorHost::SendZeroMemory() { |
| // No need to check return value. |
| if (blink::mojom::SynchronousCompositor* compositor = |
| GetSynchronousCompositor()) |
| compositor->ZeroSharedMemory(); |
| } |
| |
| void SynchronousCompositorHost::ReturnResources( |
| uint32_t layer_tree_frame_sink_id, |
| std::vector<viz::ReturnedResource> resources) { |
| DCHECK(!resources.empty()); |
| if (blink::mojom::SynchronousCompositor* compositor = |
| GetSynchronousCompositor()) |
| compositor->ReclaimResources(layer_tree_frame_sink_id, |
| std::move(resources)); |
| } |
| |
| void SynchronousCompositorHost::OnCompositorFrameTransitionDirectiveProcessed( |
| uint32_t layer_tree_frame_sink_id, |
| uint32_t sequence_id) { |
| if (blink::mojom::SynchronousCompositor* compositor = |
| GetSynchronousCompositor()) { |
| compositor->OnCompositorFrameTransitionDirectiveProcessed( |
| layer_tree_frame_sink_id, sequence_id); |
| } |
| } |
| |
| void SynchronousCompositorHost::DidPresentCompositorFrames( |
| viz::FrameTimingDetailsMap timing_details, |
| uint32_t frame_token) { |
| timing_details_.insert(timing_details.begin(), timing_details.end()); |
| if (!timing_details_.empty()) |
| AddBeginFrameRequest(BEGIN_FRAME); |
| } |
| |
| void SynchronousCompositorHost::SetMemoryPolicy(size_t bytes_limit) { |
| if (bytes_limit_ == bytes_limit) |
| return; |
| |
| bytes_limit_ = bytes_limit; |
| if (blink::mojom::SynchronousCompositor* compositor = |
| GetSynchronousCompositor()) |
| compositor->SetMemoryPolicy(bytes_limit_); |
| } |
| |
| void SynchronousCompositorHost::DidChangeRootLayerScrollOffset( |
| const gfx::PointF& root_offset) { |
| if (root_scroll_offset_ == root_offset) |
| return; |
| root_scroll_offset_ = root_offset; |
| if (blink::mojom::SynchronousCompositor* compositor = |
| GetSynchronousCompositor()) |
| compositor->SetScroll(root_scroll_offset_); |
| } |
| |
| void SynchronousCompositorHost::SynchronouslyZoomBy(float zoom_delta, |
| const gfx::Point& anchor) { |
| blink::mojom::SyncCompositorCommonRendererParamsPtr common_renderer_params; |
| { |
| mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call; |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope |
| allow_base_sync_primitives; |
| if (!IsReadyForSynchronousCall() || |
| !GetSynchronousCompositor()->ZoomBy(zoom_delta, anchor, |
| &common_renderer_params)) { |
| return; |
| } |
| } |
| UpdateState(std::move(common_renderer_params)); |
| } |
| |
| void SynchronousCompositorHost::OnComputeScroll( |
| base::TimeTicks animation_time) { |
| on_compute_scroll_called_ = true; |
| } |
| |
| ui::ViewAndroid::CopyViewCallback |
| SynchronousCompositorHost::GetCopyViewCallback() { |
| // Unretained is safe since callback is helped by ViewAndroid which has same |
| // lifetime as this, and client outlives this. |
| return base::BindRepeating(&SynchronousCompositorClient::CopyOutput, |
| base::Unretained(client_), this); |
| } |
| |
| 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::SetNeedsBeginFrames(bool needs_begin_frames) { |
| TRACE_EVENT1("cc", "SynchronousCompositorHost::SetNeedsBeginFrames", |
| "needs_begin_frames", needs_begin_frames); |
| if (needs_begin_frames) |
| AddBeginFrameRequest(PERSISTENT_BEGIN_FRAME); |
| else |
| ClearBeginFrameRequest(PERSISTENT_BEGIN_FRAME); |
| } |
| |
| void SynchronousCompositorHost::SetThreadIds( |
| const std::vector<int32_t>& thread_ids) { |
| client_->SetThreadIds(thread_ids); |
| } |
| |
| void SynchronousCompositorHost::LayerTreeFrameSinkCreated() { |
| bridge_->RemoteReady(); |
| |
| // New LayerTreeFrameSink is not aware of state from Browser side. So need to |
| // re-send all browser side state here. |
| blink::mojom::SynchronousCompositor* compositor = GetSynchronousCompositor(); |
| DCHECK(compositor); |
| compositor->SetMemoryPolicy(bytes_limit_); |
| |
| SendBeginFramePaused(); |
| } |
| |
| void SynchronousCompositorHost::UpdateState( |
| blink::mojom::SyncCompositorCommonRendererParamsPtr 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; |
| root_scroll_offset_ = params->total_scroll_offset; |
| max_scroll_offset_ = params->max_scroll_offset; |
| scrollable_size_ = params->scrollable_size; |
| page_scale_factor_ = params->page_scale_factor; |
| min_page_scale_factor_ = params->min_page_scale_factor; |
| max_page_scale_factor_ = params->max_page_scale_factor; |
| invalidate_needs_draw_ |= params->invalidate_needs_draw; |
| |
| if (need_invalidate_count_ != params->need_invalidate_count) { |
| need_invalidate_count_ = params->need_invalidate_count; |
| if (invalidate_needs_draw_) { |
| num_invalidates_since_last_draw_++; |
| client_->PostInvalidate(this); |
| } else { |
| GetSynchronousCompositor()->WillSkipDraw(); |
| } |
| } |
| |
| 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); |
| } |
| |
| UpdateRootLayerStateOnClient(); |
| } |
| |
| void SynchronousCompositorHost::DidBecomeActive() { |
| UpdateRootLayerStateOnClient(); |
| } |
| |
| void SynchronousCompositorHost::UpdateRootLayerStateOnClient() { |
| // 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 (page_scale_factor_) { |
| client_->UpdateRootLayerState( |
| this, root_scroll_offset_, max_scroll_offset_, scrollable_size_, |
| page_scale_factor_, min_page_scale_factor_, max_page_scale_factor_); |
| } |
| } |
| |
| RenderProcessHost* SynchronousCompositorHost::GetRenderProcessHost() { |
| return rwhva_->GetRenderWidgetHost()->GetProcess(); |
| } |
| |
| blink::mojom::SynchronousCompositor* |
| SynchronousCompositorHost::GetSynchronousCompositor() { |
| if (!sync_compositor_) |
| return nullptr; |
| return sync_compositor_.get(); |
| } |
| |
| void SynchronousCompositorHost::OnBeginFrame(const viz::BeginFrameArgs& args) { |
| TRACE_EVENT0("cc,benchmark", "SynchronousCompositorHost::OnBeginFrame"); |
| |
| // In sync mode, we disregard missed frame args to ensure that |
| // SynchronousCompositorBrowserFilter::SyncStateAfterVSync will be called |
| // during WindowAndroid::WindowBeginFrameSource::OnVSync() observer iteration. |
| if (args.type == viz::BeginFrameArgs::MISSED) |
| return; |
| |
| // We need to check this before |outstanding_begin_frame_requests_| will be |
| // changed by ClearBeginFrameRequest below. |
| bool needs_begin_frame = |
| (outstanding_begin_frame_requests_ & BEGIN_FRAME) || |
| (outstanding_begin_frame_requests_ & PERSISTENT_BEGIN_FRAME); |
| |
| // Update |last_begin_frame_args_| before handling |
| // |outstanding_begin_frame_requests_| to prevent the BeginFrameSource from |
| // sending the same MISSED args in infinite recursion. |
| last_begin_frame_args_ = args; |
| |
| ClearBeginFrameRequest(BEGIN_FRAME); |
| |
| if (on_compute_scroll_called_ || !rwhva_->is_currently_scrolling_viewport()) { |
| rwhva_->host()->ProgressFlingIfNeeded(args.frame_time); |
| } else if (base::FeatureList::IsEnabled( |
| features::kWebViewSuppressTapDuringFling)) { |
| // Normally, `OnComputeScroll` is called after `OnBeginFrame`, but before |
| // `DemandDrawHwAsync`. So `OnBeginFrame` calls before the first draw will |
| // end up here regardless of whether `OnComputeScroll` will be called. If |
| // these frames contain fling, then don't cancel fling prematurely. Note |
| // normally fling cannot happen from user interaction this way because touch |
| // scroll happens before fling. |
| if (draw_hw_called_) { |
| // If we are not ticking flings ourselves, also reset the tracking state |
| // for fling so the first tap during / after fling is not suppressed. |
| rwhva_->host()->StopFling(); |
| } |
| } |
| |
| if (needs_begin_frame) { |
| SendBeginFrame(args); |
| } |
| } |
| |
| const viz::BeginFrameArgs& SynchronousCompositorHost::LastUsedBeginFrameArgs() |
| const { |
| return last_begin_frame_args_; |
| } |
| |
| void SynchronousCompositorHost::OnBeginFrameSourcePausedChanged(bool paused) { |
| if (paused != begin_frame_paused_) { |
| begin_frame_paused_ = paused; |
| SendBeginFramePaused(); |
| } |
| } |
| |
| bool SynchronousCompositorHost::WantsAnimateOnlyBeginFrames() const { |
| return false; |
| } |
| |
| void SynchronousCompositorHost::SendBeginFramePaused() { |
| if (blink::mojom::SynchronousCompositor* compositor = |
| GetSynchronousCompositor()) |
| compositor->SetBeginFrameSourcePaused(begin_frame_paused_); |
| } |
| |
| void SynchronousCompositorHost::SendBeginFrame(viz::BeginFrameArgs args) { |
| if (num_invalidates_since_last_draw_ > 5u) { |
| // Throttle begin frames if there has been no draws in response to |
| // invalidates. This can happen if webview is detached or offscreen. There |
| // are cases where renderer is still expected to make progress. In this |
| // case renderer receives no back pressure so reduce the frequency of begin |
| // frames to avoid unnecessary work. |
| if (num_begin_frames_to_skip_) { |
| TRACE_EVENT_INSTANT0("cc", |
| "SynchronousCompositorHost::SendBeginFrame_skipped", |
| TRACE_EVENT_SCOPE_THREAD); |
| num_begin_frames_to_skip_--; |
| return; |
| } else { |
| num_begin_frames_to_skip_ = |
| std::min(num_invalidates_since_last_draw_ / 5, 250u); |
| } |
| } else { |
| num_begin_frames_to_skip_ = 0u; |
| } |
| |
| TRACE_EVENT2("cc", "SynchronousCompositorHost::SendBeginFrame", |
| "frame_number", args.frame_id.sequence_number, "frame_time_us", |
| args.frame_time); |
| |
| if (!bridge_->WaitAfterVSyncOnUIThread()) |
| return; |
| |
| blink::mojom::SynchronousCompositor* compositor = GetSynchronousCompositor(); |
| DCHECK(compositor); |
| compositor->BeginFrame(args, timing_details_); |
| timing_details_.clear(); |
| } |
| |
| void SynchronousCompositorHost::SetBeginFrameSource( |
| viz::BeginFrameSource* begin_frame_source) { |
| DCHECK(!begin_frame_source_); |
| DCHECK(!outstanding_begin_frame_requests_); |
| begin_frame_source_ = begin_frame_source; |
| } |
| |
| void SynchronousCompositorHost::AddBeginFrameRequest( |
| BeginFrameRequestType request) { |
| uint32_t prior_requests = outstanding_begin_frame_requests_; |
| outstanding_begin_frame_requests_ = prior_requests | request; |
| |
| // Note that if we don't currently have a BeginFrameSource, outstanding begin |
| // frame requests will be pushed if/when we get one during |
| // |StartObservingRootWindow()| or when the DelegatedFrameHostAndroid sets it. |
| viz::BeginFrameSource* source = begin_frame_source_; |
| if (source && outstanding_begin_frame_requests_ && !prior_requests) |
| source->AddObserver(this); |
| } |
| |
| void SynchronousCompositorHost::ClearBeginFrameRequest( |
| BeginFrameRequestType request) { |
| uint32_t prior_requests = outstanding_begin_frame_requests_; |
| outstanding_begin_frame_requests_ = prior_requests & ~request; |
| |
| viz::BeginFrameSource* source = begin_frame_source_; |
| if (source && !outstanding_begin_frame_requests_ && prior_requests) |
| source->RemoveObserver(this); |
| } |
| |
| void SynchronousCompositorHost::RequestOneBeginFrame() { |
| AddBeginFrameRequest(BEGIN_FRAME); |
| } |
| |
| void SynchronousCompositorHost::AddBeginFrameCompletionCallback( |
| base::OnceClosure callback) { |
| client_->AddBeginFrameCompletionCallback(std::move(callback)); |
| } |
| |
| viz::SurfaceId SynchronousCompositorHost::GetSurfaceId() const { |
| return viz::SurfaceId(frame_sink_id_, local_surface_id_); |
| } |
| |
| void SynchronousCompositorHost::DidInvalidate() { |
| invalidate_needs_draw_ = true; |
| } |
| |
| void SynchronousCompositorHost::WasEvicted() { |
| was_evicted_ = true; |
| } |
| |
| } // namespace content |