1
0
Fork 0
android_external_drm_hwcomp.../backend/Backend.cpp
Drew Davenport 3c2106f064 Allow Backend to create DrmDisplayPipeline
Define a PipelineCreator interface which allows Backends to use
Backend-specific heuristics in creating the DrmDisplayPipeline.

Change-Id: I1f929213ce403267b539635ec9d5853cf1a7f18a
2025-10-30 09:27:28 -06:00

282 lines
10 KiB
C++

/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "drmhwc"
#include "Backend.h"
#include <tuple>
#include <vector>
#include "BackendManager.h"
#include "compositor/LayerData.h"
#include "hwc/HwcDisplay.h"
#include "hwc/HwcLayer.h"
namespace android::drm_hwcomposer {
namespace {
const HwcLayer* GetCursorLayer(const std::vector<const HwcLayer*>& layers) {
auto it = std::find_if(layers.begin(), layers.end(),
[&](auto* layer) -> bool {
return layer->GetSfType() ==
CompositionType::kCursor;
});
if (it == layers.end()) {
return nullptr;
}
return *it;
}
} // namespace
auto Backend::ValidateDisplay(const HwcDisplay* display) const
-> ValidatedComposition {
const auto layers = display->GetOrderLayersByZPos();
const FlatteningController* flatcon = display->GetFlatCon();
if (flatcon != nullptr && flatcon->ShouldFlatten()) {
return GetFlattenedComposition(layers, FlattenReason::kStaticScene);
}
if (display->CtmByGpu()) {
return GetFlattenedComposition(layers, FlattenReason::kCtmWithOffset);
}
bool use_cursor_plane = false;
const auto* cursor_layer = GetCursorLayer(layers);
const auto cursor_plane = display->GetPipe().GetUsablePlanes().second;
if (cursor_layer != nullptr && cursor_plane != nullptr &&
!IsClientLayer(display, cursor_layer) &&
cursor_plane->Get()->IsValidForLayer(&cursor_layer->GetLayerData())) {
// Create and test a composition using only cursor plane and all other
// layers client-composited to infer whether the cursor plane can be used.
ValidatedComposition cursor_composition{
.composition_types = GetCompositionTypes(layers, 0, layers.size() - 1,
/*use_cursor_plane=*/true)};
use_cursor_plane = display->TestComposition(cursor_composition);
}
size_t client_start = 0;
size_t client_size = 0;
ValidatedComposition validated_composition{};
// Populates and tests |validated_composition|, returning whether it
// succeeded.
auto validate_and_test = [&]() -> bool {
validated_composition
.composition_types = GetCompositionTypes(layers, client_start,
client_size, use_cursor_plane);
bool testing_needed = client_start != 0 || client_size != layers.size();
if (testing_needed) {
return display->TestComposition(validated_composition);
}
// Reset the plan in case it was set during a previous test.
validated_composition.composition_plan.reset();
return true;
};
// Initial composition attempt.
std::tie(client_start, client_size) = GetClientLayers(display, layers,
use_cursor_plane);
bool success = validate_and_test();
// Cursor fallback: convert all non-cursor layers to client composition and
// reattempt. (Cursor layer is preserved as _either_ cursor _or_ device
// composited.)
if (!success && cursor_layer != nullptr) {
if (layers.back()->GetSfType() != CompositionType::kCursor) {
ALOGE("Cursor layer was not found at highest z-order");
// Continue to next fallback.
} else {
client_start = 0;
client_size = layers.size() - 1;
success = validate_and_test();
}
}
// Final fallback: convert all layers to client composition.
if (!success) {
validated_composition = GetFlattenedComposition(layers,
FlattenReason::
kValidateFailed);
}
if (use_cursor_plane) {
validated_composition.cursor_plane_validated = success;
}
return validated_composition;
}
Backend::ValidatedComposition Backend::GetFlattenedComposition(
const std::vector<const HwcLayer*>& layers, FlattenReason flatten_reason) {
return ValidatedComposition{
.composition_types = GetCompositionTypes(layers, 0, layers.size(), false),
.composition_plan = nullptr,
.flatten_reason = flatten_reason};
}
std::tuple<size_t, size_t> Backend::GetClientLayers(
const HwcDisplay* display, const std::vector<const HwcLayer*>& layers,
bool use_cursor_plane) const {
size_t client_start = 0;
size_t client_size = 0;
for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
if (IsClientLayer(display, layers[z_order])) {
if (client_size == 0) {
client_start = z_order;
}
client_size = (z_order - client_start) + 1;
}
}
return GetExtraClientRange(display, layers, client_start, client_size,
use_cursor_plane);
}
bool Backend::IsClientLayer(const HwcDisplay* display,
const HwcLayer* layer) const {
return !HardwareSupportsLayerType(layer->GetSfType()) ||
!layer->IsLayerUsableAsDevice() || display->CtmByGpu() ||
(layer->GetLayerData().pi.RequireScalingOrPhasing() &&
display->ForcedScalingWithGpu());
}
bool Backend::HardwareSupportsLayerType(CompositionType comp_type) {
return comp_type == CompositionType::kDevice ||
comp_type == CompositionType::kCursor;
}
uint32_t Backend::CalcPixOps(const std::vector<const HwcLayer*>& layers,
size_t first_z, size_t size) {
uint32_t pixops = 0;
ALOGE_IF(first_z + size > layers.size(),
"CalcPixOps provided range outside of layers");
for (size_t z_order = first_z;
z_order < std::min(first_z + size, layers.size()); ++z_order) {
pixops += layers[z_order]->GetPixOps();
}
return pixops;
}
auto Backend::GetCompositionTypes(const std::vector<const HwcLayer*>& layers,
size_t client_first_z, size_t client_size,
bool use_cursor_plane) -> CompositionTypeMap {
CompositionTypeMap composition_types;
for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
if (z_order >= client_first_z && z_order < client_first_z + client_size) {
composition_types[layers[z_order]] = CompositionType::kClient;
} else if (use_cursor_plane &&
layers[z_order]->GetSfType() == CompositionType::kCursor) {
composition_types[layers[z_order]] = CompositionType::kCursor;
} else {
composition_types[layers[z_order]] = CompositionType::kDevice;
}
}
return composition_types;
}
std::tuple<size_t, size_t> Backend::GetExtraClientRange(
const HwcDisplay* display, const std::vector<const HwcLayer*>& layers,
size_t client_start, size_t client_size, bool use_cursor_plane) {
size_t avail_planes = display->GetPipe().GetUsablePlanes().first.size();
size_t layers_size = layers.size();
// Cursor plane is not counted among |avail_planes|, so the cursor layer
// shouldn't be counted in |layers_size|.
if (use_cursor_plane) {
ALOGE_IF(layers.empty() ||
layers.back()->GetSfType() != CompositionType::kCursor,
"Cursor layer was not found at highest z-order");
--layers_size;
}
// If there are more layers than planes, save one plane for client composited
// layers.
if (avail_planes < layers_size) {
avail_planes--;
}
// If the cursor plane isn't being used, and the cursor layer isn't already
// in the client range, reserve a plane for it to be device composited.
if (!use_cursor_plane && avail_planes > 0 && layers_size > 0 &&
client_start + client_size < layers_size &&
layers.back()->GetSfType() == CompositionType::kCursor) {
avail_planes--;
layers_size--;
}
ALOGE_IF(client_start + client_size > layers.size(),
"GetExtraClientRange provided client range outside of layers");
// If extra layers need to be added to the client range, prepare to perform a
// sliding window search.
if (layers_size - client_size > avail_planes) {
const size_t extra_client = (layers_size - client_size) - avail_planes;
size_t start = 0;
size_t steps = 0;
if (client_size != 0) {
// There are already client layers present, so the window needs to
// encompass them. Determine the maximum offsets of the ensuing search.
const size_t prepend = std::min(client_start, extra_client);
const size_t append = std::min(layers_size - (client_start + client_size), extra_client);
start = client_start - prepend;
client_size += extra_client;
steps = 1 + std::min(std::min(append, prepend), layers_size - (start + client_size));
} else {
// There are no other client layers present, so the window may search the
// entire range.
client_size = extra_client;
steps = 1 + layers_size - extra_client;
}
// Use a sliding window to determine the client range that results in the
// fewest GPU pixops.
uint32_t gpu_pixops = UINT32_MAX;
for (size_t i = 0; i < steps; i++) {
const uint32_t po = CalcPixOps(layers, start + i, client_size);
if (po < gpu_pixops) {
gpu_pixops = po;
client_start = start + i;
}
}
}
return std::make_tuple(client_start, client_size);
}
class GenericBackendPipelineCreator : public BackendManager::PipelineCreator {
public:
GenericBackendPipelineCreator() : BackendManager::PipelineCreator("generic") {
}
std::unique_ptr<DrmDisplayPipeline> CreatePipeline(
DrmConnector& connector) override {
auto pipeline = DrmDisplayPipeline::CreatePipeline(connector);
if (pipeline) {
pipeline->backend = std::make_unique<Backend>();
}
return pipeline;
}
};
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp)
static GenericBackendPipelineCreator generic_backend;
} // namespace android::drm_hwcomposer