blob: 3ac13112d2765d459d8644ba26549d995cb3f832 [file]
// Copyright 2016 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 "ui/ozone/platform/drm/host/drm_overlay_manager.h"
#include <stddef.h>
#include <algorithm>
#include "base/command_line.h"
#include "base/trace_event/trace_event.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/ozone/platform/drm/common/drm_util.h"
#include "ui/ozone/platform/drm/host/drm_overlay_candidates_host.h"
#include "ui/ozone/platform/drm/host/drm_window_host.h"
#include "ui/ozone/platform/drm/host/drm_window_host_manager.h"
#include "ui/ozone/public/overlay_surface_candidate.h"
#include "ui/ozone/public/ozone_switches.h"
namespace ui {
using OverlaySurfaceCandidateList =
OverlayCandidatesOzone::OverlaySurfaceCandidateList;
namespace {
const size_t kMaxCacheSize = 10;
// How many times a specific configuration of overlays should be requested
// before sending a GPU validation request to the GPU process.
const int kThrottleRequestSize = 3;
} // namespace
DrmOverlayManager::DrmOverlayManager(GpuThreadAdapter* proxy,
DrmWindowHostManager* window_manager)
: proxy_(proxy), window_manager_(window_manager), cache_(kMaxCacheSize) {
proxy_->RegisterHandlerForDrmOverlayManager(this);
}
DrmOverlayManager::~DrmOverlayManager() {
proxy_->UnRegisterHandlerForDrmOverlayManager();
}
std::unique_ptr<OverlayCandidatesOzone>
DrmOverlayManager::CreateOverlayCandidates(gfx::AcceleratedWidget w) {
return std::make_unique<DrmOverlayCandidatesHost>(this, w);
}
bool DrmOverlayManager::SupportsOverlays() const {
return supports_overlays_;
}
void DrmOverlayManager::CheckOverlaySupport(
OverlayCandidatesOzone::OverlaySurfaceCandidateList* candidates,
gfx::AcceleratedWidget widget) {
TRACE_EVENT0("hwoverlays", "DrmOverlayManager::CheckOverlaySupport");
OverlaySurfaceCandidateList result_candidates;
for (auto& candidate : *candidates) {
// Reject candidates that don't fall on a pixel boundary.
if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f)) {
DCHECK(candidate.plane_z_order != 0);
result_candidates.push_back(OverlaySurfaceCandidate());
result_candidates.back().overlay_handled = false;
continue;
}
result_candidates.push_back(OverlaySurfaceCandidate(candidate));
// Start out hoping that we can have an overlay.
result_candidates.back().overlay_handled = true;
if (!CanHandleCandidate(candidate, widget)) {
DCHECK(candidate.plane_z_order != 0);
result_candidates.back().overlay_handled = false;
}
}
size_t size = candidates->size();
base::AutoLock lock(cache_lock_);
auto iter = cache_.Get(result_candidates);
if (iter == cache_.end()) {
// We can skip GPU side validation in case all candidates are invalid.
bool needs_gpu_validation = std::any_of(
result_candidates.begin(), result_candidates.end(),
[](OverlaySurfaceCandidate& c) { return c.overlay_handled; });
OverlayValidationCacheValue value;
value.request_num = 0;
value.status.resize(result_candidates.size(), needs_gpu_validation
? OVERLAY_STATUS_PENDING
: OVERLAY_STATUS_NOT);
iter = cache_.Put(result_candidates, value);
}
OverlayValidationCacheValue& value = iter->second;
if (value.request_num < kThrottleRequestSize) {
value.request_num++;
} else if (value.request_num == kThrottleRequestSize) {
value.request_num++;
if (value.status.back() == OVERLAY_STATUS_PENDING)
SendOverlayValidationRequest(result_candidates, widget);
} else {
// We haven't received an answer yet.
if (value.status.back() == OVERLAY_STATUS_PENDING)
return;
const std::vector<OverlayStatus>& status = value.status;
DCHECK(size == status.size());
for (size_t i = 0; i < size; i++) {
DCHECK(status[i] == OVERLAY_STATUS_ABLE ||
status[i] == OVERLAY_STATUS_NOT);
candidates->at(i).overlay_handled = status[i] == OVERLAY_STATUS_ABLE;
}
}
}
void DrmOverlayManager::ResetCache() {
base::AutoLock lock(cache_lock_);
cache_.Clear();
}
DrmOverlayManager::OverlayValidationCacheValue::OverlayValidationCacheValue() =
default;
DrmOverlayManager::OverlayValidationCacheValue::OverlayValidationCacheValue(
const OverlayValidationCacheValue&) = default;
DrmOverlayManager::OverlayValidationCacheValue::~OverlayValidationCacheValue() =
default;
void DrmOverlayManager::SendOverlayValidationRequest(
const OverlaySurfaceCandidateList& candidates,
gfx::AcceleratedWidget widget) const {
if (!proxy_->IsConnected())
return;
TRACE_EVENT_ASYNC_BEGIN0(
"hwoverlays", "DrmOverlayManager::SendOverlayValidationRequest", this);
proxy_->GpuCheckOverlayCapabilities(widget, candidates);
}
void DrmOverlayManager::GpuSentOverlayResult(
gfx::AcceleratedWidget widget,
const OverlaySurfaceCandidateList& candidates,
const OverlayStatusList& returns) {
TRACE_EVENT_ASYNC_END0(
"hwoverlays", "DrmOverlayManager::SendOverlayValidationRequest response",
this);
base::AutoLock lock(cache_lock_);
auto iter = cache_.Peek(candidates);
if (iter != cache_.end()) {
iter->second.status = returns;
}
}
bool DrmOverlayManager::CanHandleCandidate(
const OverlaySurfaceCandidate& candidate,
gfx::AcceleratedWidget widget) const {
if (candidate.buffer_size.IsEmpty())
return false;
if (candidate.transform == gfx::OVERLAY_TRANSFORM_INVALID)
return false;
if (candidate.plane_z_order != 0) {
// It is possible that the cc rect we get actually falls off the edge of
// the screen. Usually this is prevented via things like status bars
// blocking overlaying or cc clipping it, but in case it wasn't properly
// clipped (since GL will render this situation fine) just ignore it
// here. This should be an extremely rare occurrance.
DrmWindowHost* window = window_manager_->GetWindow(widget);
if (!window->GetBounds().Contains(
gfx::ToNearestRect(candidate.display_rect))) {
return false;
}
}
if (candidate.is_clipped && !candidate.clip_rect.Contains(
gfx::ToNearestRect(candidate.display_rect))) {
return false;
}
return true;
}
} // namespace ui