Rename to GetCompositionTypes to reflect that the function is no longer setting HwcLayer state (marking). Also make the input |layers| const since it's no longer being modified. Change-Id: I53702a6df680b9a138bb16191a3cac0b17e4b632
274 lines
9.6 KiB
C++
274 lines
9.6 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 <climits>
|
|
|
|
#include "BackendManager.h"
|
|
#include "bufferinfo/BufferInfoGetter.h"
|
|
#include "drm/DrmHwc.h"
|
|
#include "hwc/HwcDisplay.h"
|
|
|
|
namespace android {
|
|
|
|
namespace {
|
|
|
|
HwcLayer *GetCursorLayer(const std::vector<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;
|
|
}
|
|
|
|
std::pair<uint32_t, uint32_t> GetDisplaySize(const HwcDisplay *display) {
|
|
const auto *config = display->GetNextConfig();
|
|
if (config == nullptr) {
|
|
return std::make_pair(0, 0);
|
|
}
|
|
|
|
return std::make_pair(config->mode.GetRawMode().hdisplay,
|
|
config->mode.GetRawMode().vdisplay);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
auto Backend::ValidateDisplay(HwcDisplay *display) -> CompositionTypeMap {
|
|
auto layers = display->GetOrderLayersByZPos();
|
|
|
|
auto flatcon = display->GetFlatCon();
|
|
if (flatcon) {
|
|
bool should_flatten = false;
|
|
if (layers.size() <= 1)
|
|
flatcon->Disable();
|
|
else
|
|
should_flatten = flatcon->NewFrame();
|
|
|
|
if (should_flatten) {
|
|
display->total_stats().frames_flattened++;
|
|
return GetCompositionTypes(layers, 0, layers.size(),
|
|
/*use_cursor_plane=*/false);
|
|
}
|
|
}
|
|
|
|
int client_start = -1;
|
|
size_t client_size = 0;
|
|
auto *cursor_layer = GetCursorLayer(layers);
|
|
auto cursor_plane = display->GetPipe().GetUsablePlanes().second;
|
|
bool use_cursor_plane = cursor_layer != nullptr && cursor_plane != nullptr &&
|
|
!IsClientLayer(display, cursor_layer) &&
|
|
cursor_plane->Get()->IsValidForLayer(
|
|
&cursor_layer->GetLayerData());
|
|
CompositionTypeMap composition_types;
|
|
|
|
// Validates layers and creates a test composition, returning whether it
|
|
// succeeded.
|
|
auto validate_and_test = [&]() -> bool {
|
|
std::tie(client_start, client_size) = GetClientLayers(display, layers,
|
|
use_cursor_plane);
|
|
composition_types = GetCompositionTypes(layers, client_start, client_size,
|
|
use_cursor_plane);
|
|
|
|
bool testing_needed = client_start != 0 || client_size != layers.size();
|
|
AtomicCommitArgs a_args = {.test_only = true};
|
|
|
|
if (testing_needed) {
|
|
return display->CreateComposition(a_args, composition_types);
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
// Initial composition attempt.
|
|
bool success = validate_and_test();
|
|
|
|
// First fallback: convert cursor layer to device composition and reattempt.
|
|
if (!success && use_cursor_plane) {
|
|
++display->total_stats().failed_kms_cursor_validate;
|
|
use_cursor_plane = false;
|
|
success = validate_and_test();
|
|
}
|
|
|
|
// Final fallback: convert all layers to client composition.
|
|
if (!success) {
|
|
++display->total_stats().failed_kms_validate;
|
|
client_start = 0;
|
|
client_size = layers.size();
|
|
composition_types = GetCompositionTypes(layers, client_start, client_size,
|
|
use_cursor_plane);
|
|
}
|
|
|
|
display->total_stats().gpu_pixops += CalcPixOps(layers, client_start,
|
|
client_size,
|
|
GetDisplaySize(display));
|
|
display->total_stats().total_pixops += CalcPixOps(layers, 0, layers.size(),
|
|
GetDisplaySize(display));
|
|
if (use_cursor_plane) {
|
|
++display->total_stats().cursor_plane_frames;
|
|
}
|
|
return composition_types;
|
|
}
|
|
|
|
std::tuple<int, size_t> Backend::GetClientLayers(
|
|
HwcDisplay *display, const std::vector<HwcLayer *> &layers,
|
|
bool use_cursor_plane) {
|
|
int client_start = -1;
|
|
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_start < 0)
|
|
client_start = (int)z_order;
|
|
client_size = (z_order - client_start) + 1;
|
|
}
|
|
}
|
|
|
|
return GetExtraClientRange(display, layers, client_start, client_size,
|
|
use_cursor_plane);
|
|
}
|
|
|
|
bool Backend::IsClientLayer(HwcDisplay *display, HwcLayer *layer) {
|
|
return !HardwareSupportsLayerType(layer->GetSfType()) ||
|
|
!layer->IsLayerUsableAsDevice() || display->CtmByGpu() ||
|
|
(layer->GetLayerData().pi.RequireScalingOrPhasing() &&
|
|
display->GetHwc()->GetResMan().ForcedScalingWithGpu());
|
|
}
|
|
|
|
bool Backend::HardwareSupportsLayerType(CompositionType comp_type) {
|
|
return comp_type == CompositionType::kDevice ||
|
|
comp_type == CompositionType::kCursor;
|
|
}
|
|
|
|
uint32_t Backend::CalcPixOps(const std::vector<HwcLayer *> &layers,
|
|
size_t first_z, size_t size,
|
|
std::pair<uint32_t, uint32_t> display_size) {
|
|
uint32_t whole_display = display_size.first * display_size.second;
|
|
uint32_t pixops = 0;
|
|
for (size_t z_order = 0; z_order < layers.size(); ++z_order) {
|
|
if (z_order >= first_z && z_order < first_z + size) {
|
|
auto *layer = layers[z_order];
|
|
auto &df = layer->GetLayerData().pi.display_frame;
|
|
if (df.i_rect.has_value()) {
|
|
pixops += (df.i_rect->right - df.i_rect->left) *
|
|
(df.i_rect->bottom - df.i_rect->top);
|
|
} else {
|
|
// nullopt frame rect means whole display.
|
|
pixops += whole_display;
|
|
}
|
|
}
|
|
}
|
|
return pixops;
|
|
}
|
|
|
|
auto Backend::GetCompositionTypes(const std::vector<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<int, int> Backend::GetExtraClientRange(
|
|
HwcDisplay *display, const std::vector<HwcLayer *> &layers,
|
|
int 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, reserve a plane for the cursor to be
|
|
// device composited.
|
|
if (!use_cursor_plane && avail_planes > 0 && layers_size > 0 &&
|
|
layers.back()->GetSfType() == CompositionType::kCursor) {
|
|
avail_planes--;
|
|
layers_size--;
|
|
}
|
|
|
|
const int extra_client = int(layers_size - client_size) - int(avail_planes);
|
|
|
|
// If extra layers need to be added to the client range, prepare to perform a
|
|
// sliding window search.
|
|
if (extra_client > 0) {
|
|
int 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 int prepend = std::min(client_start, extra_client);
|
|
const int append = std::min(int(layers_size) -
|
|
int(client_start + client_size),
|
|
extra_client);
|
|
start = client_start - (int)prepend;
|
|
client_size += extra_client;
|
|
steps = 1 + std::min(std::min(append, prepend),
|
|
int(layers_size) - int(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,
|
|
GetDisplaySize(display));
|
|
if (po < gpu_pixops) {
|
|
gpu_pixops = po;
|
|
client_start = start + int(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return std::make_tuple(client_start, client_size);
|
|
}
|
|
|
|
// clang-format off
|
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables, cert-err58-cpp)
|
|
REGISTER_BACKEND("generic", Backend);
|
|
// clang-format on
|
|
|
|
} // namespace android
|