blob: ccef0c30ed2712eec501f36a4b2fa5f84b21cb59 [file] [log] [blame]
// 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 <set>
#include <utility>
#include <vector>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/viz/common/features.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.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_interface.h"
#include "components/viz/service/display/overlay_strategy_fullscreen.h"
#include "components/viz/service/display/overlay_strategy_single_on_top.h"
#include "components/viz/service/display/overlay_strategy_underlay.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/geometry/vector2d_f.h"
#include "ui/ozone/public/overlay_manager_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
namespace {
DBG_FLAG_FBOOL("delegated.fd.usage", usage_every_frame)
void RecordFDUsageUMA() {
static uint64_t sReportUsageFrameCounter = 0;
sReportUsageFrameCounter++;
constexpr uint32_t kReportEveryNFrames = 60 * 60 * 5;
if (((sReportUsageFrameCounter % kReportEveryNFrames) != 0) &&
!usage_every_frame()) {
return;
}
base::TimeDelta delta_time_taken;
int fd_max;
int active_fd_count;
int rlim_cur;
if (!viz::GatherFDStats(&delta_time_taken, &fd_max, &active_fd_count,
&rlim_cur))
return;
static constexpr base::TimeDelta kHistogramMinTime = base::Microseconds(5);
static constexpr base::TimeDelta kHistogramMaxTime = base::Milliseconds(10);
static constexpr int kHistogramTimeBuckets = 50;
int percentage_usage_int = (active_fd_count * 100) / fd_max;
UMA_HISTOGRAM_PERCENTAGE("Viz.FileDescriptorTracking.PercentageUsed",
percentage_usage_int);
UMA_HISTOGRAM_COUNTS_100000("Viz.FileDescriptorTracking.NumActive",
active_fd_count);
UMA_HISTOGRAM_COUNTS_100000("Viz.FileDescriptorTracking.NumSoftMax",
rlim_cur);
UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
"Viz.FileDescriptorTracking.TimeToCompute", delta_time_taken,
kHistogramMinTime, kHistogramMaxTime, kHistogramTimeBuckets);
DBG_LOG("delegated.fd.usage", "FD usage: %d / %d - time us: %f",
active_fd_count, fd_max, delta_time_taken.InMicrosecondsF());
}
// 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,
gpu::SharedImageInterface* shared_image_interface)
: OverlayProcessorOzone(std::move(overlay_candidates),
available_strategies,
shared_image_interface) {
// 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();
supports_clip_rect_ = ui::OzonePlatform::GetInstance()
->GetPlatformRuntimeProperties()
.supports_clip_rect;
needs_background_image_ = ui::OzonePlatform::GetInstance()
->GetPlatformRuntimeProperties()
.needs_background_image;
}
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;
DBG_FLAG_FBOOL("delegated.disable.delegation", disable_delegation)
bool OverlayProcessorDelegated::AttemptWithStrategies(
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,
OverlayProcessorInterface::OutputSurfaceOverlayPlane* 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;
constexpr bool is_delegated_context = true;
delegated_status_ = DelegationStatus::kCompositedOther;
if (!features::IsDelegatedCompositingEnabled()) {
delegated_status_ = DelegationStatus::kCompositedFeatureDisabled;
return false;
}
if (disable_delegation())
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 candidate_factory = OverlayCandidateFactory(
render_pass, resource_provider, surface_damage_rect_list,
&output_color_matrix, GetPrimaryPlaneDisplayRect(primary_plane),
&render_pass_filters, is_delegated_context, supports_clip_rect_);
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) {
OverlayCandidate candidate;
auto& transform = it->shared_quad_state->quad_to_target_transform;
auto display_rect = transform.MapRect(gfx::RectF(it->rect));
DBG_DRAW_TEXT(
"delegated.overlay.type",
gfx::Vector2dF(display_rect.origin().x(), display_rect.origin().y()),
base::StringPrintf("m=%d rid=%d", static_cast<int>(it->material),
it->resources.begin()->value()));
auto candidate_status = candidate_factory.FromDrawQuad(*it, candidate);
if (candidate_status == OverlayCandidate::CandidateStatus::kSuccess) {
if (it->material == DrawQuad::Material::kSolidColor) {
DBG_DRAW_RECT("delegated.overlay.color", candidate.display_rect);
} else if (it->material == DrawQuad::Material::kAggregatedRenderPass) {
DBG_DRAW_RECT("delegated.overlay.aggregated", candidate.display_rect);
} else {
DBG_DRAW_RECT("delegated.overlay.candidate", candidate.display_rect);
}
candidates->push_back(candidate);
} else if (candidate_status ==
OverlayCandidate::CandidateStatus::kFailVisible) {
// This quad can be intentionally skipped.
num_quads_skipped++;
} else {
DBG_DRAW_RECT("delegated.overlay.failed", display_rect);
DBG_LOG("delegated.overlay.failed", "error code %d", candidate_status);
switch (candidate_status) {
case OverlayCandidate::CandidateStatus::kFailNotAxisAligned:
delegated_status_ = DelegationStatus::kCompositedNotAxisAligned;
break;
case OverlayCandidate::CandidateStatus::kFailNotAxisAligned3dTransform:
delegated_status_ = DelegationStatus::kCompositedHas3dTransform;
break;
case OverlayCandidate::CandidateStatus::kFailNotAxisAligned2dShear:
delegated_status_ = DelegationStatus::kCompositedHas2dShear;
break;
case OverlayCandidate::CandidateStatus::kFailNotAxisAligned2dRotation:
delegated_status_ = DelegationStatus::kCompositedHas2dRotation;
break;
case OverlayCandidate::CandidateStatus::kFailNotOverlay:
delegated_status_ = DelegationStatus::kCompositedNotOverlay;
break;
default:
break;
}
}
}
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(nullptr, 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 OverlayProcessorInterface::OutputSurfaceOverlayPlane* 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,
OutputSurfaceOverlayPlane* output_surface_plane,
CandidateList* candidates,
gfx::Rect* damage_rect,
std::vector<gfx::Rect>* content_bounds) {
DCHECK(candidates->empty());
bool success = false;
#if !BUILDFLAG(IS_APPLE)
RecordFDUsageUMA();
#endif
DBG_DRAW_RECT("delegated.incoming.damage", (*damage_rect));
for (auto&& each : surface_damage_rect_list) {
DBG_DRAW_RECT("delegated.surface.damage", each);
}
success = AttemptWithStrategies(
output_color_matrix, render_pass_filters, render_pass_backdrop_filters,
resource_provider, render_passes, &surface_damage_rect_list,
output_surface_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();
}
UMA_HISTOGRAM_ENUMERATION("Viz.DelegatedCompositing.Status",
delegated_status_);
DBG_LOG("delegation_status", "delegation status: %d", delegated_status_);
DBG_DRAW_RECT("delegated.outgoing.damage", (*damage_rect));
TRACE_COUNTER1(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes"),
"Scheduled overlay planes", candidates->size());
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("viz.debug.overlay_planes"),
"DelegatedCompositingStatus", TRACE_EVENT_SCOPE_THREAD,
"delegated_status", delegated_status_);
}
void OverlayProcessorDelegated::AdjustOutputSurfaceOverlay(
absl::optional<OutputSurfaceOverlayPlane>* output_surface_plane) {
if (!output_surface_plane->has_value())
return;
// TODO(https://crbug.com/1224991) : Damage propagation will allow us to
// remove the primary plan entirely in the case of full delegation.
// In that case we will do "output_surface_plane->reset()" like the existing
// fullscreen overlay code.
if (delegated_status_ == DelegationStatus::kFullDelegation)
output_surface_plane->reset();
}
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->copy_requests.empty()) {
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