blob: 2bda8ac5cab034eef0fb9d714249ea8439d92e7b [file] [log] [blame]
// Copyright 2019 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/viz/service/display/overlay_processor_ozone.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/viz/common/features.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 "components/viz/service/display/overlay_strategy_underlay_cast.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/service/shared_image_manager.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace viz {
namespace {
// TODO(weiliangc): When difference between primary plane and non-primary plane
// can be internalized, merge these two helper functions.
void ConvertToOzoneOverlaySurface(
const OverlayProcessorInterface::OutputSurfaceOverlayPlane& primary_plane,
ui::OverlaySurfaceCandidate* ozone_candidate) {
ozone_candidate->transform = primary_plane.transform;
ozone_candidate->format = primary_plane.format;
ozone_candidate->display_rect = primary_plane.display_rect;
ozone_candidate->crop_rect = primary_plane.uv_rect;
ozone_candidate->clip_rect.reset();
ozone_candidate->is_opaque = !primary_plane.enable_blending;
ozone_candidate->opacity = primary_plane.opacity;
ozone_candidate->plane_z_order = 0;
ozone_candidate->buffer_size = primary_plane.resource_size;
ozone_candidate->priority_hint = primary_plane.priority_hint;
ozone_candidate->rounded_corners = primary_plane.rounded_corners;
}
void ConvertToOzoneOverlaySurface(
const OverlayCandidate& overlay_candidate,
ui::OverlaySurfaceCandidate* ozone_candidate) {
ozone_candidate->transform = overlay_candidate.transform;
ozone_candidate->format = overlay_candidate.format;
ozone_candidate->display_rect = overlay_candidate.display_rect;
ozone_candidate->crop_rect = overlay_candidate.uv_rect;
ozone_candidate->clip_rect = overlay_candidate.clip_rect;
ozone_candidate->is_opaque = overlay_candidate.is_opaque;
ozone_candidate->opacity = overlay_candidate.opacity;
ozone_candidate->plane_z_order = overlay_candidate.plane_z_order;
ozone_candidate->buffer_size = overlay_candidate.resource_size_in_pixels;
ozone_candidate->requires_overlay = overlay_candidate.requires_overlay;
ozone_candidate->priority_hint = overlay_candidate.priority_hint;
ozone_candidate->rounded_corners = overlay_candidate.rounded_corners;
}
uint32_t MailboxToUInt32(const gpu::Mailbox& mailbox) {
return (mailbox.name[0] << 24) + (mailbox.name[1] << 16) +
(mailbox.name[2] << 8) + mailbox.name[3];
}
void ReportSharedImageExists(bool exists) {
UMA_HISTOGRAM_BOOLEAN(
"Compositing.Display.OverlayProcessorOzone."
"SharedImageExists",
exists);
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
bool AllowColorSpaceCombination(
const gfx::ColorSpace& source_color_space,
const gfx::ColorSpace& destination_color_space) {
// Allow invalid source color spaces because the assumption is that the
// compositor won't do a color space conversion in this case anyway, so it
// should be consistent with the overlay path.
if (!source_color_space.IsValid())
return true;
// Allow color space mismatches as long as either a) the source color space is
// SRGB; or b) both the source and destination color spaces have the same
// color usage. It is possible that case (a) still allows for visible color
// inconsistency between overlays and composition, but we'll address that case
// if it comes up.
return source_color_space.GetContentColorUsage() ==
gfx::ContentColorUsage::kSRGB ||
source_color_space.GetContentColorUsage() ==
destination_color_space.GetContentColorUsage();
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
} // namespace
// |overlay_candidates| is an object used to answer questions about possible
// overlays configurations.
// |available_strategies| is a list of overlay strategies that should be
// initialized by InitializeStrategies.
OverlayProcessorOzone::OverlayProcessorOzone(
std::unique_ptr<ui::OverlayCandidatesOzone> overlay_candidates,
std::vector<OverlayStrategy> available_strategies,
gpu::SharedImageInterface* shared_image_interface)
: OverlayProcessorUsingStrategy(),
overlay_candidates_(std::move(overlay_candidates)),
available_strategies_(std::move(available_strategies)),
shared_image_interface_(shared_image_interface) {
for (OverlayStrategy strategy : available_strategies_) {
switch (strategy) {
case OverlayStrategy::kFullscreen:
strategies_.push_back(
std::make_unique<OverlayStrategyFullscreen>(this));
break;
case OverlayStrategy::kSingleOnTop:
strategies_.push_back(
std::make_unique<OverlayStrategySingleOnTop>(this));
break;
case OverlayStrategy::kUnderlay:
strategies_.push_back(std::make_unique<OverlayStrategyUnderlay>(this));
break;
case OverlayStrategy::kUnderlayCast:
strategies_.push_back(
std::make_unique<OverlayStrategyUnderlayCast>(this));
break;
default:
NOTREACHED();
}
}
}
OverlayProcessorOzone::~OverlayProcessorOzone() = default;
bool OverlayProcessorOzone::IsOverlaySupported() const {
return true;
}
bool OverlayProcessorOzone::NeedsSurfaceDamageRectList() const {
return true;
}
void OverlayProcessorOzone::CheckOverlaySupportImpl(
const OverlayProcessorInterface::OutputSurfaceOverlayPlane* primary_plane,
OverlayCandidateList* surfaces) {
auto full_size = surfaces->size();
if (primary_plane)
full_size += 1;
ui::OverlayCandidatesOzone::OverlaySurfaceCandidateList ozone_surface_list(
full_size);
// Convert OverlayCandidateList to OzoneSurfaceCandidateList.
{
auto ozone_surface_iterator = ozone_surface_list.begin();
// For ozone-cast, there will not be a primary_plane.
if (primary_plane) {
ConvertToOzoneOverlaySurface(*primary_plane, &(*ozone_surface_iterator));
// TODO(crbug.com/1138568): Fuchsia claims support for presenting primary
// plane as overlay, but does not provide a mailbox. Handle this case.
#if !defined(OS_FUCHSIA)
if (shared_image_interface_) {
bool result = SetNativePixmapForCandidate(&(*ozone_surface_iterator),
primary_plane->mailbox,
/*is_primary=*/true);
// We cannot validate an overlay configuration without the buffer for
// primary plane present.
if (!result) {
for (auto& candidate : *surfaces) {
candidate.overlay_handled = false;
}
return;
}
}
#endif
ozone_surface_iterator++;
}
auto surface_iterator = surfaces->cbegin();
for (; ozone_surface_iterator < ozone_surface_list.end() &&
surface_iterator < surfaces->cend();
ozone_surface_iterator++, surface_iterator++) {
ConvertToOzoneOverlaySurface(*surface_iterator,
&(*ozone_surface_iterator));
#if BUILDFLAG(IS_CHROMEOS_ASH)
// On Chrome OS, skip the candidate if we think a color space combination
// might cause visible color differences between compositing and overlays.
// The reason is that on Chrome OS, we don't yet have an API to set up
// color space conversion per plane. Note however that we should only do
// this if the candidate does not require an overlay (e.g., for protected
// content, it's better to display it with an incorrect color space than
// to not display it at all).
// TODO(b/181974042): plumb the color space all the way to the ozone DRM
// backend when we get an API for per-plane color management.
DCHECK(primary_plane);
if (!surface_iterator->requires_overlay &&
!AllowColorSpaceCombination(
/*source_color_space=*/surface_iterator->color_space,
/*destination_color_space=*/primary_plane->color_space)) {
*ozone_surface_iterator = ui::OverlaySurfaceCandidate();
ozone_surface_iterator->plane_z_order = surface_iterator->plane_z_order;
continue;
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
if (shared_image_interface_) {
bool result = SetNativePixmapForCandidate(&(*ozone_surface_iterator),
surface_iterator->mailbox,
/*is_primary=*/false);
// Skip the candidate if the corresponding NativePixmap is not found.
if (!result) {
*ozone_surface_iterator = ui::OverlaySurfaceCandidate();
ozone_surface_iterator->plane_z_order =
surface_iterator->plane_z_order;
}
}
}
}
overlay_candidates_->CheckOverlaySupport(&ozone_surface_list);
// Copy information from OzoneSurfaceCandidatelist back to
// OverlayCandidateList.
{
DCHECK_EQ(full_size, ozone_surface_list.size());
auto ozone_surface_iterator = ozone_surface_list.cbegin();
// The primary plane is always handled, and don't need to copy information.
if (primary_plane)
ozone_surface_iterator++;
auto surface_iterator = surfaces->begin();
for (; surface_iterator < surfaces->end() &&
ozone_surface_iterator < ozone_surface_list.cend();
surface_iterator++, ozone_surface_iterator++) {
surface_iterator->overlay_handled =
ozone_surface_iterator->overlay_handled;
surface_iterator->display_rect = ozone_surface_iterator->display_rect;
}
}
}
gfx::Rect OverlayProcessorOzone::GetOverlayDamageRectForOutputSurface(
const OverlayCandidate& overlay) const {
return ToEnclosedRect(overlay.display_rect);
}
bool OverlayProcessorOzone::SetNativePixmapForCandidate(
ui::OverlaySurfaceCandidate* candidate,
const gpu::Mailbox& mailbox,
bool is_primary) {
DCHECK(shared_image_interface_);
UMA_HISTOGRAM_BOOLEAN(
"Compositing.Display.OverlayProcessorOzone."
"IsCandidateSharedImage",
mailbox.IsSharedImage());
if (!mailbox.IsSharedImage())
return false;
scoped_refptr<gfx::NativePixmap> native_pixmap =
shared_image_interface_->GetNativePixmap(mailbox);
if (!native_pixmap) {
// SharedImage creation and destruction happens on a different
// thread so there is no guarantee that we can always look them up
// successfully. If a SharedImage doesn't exist, ignore the
// candidate. We will try again next frame.
DLOG(ERROR) << "Unable to find the NativePixmap corresponding to the "
"overlay candidate";
ReportSharedImageExists(false);
return false;
}
ReportSharedImageExists(true);
if (is_primary && (candidate->buffer_size != native_pixmap->GetBufferSize() ||
candidate->format != native_pixmap->GetBufferFormat())) {
// If |mailbox| corresponds to the last submitted primary plane, its
// parameters may not match those of the current candidate due to a
// reshape. If the size and format don't match, skip this candidate for
// now, and try again next frame.
return false;
}
candidate->native_pixmap = std::move(native_pixmap);
candidate->native_pixmap_unique_id = MailboxToUInt32(mailbox);
return true;
}
} // namespace viz