| // 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_delegated.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "components/viz/common/features.h" |
| #include "components/viz/common/viz_utils.h" |
| #include "components/viz/service/debugger/viz_debugger.h" |
| #include "components/viz/service/display/display_resource_provider.h" |
| #include "components/viz/service/display/output_surface.h" |
| #include "components/viz/service/display/overlay_candidate.h" |
| #include "components/viz/service/display/overlay_candidate_factory.h" |
| #include "components/viz/service/display/overlay_processor_delegated_support.h" |
| #include "components/viz/service/display/overlay_processor_interface.h" |
| #include "gpu/command_buffer/client/shared_image_interface.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/gfx/geometry/transform.h" |
| #include "ui/ozone/public/overlay_manager_ozone.h" |
| #include "ui/ozone/public/ozone_platform.h" |
| |
| namespace { |
| // Block delegation if there has been a copy request in the last 3 frames. |
| constexpr int kCopyRequestBlockFrames = 3; |
| |
| } // namespace |
| |
| namespace viz { |
| |
| OverlayProcessorDelegated::OverlayProcessorDelegated( |
| std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates, |
| std::vector<OverlayStrategy> available_strategies, |
| std::unique_ptr<PixmapProvider> pixmap_provider) |
| : OverlayProcessorOzone(std::move(overlay_candidates), |
| available_strategies, |
| std::move(pixmap_provider)) { |
| // TODO(msisov, petermcneeley): remove this once Wayland uses only delegated |
| // context. May be null in tests. |
| if (ui::OzonePlatform::GetInstance()->GetOverlayManager()) { |
| ui::OzonePlatform::GetInstance() |
| ->GetOverlayManager() |
| ->SetContextDelegated(); |
| } |
| } |
| |
| OverlayProcessorDelegated::~OverlayProcessorDelegated() = default; |
| |
| DBG_FLAG_FBOOL("delegated.enable.quad_split", quad_split) |
| |
| bool OverlayProcessorDelegated::DisableSplittingQuads() const { |
| // This determines if we will split quads on delegation or on delegee side. |
| return !quad_split(); |
| } |
| |
| constexpr size_t kTooManyQuads = 64; |
| |
| bool OverlayProcessorDelegated::AttemptWithStrategies( |
| const SkM44& output_color_matrix, |
| const OverlayProcessorInterface::FilterOperationsMap& render_pass_filters, |
| const OverlayProcessorInterface::FilterOperationsMap& |
| render_pass_backdrop_filters, |
| const DisplayResourceProvider* resource_provider, |
| AggregatedRenderPassList* render_pass_list, |
| SurfaceDamageRectList* surface_damage_rect_list, |
| std::optional<OverlayCandidate>& primary_plane, |
| OverlayCandidateList* candidates, |
| std::vector<gfx::Rect>* content_bounds) { |
| DCHECK(candidates->empty()); |
| auto* render_pass = render_pass_list->back().get(); |
| QuadList* quad_list = &render_pass->quad_list; |
| delegated_status_ = DelegationStatus::kCompositedOther; |
| |
| if (!features::IsDelegatedCompositingEnabled()) { |
| delegated_status_ = DelegationStatus::kCompositedFeatureDisabled; |
| return false; |
| } |
| |
| if (ForceDisableDelegation()) { |
| delegated_status_ = DelegationStatus::kCompositedFeatureDisabled; |
| return false; |
| } |
| |
| // Do not delegate when we have copy requests on the root render pass or we |
| // will end up with the delegated quads missing from the frame buffer. |
| // Delegating with copy requests also increases power usage. |
| if (BlockForCopyRequests(render_pass_list)) { |
| delegated_status_ = DelegationStatus::kCompositedCopyRequest; |
| return false; |
| } |
| |
| if (quad_list->size() >= kTooManyQuads) { |
| delegated_status_ = DelegationStatus::kCompositedTooManyQuads; |
| return false; |
| } |
| |
| if (!render_pass_backdrop_filters.empty()) { |
| delegated_status_ = DelegationStatus::kCompositedBackdropFilter; |
| return false; |
| } |
| |
| OverlayCandidateFactory::OverlayContext context; |
| context.is_delegated_context = true; |
| context.supports_clip_rect = false; |
| context.supports_out_of_window_clip_rect = false; |
| context.supports_arbitrary_transform = false; |
| context.supports_mask_filter = true; |
| context.transform_and_clip_rpdq = false; |
| context.supports_flip_rotate_transform = SupportsFlipRotateTransform(); |
| |
| OverlayCandidateFactory candidate_factory = OverlayCandidateFactory( |
| render_pass, resource_provider, surface_damage_rect_list, |
| &output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane), |
| &render_pass_filters, context); |
| |
| unassigned_damage_ = gfx::RectF(candidate_factory.GetUnassignedDamage()); |
| |
| const auto kExtraCandiates = needs_background_image_ ? 1 : 0; |
| candidates->reserve(quad_list->size() + kExtraCandiates); |
| int num_quads_skipped = 0; |
| |
| for (auto it = quad_list->begin(); it != quad_list->end(); ++it) { |
| if (auto result = TryPromoteDrawQuadForDelegation(candidate_factory, *it); |
| result.has_value()) { |
| if (const auto& candidate = result.value(); candidate.has_value()) { |
| candidates->push_back(candidate.value()); |
| } else { |
| // This quad can be intentionally skipped. |
| num_quads_skipped++; |
| } |
| } else { |
| delegated_status_ = result.error(); |
| } |
| } |
| |
| if (candidates->empty() || |
| (candidates->size() + num_quads_skipped) != quad_list->size()) { |
| candidates->clear(); |
| return false; |
| } |
| |
| int curr_plane_order = candidates->size(); |
| for (auto&& each : *candidates) { |
| each.plane_z_order = curr_plane_order--; |
| } |
| |
| // Check for support. |
| this->CheckOverlaySupport(std::nullopt, candidates); |
| |
| for (auto&& each : *candidates) { |
| if (!each.overlay_handled) { |
| candidates->clear(); |
| delegated_status_ = DelegationStatus::kCompositedCheckOverlayFail; |
| DBG_DRAW_RECT("delegated.handled.failed", each.display_rect); |
| DBG_LOG("delegated.handled.failed", "Handled failed %s", |
| each.display_rect.ToString().c_str()); |
| return false; |
| } |
| } |
| |
| // We cannot erase the quads that were handled as overlays because raw |
| // pointers of the aggregate draw quads were placed in the |rpdq| member of |
| // the |OverlayCandidate|. As keeping with the pattern in |
| // overlay_processor_mac we will also set the damage to empty on the |
| // successful promotion of all quads. |
| delegated_status_ = DelegationStatus::kFullDelegation; |
| return true; |
| } |
| |
| gfx::RectF OverlayProcessorDelegated::GetPrimaryPlaneDisplayRect( |
| const std::optional<OverlayCandidate>& primary_plane) { |
| return primary_plane ? primary_plane->display_rect : gfx::RectF(); |
| } |
| |
| void OverlayProcessorDelegated::ProcessForOverlays( |
| DisplayResourceProvider* resource_provider, |
| AggregatedRenderPassList* render_passes, |
| const SkM44& output_color_matrix, |
| const OverlayProcessorInterface::FilterOperationsMap& render_pass_filters, |
| const OverlayProcessorInterface::FilterOperationsMap& |
| render_pass_backdrop_filters, |
| SurfaceDamageRectList surface_damage_rect_list, |
| std::optional<OverlayCandidate>& primary_plane, |
| CandidateList* candidates, |
| gfx::Rect* damage_rect, |
| std::vector<gfx::Rect>* content_bounds) { |
| DCHECK(candidates->empty()); |
| bool success = false; |
| |
| DebugLogBeforeDelegation(*damage_rect, surface_damage_rect_list); |
| |
| success = AttemptWithStrategies( |
| output_color_matrix, render_pass_filters, render_pass_backdrop_filters, |
| resource_provider, render_passes, &surface_damage_rect_list, |
| primary_plane, candidates, content_bounds); |
| |
| DCHECK(candidates->empty() || success); |
| |
| if (success) { |
| overlay_damage_rect_ = *damage_rect; |
| // Save all the damage for the case when we fail delegation. |
| previous_frame_overlay_rect_.Union(*damage_rect); |
| // All quads handled. Primary plane damage is zero. |
| *damage_rect = gfx::Rect(); |
| } else { |
| overlay_damage_rect_ = previous_frame_overlay_rect_; |
| // Add in all the damage from all fully delegated frames. |
| damage_rect->Union(previous_frame_overlay_rect_); |
| previous_frame_overlay_rect_ = gfx::Rect(); |
| // This is only relevant when delegating. |
| unassigned_damage_ = gfx::RectF(); |
| |
| CHECK(primary_plane); |
| render_passes->back()->has_transparent_background |= |
| !primary_plane->is_opaque; |
| |
| // TODO(crbug.com/40775556) : Damage propagation will allow us to remove the |
| // primary plan entirely in the case of full delegation. |
| InsertPrimaryPlane(std::move(primary_plane).value(), *candidates); |
| primary_plane.reset(); |
| } |
| |
| DebugLogAfterDelegation(delegated_status_, *candidates, *damage_rect); |
| } |
| |
| gfx::RectF OverlayProcessorDelegated::GetUnassignedDamage() const { |
| return unassigned_damage_; |
| } |
| |
| bool OverlayProcessorDelegated::BlockForCopyRequests( |
| const AggregatedRenderPassList* render_pass_list) { |
| bool has_copy = false; |
| for (auto& pass : *render_pass_list) { |
| if (pass->HasCapture()) { |
| has_copy = true; |
| break; |
| } |
| } |
| |
| if (has_copy) { |
| copy_request_counter_ = kCopyRequestBlockFrames; |
| } else { |
| copy_request_counter_ = std::max(0, copy_request_counter_ - 1); |
| } |
| |
| return copy_request_counter_ > 0; |
| } |
| |
| } // namespace viz |