blob: 2906dfee5df8af4fe316f201d67c770dbcf6950f [file] [log] [blame]
// Copyright 2014 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_strategy_single_on_top.h"
#include <vector>
#include "base/check_op.h"
#include "components/viz/common/display/overlay_strategy.h"
#include "components/viz/common/quads/draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/service/display/overlay_candidate_factory.h"
#include "components/viz/service/display/overlay_proposed_candidate.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace viz {
namespace {
using OverlayProposedCandidateIndex =
std::vector<OverlayProposedCandidate>::size_type;
// Calculates and caches for the candidates in the list between
// `single_on_top_candidates_start` (inclusively) and
// `single_on_top_candidates_end` (exclusively) if they are occluded by
// `candidate_with_display_masks`.
//
// Returns true if `candidate_with_display_masks` does occlude any other
// candidate.
bool CalculateOcclusionByRoundedDisplayMaskCandidate(
OverlayProposedCandidate& candidate_with_display_masks,
std::vector<OverlayProposedCandidate>* candidates,
OverlayProposedCandidateIndex single_on_top_candidates_begin,
OverlayProposedCandidateIndex single_on_top_candidates_end) {
DCHECK(candidate_with_display_masks.candidate.has_rounded_display_masks);
auto mask_bounds = OverlayProposedCandidate::GetRoundedDisplayMasksBounds(
candidate_with_display_masks);
bool intersects_candidate = false;
for (OverlayProposedCandidateIndex i = single_on_top_candidates_begin;
i < single_on_top_candidates_end; i++) {
auto& overlap_candidate = candidates->at(i);
// The rects are rounded as they're snapped by the compositor to pixel
// unless it is AA'ed, in which case, it won't be overlaid.
gfx::Rect overlap_rect =
gfx::ToRoundedRect(OverlayCandidate::DisplayRectInTargetSpace(
overlap_candidate.candidate));
// Check that no candidate overlaps with any of painted masks. Quads
// that have rounded-display masks, are all transparent except for the drawn
// masks.
for (const gfx::Rect& mask_bound : mask_bounds) {
if (mask_bound.Intersects(overlap_rect)) {
intersects_candidate = true;
// Cache the occluding `candidate_with_display_masks` for the
// overlap_candidate.
overlap_candidate.occluding_mask_keys.insert(
OverlayProposedCandidate::ToProposeKey(
candidate_with_display_masks));
}
}
}
return intersects_candidate;
}
bool HasRoundedDisplayMasks(const DrawQuad* quad) {
if (const auto* texture_quad = quad->DynamicCast<TextureDrawQuad>()) {
return !texture_quad->rounded_display_masks_info.IsEmpty();
}
return false;
}
} // namespace
OverlayStrategySingleOnTop::OverlayStrategySingleOnTop(
OverlayProcessorUsingStrategy* capability_checker)
: capability_checker_(capability_checker) {
DCHECK(capability_checker);
}
OverlayStrategySingleOnTop::~OverlayStrategySingleOnTop() = default;
void OverlayStrategySingleOnTop::Propose(
const SkM44& output_color_matrix,
const OverlayProcessorInterface::FilterOperationsMap& render_pass_filters,
const OverlayProcessorInterface::FilterOperationsMap&
render_pass_backdrop_filters,
DisplayResourceProvider* resource_provider,
AggregatedRenderPassList* render_pass_list,
SurfaceDamageRectList* surface_damage_rect_list,
const PrimaryPlane* primary_plane,
std::vector<OverlayProposedCandidate>* candidates,
std::vector<gfx::Rect>* content_bounds) {
auto* render_pass = render_pass_list->back().get();
QuadList* quad_list = &render_pass->quad_list;
const OverlayCandidateFactory::OverlayContext context = {
.supports_rounded_display_masks = true};
// Build a list of candidates with the associated quad.
OverlayCandidateFactory candidate_factory = OverlayCandidateFactory(
render_pass, resource_provider, surface_damage_rect_list,
&output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
&render_pass_filters, context);
std::vector<OverlayProposedCandidate> candidates_with_masks;
OverlayProposedCandidateIndex single_on_top_candidates_begin =
candidates->size();
QuadList::Iterator first_non_mask_quad_it = quad_list->begin();
bool seen_non_mask_quad = false;
for (auto quad_it = quad_list->begin(); quad_it != quad_list->end();
++quad_it) {
bool is_first_non_mask_candidate =
!HasRoundedDisplayMasks(*quad_it) && !seen_non_mask_quad;
if (is_first_non_mask_candidate) {
seen_non_mask_quad = true;
first_non_mask_quad_it = quad_it;
}
OverlayCandidate candidate;
if (candidate_factory.FromDrawQuad(*quad_it, candidate) !=
OverlayCandidate::CandidateStatus::kSuccess) {
// Quads with display masks should always be valid overlay candidates.
DCHECK(!HasRoundedDisplayMasks(*quad_it));
continue;
}
if (candidate.has_mask_filter) {
continue;
}
if (candidate.has_rounded_display_masks) {
// Quads with rounded-display masks are only considered as SingleOnTop
// candidates if they are the top most quads.
if (seen_non_mask_quad) {
continue;
}
// Candidates with rounded-display masks should not overlap with any other
// quad with rounded-display masks.
DCHECK(!candidate_factory.IsOccluded(candidate, quad_list->begin(),
quad_it));
candidates_with_masks.emplace_back(quad_it, candidate, this);
} else if (!candidate_factory.IsOccluded(candidate, first_non_mask_quad_it,
quad_it)) {
// We exclude quads with rounded-display masks from the occlusion
// calculation as they will be promoted to overlays if they occlude any
// SingleOnTop candidate. In case these quads are not promoted, the
// candidates that are occluded by these mask candidates will be
// composited for UI correctness.
candidates->emplace_back(quad_it, candidate, this);
}
}
DCHECK_LE(candidates_with_masks.size(), 2u);
// To save power we can skip promoting candidates with rounded display masks
// if they do not occlude any other SingleOnTop candidate.
for (auto& mask_candidate : candidates_with_masks) {
// We mark all the SingleOnTop candidates that are occluded by any mask
// rounded-display masks to be later used in overlay processing.
if (CalculateOcclusionByRoundedDisplayMaskCandidate(
mask_candidate, candidates, single_on_top_candidates_begin,
candidates->size())) {
candidates->push_back(mask_candidate);
}
}
}
bool OverlayStrategySingleOnTop::Attempt(
const SkM44& output_color_matrix,
const OverlayProcessorInterface::FilterOperationsMap& render_pass_filters,
const OverlayProcessorInterface::FilterOperationsMap&
render_pass_backdrop_filters,
DisplayResourceProvider* resource_provider,
AggregatedRenderPassList* render_pass_list,
SurfaceDamageRectList* surface_damage_rect_list,
const PrimaryPlane* primary_plane,
OverlayCandidateList* candidate_list,
std::vector<gfx::Rect>* content_bounds,
const OverlayProposedCandidate& proposed_candidate) {
// Before we attempt an overlay strategy, we shouldn't have a candidate.
DCHECK(candidate_list->empty());
auto* render_pass = render_pass_list->back().get();
return TryOverlay(render_pass, primary_plane, candidate_list,
proposed_candidate);
}
bool OverlayStrategySingleOnTop::TryOverlay(
AggregatedRenderPass* render_pass,
const PrimaryPlane* primary_plane,
OverlayCandidateList* candidate_list,
const OverlayProposedCandidate& proposed_candidate) {
// SingleOnTop strategy means we should have one candidate.
DCHECK(candidate_list->empty());
// Add the overlay.
OverlayCandidateList new_candidate_list = *candidate_list;
new_candidate_list.push_back(proposed_candidate.candidate);
new_candidate_list.back().plane_z_order = 1;
// Check for support.
capability_checker_->CheckOverlaySupport(primary_plane, &new_candidate_list);
const OverlayCandidate& overlay_candidate = new_candidate_list.back();
// If the candidate can be handled by an overlay, create a pass for it.
if (overlay_candidate.overlay_handled) {
CommitCandidate(proposed_candidate, render_pass);
candidate_list->swap(new_candidate_list);
return true;
}
return false;
}
void OverlayStrategySingleOnTop::CommitCandidate(
const OverlayProposedCandidate& proposed_candidate,
AggregatedRenderPass* render_pass) {
render_pass->quad_list.EraseAndInvalidateAllPointers(
proposed_candidate.quad_iter);
}
OverlayStrategy OverlayStrategySingleOnTop::GetUMAEnum() const {
return OverlayStrategy::kSingleOnTop;
}
} // namespace viz