| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/viz/service/display/overlay_processor_android.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/synchronization/waitable_event.h" |
| #include "components/viz/common/quads/texture_draw_quad.h" |
| #include "components/viz/service/display/display_compositor_memory_and_task_controller.h" |
| #include "components/viz/service/display/overlay_processor_on_gpu.h" |
| #include "components/viz/service/display/overlay_strategy_underlay.h" |
| #include "components/viz/service/display/skia_output_surface.h" |
| #include "gpu/command_buffer/service/scheduler_sequence.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| |
| namespace viz { |
| OverlayProcessorAndroid::OverlayProcessorAndroid( |
| DisplayCompositorMemoryAndTaskController* display_controller) |
| : OverlayProcessorUsingStrategy(), |
| gpu_task_scheduler_(display_controller->gpu_task_scheduler()) { |
| // Promoting video to overlay with SurfaceView overlays requires recreation of |
| // main SurfaceView and Display. This leads to the situation when we |
| // consider video overlay not being efficient for the first frame after we |
| // updated SurfaceView and video gets demoted back to composition. To avoid |
| // this, we disable heuristics that filter out not efficient quads but still |
| // sort them by potential power savings. |
| prioritization_config_.changing_threshold = false; |
| prioritization_config_.damage_rate_threshold = false; |
| |
| // In unittests, we don't have the gpu_task_scheduler_ set up, but still want |
| // to test ProcessForOverlays functionalities where we are making overlay |
| // candidates correctly. |
| if (gpu_task_scheduler_) { |
| gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; |
| // TODO(weiliangc): Eventually move the on gpu initialization to another |
| // static function. |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| auto callback = base::BindOnce( |
| &OverlayProcessorAndroid::InitializeOverlayProcessorOnGpu, |
| base::Unretained(this), display_controller->controller_on_gpu(), |
| &event); |
| gpu_task_scheduler_->ScheduleGpuTask( |
| std::move(callback), /*sync_token_fences=*/{}, gpu::SyncToken()); |
| event.Wait(); |
| } |
| |
| // For Android, we do not have the ability to skip an overlay, since the |
| // texture is already in a SurfaceView. Ideally, we would honor a 'force |
| // overlay' flag that FromDrawQuad would also check. |
| // For now, though, just skip the opacity check. We really have no idea if |
| // the underlying overlay is opaque anyway; the candidate is referring to |
| // a dummy resource that has no relation to what the overlay contains. |
| // https://crbug.com/842931 . |
| strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>( |
| this, OverlayStrategyUnderlay::OpaqueMode::AllowTransparentCandidates)); |
| |
| overlay_candidates_.clear(); |
| } |
| |
| OverlayProcessorAndroid::~OverlayProcessorAndroid() { |
| if (processor_on_gpu_) { |
| gpu::ScopedAllowScheduleGpuTask allow_schedule_gpu_task; |
| // If we have a |gpu_task_scheduler_|, we must have started initializing |
| // a |processor_on_gpu_| on the |gpu_task_scheduler_|. |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| auto callback = |
| base::BindOnce(&OverlayProcessorAndroid::DestroyOverlayProcessorOnGpu, |
| base::Unretained(this), &event); |
| gpu_task_scheduler_->ScheduleGpuTask( |
| std::move(callback), /*sync_token_fences=*/{}, gpu::SyncToken()); |
| event.Wait(); |
| } |
| } |
| |
| void OverlayProcessorAndroid::InitializeOverlayProcessorOnGpu( |
| gpu::DisplayCompositorMemoryAndTaskControllerOnGpu* |
| display_controller_on_gpu, |
| base::WaitableEvent* event) { |
| processor_on_gpu_ = |
| std::make_unique<OverlayProcessorOnGpu>(display_controller_on_gpu); |
| DCHECK(event); |
| event->Signal(); |
| } |
| |
| void OverlayProcessorAndroid::DestroyOverlayProcessorOnGpu( |
| base::WaitableEvent* event) { |
| processor_on_gpu_ = nullptr; |
| DCHECK(event); |
| event->Signal(); |
| } |
| |
| bool OverlayProcessorAndroid::IsOverlaySupported() const { |
| return true; |
| } |
| |
| bool OverlayProcessorAndroid::NeedsSurfaceDamageRectList() const { |
| return false; |
| } |
| |
| void OverlayProcessorAndroid::ScheduleOverlays( |
| DisplayResourceProvider* resource_provider) { |
| if (!processor_on_gpu_) |
| return; |
| |
| // Even if we don't have anything to overlay, still generate overlay locks for |
| // empty frame. |
| pending_overlay_locks_.emplace_back(); |
| |
| // Early out if we don't have any overlay candidates. |
| if (overlay_candidates_.empty()) |
| return; |
| |
| auto& locks = pending_overlay_locks_.back(); |
| std::vector<gpu::SyncToken> locks_sync_tokens; |
| for (auto& candidate : overlay_candidates_) { |
| locks.emplace_back(resource_provider, candidate.resource_id); |
| locks_sync_tokens.push_back(locks.back().sync_token()); |
| } |
| |
| auto task = base::BindOnce(&OverlayProcessorOnGpu::ScheduleOverlays, |
| base::Unretained(processor_on_gpu_.get()), |
| std::move(overlay_candidates_)); |
| gpu_task_scheduler_->ScheduleGpuTask(std::move(task), locks_sync_tokens, |
| gpu::SyncToken()); |
| overlay_candidates_.clear(); |
| } |
| |
| void OverlayProcessorAndroid::OverlayPresentationComplete() { |
| // If there is not |processor_on_gpu_| to send information to, we won't have |
| // overlay resources locked, and don't need to clear the locks. |
| if (!processor_on_gpu_) |
| return; |
| |
| // This is a signal from Display::DidReceiveSwapBuffersAck. We use this to |
| // help clear locks on resources from the old frame. |
| committed_overlay_locks_.clear(); |
| std::swap(committed_overlay_locks_, pending_overlay_locks_.front()); |
| pending_overlay_locks_.pop_front(); |
| } |
| |
| void OverlayProcessorAndroid::CheckOverlaySupportImpl( |
| const std::optional<OverlayCandidate>& primary_plane, |
| OverlayCandidateList* candidates) { |
| // For pre-SurfaceControl Android we should not have output surface as overlay |
| // plane. |
| DCHECK(!primary_plane); |
| |
| // There should only be at most a single overlay candidate: the |
| // video quad. |
| // There's no check that the presented candidate is really a video frame for |
| // a fullscreen video. Instead it's assumed that if a quad is marked as |
| // overlayable, it's a fullscreen video quad. |
| DCHECK_LE(candidates->size(), 1u); |
| |
| if (!candidates->empty()) { |
| OverlayCandidate& candidate = candidates->front(); |
| |
| // This quad either will be promoted, or would be if it were backed by a |
| // SurfaceView. Record that it should get a promotion hint. |
| promotion_hint_info_map_[candidate.resource_id] = candidate.display_rect; |
| |
| if (!candidate.is_video_in_surface_view) { |
| // This quad would be promoted if it were backed by a SurfaceView. Since |
| // it isn't, we can't promote it. |
| return; |
| } |
| |
| candidate.display_rect = |
| gfx::RectF(gfx::ToEnclosingRect(candidate.display_rect)); |
| candidate.overlay_handled = true; |
| candidate.plane_z_order = -1; |
| |
| // This quad will be promoted. We clear the promotable hints here, since |
| // we can only promote a single quad. Otherwise, somebody might try to |
| // back one of the promotable quads with a SurfaceView, and either it or |
| // |candidate| would have to fall back to a texture. |
| promotion_hint_info_map_.clear(); |
| promotion_hint_info_map_[candidate.resource_id] = candidate.display_rect; |
| } |
| } |
| |
| gfx::Rect OverlayProcessorAndroid::GetOverlayDamageRectForOutputSurface( |
| const OverlayCandidate& overlay) const { |
| return ToEnclosedRect(overlay.display_rect); |
| } |
| |
| void OverlayProcessorAndroid::TakeOverlayCandidates( |
| OverlayCandidateList* candidate_list) { |
| overlay_candidates_.swap(*candidate_list); |
| candidate_list->clear(); |
| } |
| |
| void OverlayProcessorAndroid::NotifyOverlayPromotion( |
| DisplayResourceProvider* resource_provider, |
| const CandidateList& candidates, |
| const QuadList& quad_list) { |
| // If we don't have a processor_on_gpu_, there is nothing to send the overlay |
| // promotions to. |
| if (!processor_on_gpu_) { |
| promotion_hint_info_map_.clear(); |
| return; |
| } |
| |
| // Set of resources that have requested a promotion hint that also have quads |
| // that use them. |
| ResourceIdSet promotion_hint_requestor_set; |
| |
| for (auto* quad : quad_list) { |
| if (quad->material != DrawQuad::Material::kTextureContent) |
| continue; |
| const TextureDrawQuad* texture_quad = TextureDrawQuad::MaterialCast(quad); |
| ResourceId id = texture_quad->resource_id; |
| if (!resource_provider->DoesResourceWantPromotionHint(id)) |
| continue; |
| promotion_hint_requestor_set.insert(id); |
| } |
| |
| if (promotion_hint_requestor_set.empty()) { |
| promotion_hint_info_map_.clear(); |
| return; |
| } |
| |
| base::flat_set<gpu::Mailbox> promotion_denied; |
| base::flat_map<gpu::Mailbox, gfx::Rect> possible_promotions; |
| |
| DCHECK(candidates.empty() || candidates.size() == 1u); |
| |
| std::vector< |
| std::unique_ptr<DisplayResourceProvider::ScopedReadLockSharedImage>> |
| locks; |
| for (auto& request : promotion_hint_requestor_set) { |
| // If we successfully promote one candidate, then that promotion hint |
| // should be sent later when we schedule the overlay. |
| if (!candidates.empty() && candidates.front().resource_id == request) |
| continue; |
| |
| locks.emplace_back( |
| std::make_unique<DisplayResourceProvider::ScopedReadLockSharedImage>( |
| resource_provider, request)); |
| auto iter = promotion_hint_info_map_.find(request); |
| if (iter != promotion_hint_info_map_.end()) { |
| // This is a possible promotion. |
| possible_promotions.emplace(locks.back()->mailbox(), |
| gfx::ToEnclosedRect(iter->second)); |
| } else { |
| promotion_denied.insert(locks.back()->mailbox()); |
| } |
| } |
| |
| std::vector<gpu::SyncToken> locks_sync_tokens; |
| for (auto& read_lock : locks) |
| locks_sync_tokens.push_back(read_lock->sync_token()); |
| |
| if (gpu_task_scheduler_) { |
| auto task = base::BindOnce(&OverlayProcessorOnGpu::NotifyOverlayPromotions, |
| base::Unretained(processor_on_gpu_.get()), |
| std::move(promotion_denied), |
| std::move(possible_promotions)); |
| gpu_task_scheduler_->ScheduleGpuTask(std::move(task), locks_sync_tokens, |
| gpu::SyncToken()); |
| } |
| promotion_hint_info_map_.clear(); |
| } |
| |
| } // namespace viz |