| // 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 "third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h" |
| |
| #include <vector> |
| |
| #include "base/auto_reset.h" |
| #include "base/bind.h" |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/notreached.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "cc/trees/layer_tree_frame_sink_client.h" |
| #include "components/power_scheduler/power_mode.h" |
| #include "components/power_scheduler/power_mode_arbiter.h" |
| #include "components/power_scheduler/power_mode_voter.h" |
| #include "components/viz/common/display/renderer_settings.h" |
| #include "components/viz/common/features.h" |
| #include "components/viz/common/gpu/context_provider.h" |
| #include "components/viz/common/quads/compositor_frame.h" |
| #include "components/viz/common/quads/compositor_render_pass.h" |
| #include "components/viz/common/quads/surface_draw_quad.h" |
| #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" |
| #include "components/viz/service/display/display.h" |
| #include "components/viz/service/display/output_surface.h" |
| #include "components/viz/service/display/output_surface_frame.h" |
| #include "components/viz/service/display/overlay_processor_stub.h" |
| #include "components/viz/service/display/software_output_device.h" |
| #include "components/viz/service/display/texture_deleter.h" |
| #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" |
| #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h" |
| #include "gpu/command_buffer/client/context_support.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "gpu/command_buffer/common/gpu_memory_allocation.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gfx/skia_util.h" |
| #include "ui/gfx/transform.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| const viz::FrameSinkId kRootFrameSinkId(1, 1); |
| const viz::FrameSinkId kChildFrameSinkId(1, 2); |
| |
| // Do not limit number of resources, so use an unrealistically high value. |
| const size_t kNumResourcesLimit = 10 * 1000 * 1000; |
| |
| class SoftwareDevice : public viz::SoftwareOutputDevice { |
| public: |
| SoftwareDevice(SkCanvas** canvas) : canvas_(canvas) {} |
| |
| void Resize(const gfx::Size& pixel_size, float device_scale_factor) override { |
| // Intentional no-op: canvas size is controlled by the embedder. |
| } |
| SkCanvas* BeginPaint(const gfx::Rect& damage_rect) override { |
| DCHECK(*canvas_) << "BeginPaint with no canvas set"; |
| return *canvas_; |
| } |
| void EndPaint() override {} |
| |
| private: |
| SkCanvas** canvas_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SoftwareDevice); |
| }; |
| |
| // This is used with resourceless software draws. |
| class SoftwareCompositorFrameSinkClient |
| : public viz::mojom::CompositorFrameSinkClient { |
| public: |
| SoftwareCompositorFrameSinkClient() = default; |
| ~SoftwareCompositorFrameSinkClient() override = default; |
| |
| void DidReceiveCompositorFrameAck( |
| const std::vector<viz::ReturnedResource>& resources) override { |
| DCHECK(resources.empty()); |
| } |
| void OnBeginFrame(const viz::BeginFrameArgs& args, |
| const viz::FrameTimingDetailsMap& timing_details) override { |
| } |
| void ReclaimResources( |
| const std::vector<viz::ReturnedResource>& resources) override { |
| DCHECK(resources.empty()); |
| } |
| void OnBeginFramePausedChanged(bool paused) override {} |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(SoftwareCompositorFrameSinkClient); |
| }; |
| |
| } // namespace |
| |
| class SynchronousLayerTreeFrameSink::SoftwareOutputSurface |
| : public viz::OutputSurface { |
| public: |
| SoftwareOutputSurface(std::unique_ptr<SoftwareDevice> software_device) |
| : viz::OutputSurface(std::move(software_device)) {} |
| |
| // viz::OutputSurface implementation. |
| void BindToClient(viz::OutputSurfaceClient* client) override {} |
| void EnsureBackbuffer() override {} |
| void DiscardBackbuffer() override {} |
| void BindFramebuffer() override {} |
| void SwapBuffers(viz::OutputSurfaceFrame frame) override {} |
| void Reshape(const gfx::Size& size, |
| float scale_factor, |
| const gfx::ColorSpace& color_space, |
| gfx::BufferFormat format, |
| bool use_stencil) override {} |
| uint32_t GetFramebufferCopyTextureFormat() override { return 0; } |
| bool IsDisplayedAsOverlayPlane() const override { return false; } |
| unsigned GetOverlayTextureId() const override { return 0; } |
| bool HasExternalStencilTest() const override { return false; } |
| void ApplyExternalStencil() override {} |
| unsigned UpdateGpuFence() override { return 0; } |
| void SetUpdateVSyncParametersCallback( |
| viz::UpdateVSyncParametersCallback callback) override {} |
| void SetDisplayTransformHint(gfx::OverlayTransform transform) override {} |
| gfx::OverlayTransform GetDisplayTransform() override { |
| return gfx::OVERLAY_TRANSFORM_NONE; |
| } |
| }; |
| |
| base::TimeDelta SynchronousLayerTreeFrameSink::StubDisplayClient:: |
| GetPreferredFrameIntervalForFrameSinkId( |
| const viz::FrameSinkId& id, |
| viz::mojom::blink::CompositorFrameSinkType* type) { |
| return viz::BeginFrameArgs::MinInterval(); |
| } |
| |
| SynchronousLayerTreeFrameSink::SynchronousLayerTreeFrameSink( |
| scoped_refptr<viz::ContextProvider> context_provider, |
| scoped_refptr<viz::RasterContextProvider> worker_context_provider, |
| scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner, |
| gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager, |
| uint32_t layer_tree_frame_sink_id, |
| std::unique_ptr<viz::BeginFrameSource> synthetic_begin_frame_source, |
| SynchronousCompositorRegistry* registry, |
| mojo::PendingRemote<viz::mojom::blink::CompositorFrameSink> |
| compositor_frame_sink_remote, |
| mojo::PendingReceiver<viz::mojom::blink::CompositorFrameSinkClient> |
| client_receiver) |
| : cc::LayerTreeFrameSink(std::move(context_provider), |
| std::move(worker_context_provider), |
| std::move(compositor_task_runner), |
| gpu_memory_buffer_manager), |
| layer_tree_frame_sink_id_(layer_tree_frame_sink_id), |
| registry_(registry), |
| memory_policy_(0u), |
| unbound_compositor_frame_sink_(std::move(compositor_frame_sink_remote)), |
| unbound_client_(std::move(client_receiver)), |
| synthetic_begin_frame_source_(std::move(synthetic_begin_frame_source)), |
| viz_frame_submission_enabled_( |
| features::IsUsingVizFrameSubmissionForWebView()), |
| use_zero_copy_sw_draw_( |
| Platform::Current() |
| ->IsZeroCopySynchronousSwDrawEnabledForAndroidWebView()), |
| animation_power_mode_voter_( |
| power_scheduler::PowerModeArbiter::GetInstance()->NewVoter( |
| "PowerModeVoter.Animation")) { |
| DCHECK(registry_); |
| DETACH_FROM_THREAD(thread_checker_); |
| memory_policy_.priority_cutoff_when_visible = |
| gpu::MemoryAllocation::CUTOFF_ALLOW_NICE_TO_HAVE; |
| } |
| |
| SynchronousLayerTreeFrameSink::~SynchronousLayerTreeFrameSink() = default; |
| |
| void SynchronousLayerTreeFrameSink::SetSyncClient( |
| SynchronousLayerTreeFrameSinkClient* compositor) { |
| sync_client_ = compositor; |
| } |
| |
| bool SynchronousLayerTreeFrameSink::BindToClient( |
| cc::LayerTreeFrameSinkClient* sink_client) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (!cc::LayerTreeFrameSink::BindToClient(sink_client)) |
| return false; |
| |
| if (viz_frame_submission_enabled_) { |
| compositor_frame_sink_.Bind(std::move(unbound_compositor_frame_sink_)); |
| client_receiver_.Bind(std::move(unbound_client_), compositor_task_runner_); |
| } |
| |
| // The SharedBitmapManager is null since software compositing is not supported |
| // or used on Android. |
| frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>( |
| /*shared_bitmap_manager=*/nullptr); |
| |
| if (synthetic_begin_frame_source_) { |
| client_->SetBeginFrameSource(synthetic_begin_frame_source_.get()); |
| } else { |
| external_begin_frame_source_ = |
| std::make_unique<viz::ExternalBeginFrameSource>(this); |
| external_begin_frame_source_->OnSetBeginFrameSourcePaused( |
| begin_frames_paused_); |
| client_->SetBeginFrameSource(external_begin_frame_source_.get()); |
| } |
| |
| client_->SetMemoryPolicy(memory_policy_); |
| client_->SetTreeActivationCallback(base::BindRepeating( |
| &SynchronousLayerTreeFrameSink::DidActivatePendingTree, |
| base::Unretained(this))); |
| registry_->RegisterLayerTreeFrameSink(this); |
| |
| software_frame_sink_client_ = |
| std::make_unique<SoftwareCompositorFrameSinkClient>(); |
| constexpr bool root_support_is_root = true; |
| constexpr bool child_support_is_root = false; |
| root_support_ = std::make_unique<viz::CompositorFrameSinkSupport>( |
| software_frame_sink_client_.get(), frame_sink_manager_.get(), |
| kRootFrameSinkId, root_support_is_root); |
| child_support_ = std::make_unique<viz::CompositorFrameSinkSupport>( |
| software_frame_sink_client_.get(), frame_sink_manager_.get(), |
| kChildFrameSinkId, child_support_is_root); |
| |
| viz::RendererSettings software_renderer_settings; |
| |
| auto output_surface = std::make_unique<SoftwareOutputSurface>( |
| std::make_unique<SoftwareDevice>(¤t_sw_canvas_)); |
| software_output_surface_ = output_surface.get(); |
| |
| auto overlay_processor = std::make_unique<viz::OverlayProcessorStub>(); |
| |
| // The gpu_memory_buffer_manager here is null as the Display is only used for |
| // resourcesless software draws, where no resources are included in the frame |
| // swapped from the compositor. So there is no need for it. |
| // The shared_bitmap_manager_ is provided for the Display to allocate |
| // resources. |
| // TODO(crbug.com/692814): The Display never sends its resources out of |
| // process so there is no reason for it to use a SharedBitmapManager. |
| // The gpu::GpuTaskSchedulerHelper here is null as the OutputSurface is |
| // software only and the overlay processor is a stub. |
| display_ = std::make_unique<viz::Display>( |
| &shared_bitmap_manager_, software_renderer_settings, &debug_settings_, |
| kRootFrameSinkId, nullptr /* gpu::GpuTaskSchedulerHelper */, |
| std::move(output_surface), std::move(overlay_processor), |
| nullptr /* scheduler */, nullptr /* current_task_runner */); |
| display_->Initialize(&display_client_, |
| frame_sink_manager_->surface_manager()); |
| display_->SetVisible(true); |
| return true; |
| } |
| |
| void SynchronousLayerTreeFrameSink::DetachFromClient() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| client_->SetBeginFrameSource(nullptr); |
| // Destroy the begin frame source on the same thread it was bound on. |
| synthetic_begin_frame_source_ = nullptr; |
| external_begin_frame_source_ = nullptr; |
| if (sync_client_) |
| sync_client_->SinkDestroyed(); |
| registry_->UnregisterLayerTreeFrameSink(this); |
| client_->SetTreeActivationCallback(base::RepeatingClosure()); |
| root_support_.reset(); |
| child_support_.reset(); |
| software_frame_sink_client_ = nullptr; |
| software_output_surface_ = nullptr; |
| display_ = nullptr; |
| frame_sink_manager_ = nullptr; |
| |
| client_receiver_.reset(); |
| compositor_frame_sink_.reset(); |
| |
| cc::LayerTreeFrameSink::DetachFromClient(); |
| } |
| |
| void SynchronousLayerTreeFrameSink::SetLocalSurfaceId( |
| const viz::LocalSurfaceId& local_surface_id) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| local_surface_id_ = local_surface_id; |
| } |
| |
| void SynchronousLayerTreeFrameSink::SubmitCompositorFrame( |
| viz::CompositorFrame frame, |
| bool hit_test_data_changed, |
| bool show_hit_test_borders) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(sync_client_); |
| |
| base::Optional<viz::CompositorFrame> submit_frame; |
| gfx::Size child_size = in_software_draw_ |
| ? sw_viewport_for_current_draw_.size() |
| : frame.size_in_pixels(); |
| if (!child_local_surface_id_.is_valid() || child_size_ != child_size || |
| device_scale_factor_ != frame.metadata.device_scale_factor) { |
| child_local_surface_id_allocator_.GenerateId(); |
| child_local_surface_id_ = |
| child_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); |
| child_size_ = child_size; |
| device_scale_factor_ = frame.metadata.device_scale_factor; |
| } |
| |
| if (in_software_draw_) { |
| // The frame we send to the client is actually just the metadata. Preserve |
| // the |frame| for the software path below. |
| submit_frame.emplace(); |
| submit_frame->metadata = frame.metadata.Clone(); |
| |
| // The layer compositor should be giving a frame that covers the |
| // |sw_viewport_for_current_draw_| but at 0,0. |
| DCHECK(gfx::Rect(child_size) == frame.render_pass_list.back()->output_rect); |
| |
| // Make a size that covers from 0,0 and includes the area coming from the |
| // layer compositor. |
| gfx::Size display_size(sw_viewport_for_current_draw_.right(), |
| sw_viewport_for_current_draw_.bottom()); |
| display_->Resize(display_size); |
| |
| if (!root_local_surface_id_.is_valid() || display_size_ != display_size || |
| device_scale_factor_ != frame.metadata.device_scale_factor) { |
| root_local_surface_id_allocator_.GenerateId(); |
| root_local_surface_id_ = |
| root_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); |
| display_size_ = display_size; |
| device_scale_factor_ = frame.metadata.device_scale_factor; |
| } |
| |
| display_->SetLocalSurfaceId(root_local_surface_id_, |
| frame.metadata.device_scale_factor); |
| |
| // The offset for the child frame relative to the origin of the canvas being |
| // drawn into. |
| gfx::Transform child_transform; |
| child_transform.Translate( |
| gfx::Vector2dF(sw_viewport_for_current_draw_.OffsetFromOrigin())); |
| |
| // Make a root frame that embeds the frame coming from the layer compositor |
| // and positions it based on the provided viewport. |
| // TODO(danakj): We could apply the transform here instead of passing it to |
| // the LayerTreeFrameSink client too? (We'd have to do the same for |
| // hardware frames in SurfacesInstance?) |
| viz::CompositorFrame embed_frame; |
| embed_frame.metadata.begin_frame_ack = frame.metadata.begin_frame_ack; |
| embed_frame.metadata.device_scale_factor = |
| frame.metadata.device_scale_factor; |
| embed_frame.render_pass_list.push_back(viz::CompositorRenderPass::Create()); |
| |
| // The embedding RenderPass covers the entire Display's area. |
| const auto& embed_render_pass = embed_frame.render_pass_list.back(); |
| embed_render_pass->SetNew(viz::CompositorRenderPassId{1}, |
| gfx::Rect(display_size), gfx::Rect(display_size), |
| gfx::Transform()); |
| embed_render_pass->has_transparent_background = false; |
| |
| // The RenderPass has a single SurfaceDrawQuad (and SharedQuadState for it). |
| bool are_contents_opaque = |
| !frame.render_pass_list.back()->has_transparent_background; |
| auto* shared_quad_state = |
| embed_render_pass->CreateAndAppendSharedQuadState(); |
| auto* surface_quad = |
| embed_render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>(); |
| shared_quad_state->SetAll( |
| child_transform, gfx::Rect(child_size), gfx::Rect(child_size), |
| gfx::MaskFilterInfo(), gfx::Rect() /* clip_rect */, |
| false /* is_clipped */, are_contents_opaque /* are_contents_opaque */, |
| 1.f /* opacity */, SkBlendMode::kSrcOver, 0 /* sorting_context_id */); |
| surface_quad->SetNew( |
| shared_quad_state, gfx::Rect(child_size), gfx::Rect(child_size), |
| viz::SurfaceRange( |
| base::nullopt, |
| viz::SurfaceId(kChildFrameSinkId, child_local_surface_id_)), |
| SK_ColorWHITE, false /* stretch_content_to_fill_bounds */); |
| |
| child_support_->SubmitCompositorFrame(child_local_surface_id_, |
| std::move(frame)); |
| root_support_->SubmitCompositorFrame(root_local_surface_id_, |
| std::move(embed_frame)); |
| display_->DrawAndSwap(base::TimeTicks::Now()); |
| |
| // We don't track metrics for frames submitted to |display_| but it still |
| // expects that every frame will receive a swap ack and presentation |
| // feedback so we send null signals here. |
| display_->DidReceiveSwapBuffersAck(gfx::SwapTimings()); |
| display_->DidReceivePresentationFeedback( |
| gfx::PresentationFeedback::Failure()); |
| } else { |
| if (viz_frame_submission_enabled_) { |
| frame.metadata.begin_frame_ack = |
| viz::BeginFrameAck::CreateManualAckWithDamage(); |
| |
| // For hardware draws with viz we send frame to compositor_frame_sink_ |
| compositor_frame_sink_->SubmitCompositorFrame( |
| local_surface_id_, std::move(frame), client_->BuildHitTestData(), 0); |
| } else { |
| // For hardware draws without viz we send the whole frame to the client so |
| // it can draw the content in it. |
| submit_frame = std::move(frame); |
| } |
| } |
| // NOTE: submit_frame will be empty if viz_frame_submission_enabled_ enabled, |
| // but it won't be used upstream |
| // Because OnDraw can synchronously override the viewport without going |
| // through commit and activation, we generate our own LocalSurfaceId by |
| // checking the submitted frame instead of using the one set here. |
| sync_client_->SubmitCompositorFrame( |
| layer_tree_frame_sink_id_, |
| viz_frame_submission_enabled_ ? local_surface_id_ |
| : child_local_surface_id_, |
| std::move(submit_frame), client_->BuildHitTestData()); |
| did_submit_frame_ = true; |
| } |
| |
| void SynchronousLayerTreeFrameSink::DidNotProduceFrame( |
| const viz::BeginFrameAck& ack) { |
| // We do not call CompositorFrameSink::DidNotProduceFrame here because |
| // submission of frame depends on DemandDraw calls. DidNotProduceFrame will be |
| // called there or during OnBeginFrame as fallback. |
| } |
| |
| void SynchronousLayerTreeFrameSink::DidAllocateSharedBitmap( |
| base::ReadOnlySharedMemoryRegion region, |
| const viz::SharedBitmapId& id) { |
| // Webview does not use software compositing (other than resourceless draws, |
| // but this is called for software /resources/). |
| NOTREACHED(); |
| } |
| |
| void SynchronousLayerTreeFrameSink::DidDeleteSharedBitmap( |
| const viz::SharedBitmapId& id) { |
| // Webview does not use software compositing (other than resourceless draws, |
| // but this is called for software /resources/). |
| NOTREACHED(); |
| } |
| |
| void SynchronousLayerTreeFrameSink::Invalidate(bool needs_draw) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (sync_client_) |
| sync_client_->Invalidate(needs_draw); |
| } |
| |
| void SynchronousLayerTreeFrameSink::DemandDrawHw( |
| const gfx::Size& viewport_size, |
| const gfx::Rect& viewport_rect_for_tile_priority, |
| const gfx::Transform& transform_for_tile_priority, |
| bool need_new_local_surface_id) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(HasClient()); |
| DCHECK(context_provider_.get()); |
| |
| if (need_new_local_surface_id) { |
| child_local_surface_id_ = viz::LocalSurfaceId(); |
| } |
| |
| client_->SetExternalTilePriorityConstraints(viewport_rect_for_tile_priority, |
| transform_for_tile_priority); |
| InvokeComposite(gfx::Transform(), gfx::Rect(viewport_size)); |
| } |
| |
| void SynchronousLayerTreeFrameSink::DemandDrawSwZeroCopy() { |
| DCHECK(use_zero_copy_sw_draw_); |
| DemandDrawSw( |
| Platform::Current()->SynchronousCompositorGetSkCanvasForAndroidWebView()); |
| } |
| |
| void SynchronousLayerTreeFrameSink::DemandDrawSw(SkCanvas* canvas) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(canvas); |
| DCHECK(!current_sw_canvas_); |
| |
| base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas); |
| |
| SkIRect canvas_clip = canvas->getDeviceClipBounds(); |
| gfx::Rect viewport = gfx::SkIRectToRect(canvas_clip); |
| |
| gfx::Transform transform(gfx::Transform::kSkipInitialization); |
| transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4. |
| |
| // We will resize the Display to ensure it covers the entire |viewport|, so |
| // save it for later. |
| sw_viewport_for_current_draw_ = viewport; |
| |
| base::AutoReset<bool> set_in_software_draw(&in_software_draw_, true); |
| InvokeComposite(transform, viewport); |
| } |
| |
| void SynchronousLayerTreeFrameSink::WillSkipDraw() { |
| client_->OnDraw(gfx::Transform(), gfx::Rect(), in_software_draw_, |
| true /*skip_draw*/); |
| } |
| |
| bool SynchronousLayerTreeFrameSink::UseZeroCopySoftwareDraw() { |
| return use_zero_copy_sw_draw_; |
| } |
| |
| void SynchronousLayerTreeFrameSink::InvokeComposite( |
| const gfx::Transform& transform, |
| const gfx::Rect& viewport) { |
| did_submit_frame_ = false; |
| // Adjust transform so that the layer compositor draws the |viewport| rect |
| // at its origin. The offset of the |viewport| we pass to the layer compositor |
| // must also be zero, since the rect needs to be in the coordinates of the |
| // layer compositor. |
| gfx::Transform adjusted_transform = transform; |
| adjusted_transform.matrix().postTranslate(-viewport.x(), -viewport.y(), 0); |
| // Don't propagate the viewport origin, as it will affect the clip rect. |
| client_->OnDraw(adjusted_transform, gfx::Rect(viewport.size()), |
| in_software_draw_, false /*skip_draw*/); |
| |
| if (did_submit_frame_) { |
| // This must happen after unwinding the stack and leaving the compositor. |
| // Usually it is a separate task but we just defer it until OnDraw |
| // completes instead. |
| client_->DidReceiveCompositorFrameAck(); |
| } |
| } |
| |
| void SynchronousLayerTreeFrameSink::ReclaimResources( |
| uint32_t layer_tree_frame_sink_id, |
| const Vector<viz::ReturnedResource>& resources) { |
| // Ignore message if it's a stale one coming from a different output surface |
| // (e.g. after a lost context). |
| if (layer_tree_frame_sink_id != layer_tree_frame_sink_id_) |
| return; |
| client_->ReclaimResources( |
| std::vector<viz::ReturnedResource>(resources.begin(), resources.end())); |
| } |
| |
| void SynchronousLayerTreeFrameSink::SetMemoryPolicy(size_t bytes_limit) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| bool became_zero = memory_policy_.bytes_limit_when_visible && !bytes_limit; |
| bool became_non_zero = |
| !memory_policy_.bytes_limit_when_visible && bytes_limit; |
| memory_policy_.bytes_limit_when_visible = bytes_limit; |
| memory_policy_.num_resources_limit = kNumResourcesLimit; |
| |
| if (client_) |
| client_->SetMemoryPolicy(memory_policy_); |
| |
| if (became_zero) { |
| // This is small hack to drop context resources without destroying it |
| // when this compositor is put into the background. |
| context_provider()->ContextSupport()->SetAggressivelyFreeResources( |
| true /* aggressively_free_resources */); |
| } else if (became_non_zero) { |
| context_provider()->ContextSupport()->SetAggressivelyFreeResources( |
| false /* aggressively_free_resources */); |
| } |
| } |
| |
| void SynchronousLayerTreeFrameSink::DidActivatePendingTree() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| if (sync_client_) |
| sync_client_->DidActivatePendingTree(); |
| } |
| |
| void SynchronousLayerTreeFrameSink::DidReceiveCompositorFrameAck( |
| const Vector<viz::ReturnedResource>& resources) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(viz_frame_submission_enabled_); |
| client_->ReclaimResources( |
| std::vector<viz::ReturnedResource>(resources.begin(), resources.end())); |
| // client_->DidReceiveCompositorFrameAck() is called just after frame |
| // submission so cc won't be throttled on actual draw which can happen late |
| // (or not happen at all) for WebView. |
| } |
| |
| void SynchronousLayerTreeFrameSink::OnBeginFrame( |
| const viz::BeginFrameArgs& args, |
| const HashMap<uint32_t, viz::FrameTimingDetails>& timing_details) { |
| DCHECK(viz_frame_submission_enabled_); |
| |
| // We do not receive BeginFrames via CompositorFrameSink, so we do not forward |
| // it to cc. We still might get one with FrameTimingDetailsMap, so we report |
| // it here. |
| |
| if (client_) { |
| for (const auto& pair : timing_details) { |
| client_->DidPresentCompositorFrame(pair.key, pair.value); |
| } |
| } |
| } |
| |
| void SynchronousLayerTreeFrameSink::ReclaimResources( |
| const Vector<viz::ReturnedResource>& resources) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| DCHECK(viz_frame_submission_enabled_); |
| client_->ReclaimResources( |
| std::vector<viz::ReturnedResource>(resources.begin(), resources.end())); |
| } |
| |
| void SynchronousLayerTreeFrameSink::OnBeginFramePausedChanged(bool paused) { |
| DCHECK(viz_frame_submission_enabled_); |
| } |
| |
| void SynchronousLayerTreeFrameSink::OnNeedsBeginFrames( |
| bool needs_begin_frames) { |
| if (needs_begin_frames_ != needs_begin_frames) { |
| if (needs_begin_frames) { |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cc,benchmark", "NeedsBeginFrames", |
| this); |
| animation_power_mode_voter_->VoteFor( |
| power_scheduler::PowerMode::kAnimation); |
| } else { |
| TRACE_EVENT_NESTABLE_ASYNC_END0("cc,benchmark", "NeedsBeginFrames", this); |
| animation_power_mode_voter_->ResetVoteAfterTimeout( |
| power_scheduler::PowerModeVoter::kAnimationTimeout); |
| } |
| } |
| needs_begin_frames_ = needs_begin_frames; |
| if (sync_client_) { |
| sync_client_->SetNeedsBeginFrames(needs_begin_frames); |
| } |
| } |
| |
| void SynchronousLayerTreeFrameSink::DidPresentCompositorFrame( |
| const viz::FrameTimingDetailsMap& timing_details) { |
| DCHECK(!viz_frame_submission_enabled_ || timing_details.empty()); |
| |
| if (!client_) |
| return; |
| for (const auto& pair : timing_details) |
| client_->DidPresentCompositorFrame(pair.first, pair.second); |
| } |
| |
| void SynchronousLayerTreeFrameSink::BeginFrame( |
| const viz::BeginFrameArgs& args) { |
| if (external_begin_frame_source_) |
| external_begin_frame_source_->OnBeginFrame(args); |
| } |
| |
| void SynchronousLayerTreeFrameSink::SetBeginFrameSourcePaused(bool paused) { |
| if (external_begin_frame_source_) |
| external_begin_frame_source_->OnSetBeginFrameSourcePaused(paused); |
| } |
| |
| } // namespace blink |