| // Copyright 2016 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 "components/exo/layer_tree_frame_sink_holder.h" |
| |
| #include "base/bind.h" |
| #include "base/containers/contains.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "cc/trees/layer_tree_frame_sink.h" |
| #include "components/exo/surface_tree_host.h" |
| #include "components/viz/common/frame_timing_details.h" |
| #include "components/viz/common/hit_test/hit_test_region_list.h" |
| #include "components/viz/common/resources/returned_resource.h" |
| #include "gpu/command_buffer/client/context_support.h" |
| #include "gpu/command_buffer/client/gles2_interface.h" |
| #include "ui/gfx/gpu_fence.h" |
| |
| namespace exo { |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // LayerTreeFrameSinkHolder, public: |
| |
| LayerTreeFrameSinkHolder::LayerTreeFrameSinkHolder( |
| SurfaceTreeHost* surface_tree_host, |
| std::unique_ptr<cc::LayerTreeFrameSink> frame_sink, |
| scoped_refptr<viz::ContextProvider> context_provider) |
| : surface_tree_host_(surface_tree_host), |
| frame_sink_(std::move(frame_sink)), |
| context_provider_(context_provider), |
| use_gpu_fence_( |
| context_provider_->ContextCapabilities().chromium_gpu_fence) { |
| frame_sink_->BindToClient(this); |
| } |
| |
| LayerTreeFrameSinkHolder::~LayerTreeFrameSinkHolder() { |
| if (frame_sink_) |
| frame_sink_->DetachFromClient(); |
| |
| if (lifetime_manager_) |
| lifetime_manager_->RemoveObserver(this); |
| } |
| |
| // static |
| void LayerTreeFrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed( |
| std::unique_ptr<LayerTreeFrameSinkHolder> holder) { |
| // Delete immediately if LayerTreeFrameSink was already lost. |
| if (holder->is_lost_) |
| return; |
| |
| if (holder->last_frame_size_in_pixels_.IsEmpty()) { |
| // Delete sink holder immediately if no frame has been submitted. |
| DCHECK(holder->last_frame_resources_.empty()); |
| return; |
| } |
| |
| // Submit an empty frame to ensure that pending release callbacks will be |
| // processed in a finite amount of time. |
| viz::CompositorFrame frame; |
| frame.metadata.begin_frame_ack.frame_id = |
| viz::BeginFrameId(viz::BeginFrameArgs::kManualSourceId, |
| viz::BeginFrameArgs::kStartingFrameNumber); |
| frame.metadata.begin_frame_ack.has_damage = true; |
| frame.metadata.frame_token = ++holder->next_frame_token_; |
| frame.metadata.device_scale_factor = holder->last_frame_device_scale_factor_; |
| auto pass = viz::CompositorRenderPass::Create(); |
| pass->SetNew(viz::CompositorRenderPassId{1}, |
| gfx::Rect(holder->last_frame_size_in_pixels_), |
| gfx::Rect(holder->last_frame_size_in_pixels_), gfx::Transform()); |
| frame.render_pass_list.push_back(std::move(pass)); |
| holder->last_frame_resources_.clear(); |
| holder->frame_sink_->SubmitCompositorFrame(std::move(frame), |
| /*hit_test_data_changed=*/true); |
| |
| // Delete sink holder immediately if not waiting for resources to be |
| // reclaimed. |
| if (holder->resource_manager_.HasNoCallbacks()) |
| return; |
| |
| WMHelper::LifetimeManager* lifetime_manager = |
| WMHelper::GetInstance()->GetLifetimeManager(); |
| holder->lifetime_manager_ = lifetime_manager; |
| holder->surface_tree_host_ = nullptr; |
| |
| // If we have pending release callbacks then extend the lifetime of holder |
| // by adding it as a LifetimeManager observer. The holder will delete itself |
| // when LifetimeManager shuts down or when all pending release callbacks have |
| // been called. |
| lifetime_manager->AddObserver(holder.release()); |
| } |
| |
| void LayerTreeFrameSinkHolder::SubmitCompositorFrame( |
| viz::CompositorFrame frame) { |
| DCHECK(!is_lost_); |
| |
| last_frame_size_in_pixels_ = frame.size_in_pixels(); |
| last_frame_device_scale_factor_ = frame.metadata.device_scale_factor; |
| last_frame_resources_.clear(); |
| for (auto& resource : frame.resource_list) |
| last_frame_resources_.push_back(resource.id); |
| frame_sink_->SubmitCompositorFrame(std::move(frame), |
| /*hit_test_data_changed=*/true); |
| } |
| |
| void LayerTreeFrameSinkHolder::DidNotProduceFrame( |
| const viz::BeginFrameAck& ack) { |
| DCHECK(!is_lost_); |
| frame_sink_->DidNotProduceFrame(ack, cc::FrameSkippedReason::kNoDamage); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // cc::LayerTreeFrameSinkClient overrides: |
| |
| absl::optional<viz::HitTestRegionList> |
| LayerTreeFrameSinkHolder::BuildHitTestData() { |
| return {}; |
| } |
| |
| void LayerTreeFrameSinkHolder::ReclaimResources( |
| std::vector<viz::ReturnedResource> resources) { |
| if (!use_gpu_fence_) { |
| ReclaimResourcesInternal(std::move(resources), nullptr); |
| return; |
| } |
| |
| // See the comment at ReclaimResourcesInternal's declaration. |
| gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL(); |
| auto gpu_fence_id = gles2->CreateGpuFenceCHROMIUM(); |
| context_provider_->ContextSupport()->GetGpuFence( |
| gpu_fence_id, |
| base::BindOnce(&LayerTreeFrameSinkHolder::ReclaimResourcesInternal, |
| weak_ptr_factory_.GetWeakPtr(), std::move(resources))); |
| gles2->DestroyGpuFenceCHROMIUM(gpu_fence_id); |
| } |
| |
| void LayerTreeFrameSinkHolder::DidReceiveCompositorFrameAck() { |
| if (surface_tree_host_) |
| surface_tree_host_->DidReceiveCompositorFrameAck(); |
| } |
| |
| void LayerTreeFrameSinkHolder::DidPresentCompositorFrame( |
| uint32_t frame_token, |
| const viz::FrameTimingDetails& details) { |
| if (surface_tree_host_) { |
| surface_tree_host_->DidPresentCompositorFrame( |
| frame_token, details.presentation_feedback); |
| } |
| } |
| |
| void LayerTreeFrameSinkHolder::DidLoseLayerTreeFrameSink() { |
| last_frame_resources_.clear(); |
| resource_manager_.ClearAllCallbacks(); |
| is_lost_ = true; |
| |
| if (lifetime_manager_) |
| ScheduleDelete(); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // LayerTreeFrameSinkHolder, private: |
| |
| void LayerTreeFrameSinkHolder::ScheduleDelete() { |
| if (delete_pending_) |
| return; |
| delete_pending_ = true; |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| } |
| |
| void LayerTreeFrameSinkHolder::OnDestroyed() { |
| lifetime_manager_->RemoveObserver(this); |
| lifetime_manager_ = nullptr; |
| |
| // Make sure frame sink never outlives the shell. |
| frame_sink_->DetachFromClient(); |
| frame_sink_.reset(); |
| ScheduleDelete(); |
| } |
| |
| void LayerTreeFrameSinkHolder::ReclaimResourcesInternal( |
| std::vector<viz::ReturnedResource> resources, |
| std::unique_ptr<gfx::GpuFence> release_fence) { |
| for (auto& resource : resources) { |
| // Skip resources that are also in last frame. This can happen if |
| // the frame sink id changed. |
| if (base::Contains(last_frame_resources_, resource.id)) { |
| continue; |
| } |
| |
| if (resource.release_fence.is_null() && release_fence) |
| resource.release_fence = release_fence->GetGpuFenceHandle().Clone(); |
| |
| resource_manager_.ReclaimResource(std::move(resource)); |
| } |
| |
| if (lifetime_manager_ && resource_manager_.HasNoCallbacks()) |
| ScheduleDelete(); |
| } |
| |
| } // namespace exo |