From 4c4646e7b8a5cffdc8a2d53374b5340c07d14012 Mon Sep 17 00:00:00 2001 From: Sean Paul Date: Tue, 10 May 2016 04:19:24 -0400 Subject: [PATCH] drm_hwcomposer: Introduce Planner interface This patch introduces a new Planner interface to the platform specific code. This new interface will allow for platform-specific plane provisioning decisions to cover various hardware quirks. Each platform must provide a Planner with one or more PlanStage steps. These stages are run in order and are used to move the given layers onto composition planes. There are two generic stages provided by the platform: - Protected: Places layers on dedicated planes - Greedy: Provisions as many layers to planes and sticks the rest in precomp There is also one platform-specific stage included: - ProtectedRotated: Places any protected & rotated layer on the primary plane BUG=b/28117135 TEST=Tested on ryu with a variety of window layouts Signed-off-by: Sean Paul Change-Id: Ib6062ab4779166753afaf122450bb63126bf9161 --- Android.mk | 7 +- drmdisplaycomposition.h | 2 +- platform.cpp | 167 ++++++++++++++++++++++++++++++++++++++++++++++++ platform.h | 115 +++++++++++++++++++++++++++++++++ platformdrmgeneric.cpp | 8 +++ platformnv.cpp | 88 +++++++++++++++++++++++++ platformnv.h | 18 ++++++ 7 files changed, 401 insertions(+), 4 deletions(-) create mode 100644 platform.cpp diff --git a/Android.mk b/Android.mk index 5ebdbee..5308225 100644 --- a/Android.mk +++ b/Android.mk @@ -30,6 +30,7 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES := \ + external/drm_gralloc \ external/libdrm \ external/libdrm/include/drm \ system/core/include/utils \ @@ -53,17 +54,17 @@ LOCAL_SRC_FILES := \ drmproperty.cpp \ glworker.cpp \ hwcomposer.cpp \ + platform.cpp \ + platformdrmgeneric.cpp \ + platformnv.cpp \ separate_rects.cpp \ virtualcompositorworker.cpp \ vsyncworker.cpp \ worker.cpp ifeq ($(strip $(BOARD_DRM_HWCOMPOSER_BUFFER_IMPORTER)),nvidia-gralloc) -LOCAL_SRC_FILES += platformnv.cpp LOCAL_CPPFLAGS += -DUSE_NVIDIA_IMPORTER else -LOCAL_C_INCLUDES += external/drm_gralloc -LOCAL_SRC_FILES += platformdrmgeneric.cpp LOCAL_CPPFLAGS += -DUSE_DRM_GENERIC_IMPORTER endif diff --git a/drmdisplaycomposition.h b/drmdisplaycomposition.h index 6c52664..4f48b52 100644 --- a/drmdisplaycomposition.h +++ b/drmdisplaycomposition.h @@ -21,7 +21,6 @@ #include "drmhwcomposer.h" #include "drmplane.h" #include "glworker.h" -#include "platform.h" #include #include @@ -32,6 +31,7 @@ namespace android { +class Importer; class SquashState; enum DrmCompositionType { diff --git a/platform.cpp b/platform.cpp new file mode 100644 index 0000000..105d8f7 --- /dev/null +++ b/platform.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 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 "hwc-platform" + +#include "drmresources.h" +#include "platform.h" + +#include + +namespace android { + +std::vector Planner::GetUsablePlanes( + DrmCrtc *crtc, std::vector *primary_planes, + std::vector *overlay_planes) { + std::vector usable_planes; + std::copy_if(primary_planes->begin(), primary_planes->end(), + std::back_inserter(usable_planes), + [=](DrmPlane *plane) { return plane->GetCrtcSupported(*crtc); }); + std::copy_if(overlay_planes->begin(), overlay_planes->end(), + std::back_inserter(usable_planes), + [=](DrmPlane *plane) { return plane->GetCrtcSupported(*crtc); }); + return usable_planes; +} + +std::tuple> Planner::ProvisionPlanes( + std::map &layers, bool use_squash_fb, DrmCrtc *crtc, + std::vector *primary_planes, + std::vector *overlay_planes) { + std::vector composition; + std::vector planes = + GetUsablePlanes(crtc, primary_planes, overlay_planes); + if (planes.empty()) { + ALOGE("Display %d has no usable planes", crtc->display()); + return std::make_tuple(-ENODEV, std::vector()); + } + + // If needed, reserve the squash plane at the highest z-order + DrmPlane *squash_plane = NULL; + if (use_squash_fb) { + if (!planes.empty()) { + squash_plane = planes.back(); + planes.pop_back(); + } else { + ALOGI("Not enough planes to reserve for squash fb"); + } + } + + // If needed, reserve the precomp plane at the next highest z-order + DrmPlane *precomp_plane = NULL; + if (layers.size() > planes.size()) { + if (!planes.empty()) { + precomp_plane = planes.back(); + planes.pop_back(); + composition.emplace_back(DrmCompositionPlane::Type::kPrecomp, + precomp_plane, crtc); + } else { + ALOGE("Not enough planes to reserve for precomp fb"); + } + } + + // Go through the provisioning stages and provision planes + for (auto &i : stages_) { + int ret = i->ProvisionPlanes(&composition, layers, crtc, &planes); + if (ret) { + ALOGE("Failed provision stage with ret %d", ret); + return std::make_tuple(ret, std::vector()); + } + } + + if (squash_plane) + composition.emplace_back(DrmCompositionPlane::Type::kSquash, squash_plane, + crtc); + + return std::make_tuple(0, std::move(composition)); +} + +int PlanStageProtected::ProvisionPlanes( + std::vector *composition, + std::map &layers, DrmCrtc *crtc, + std::vector *planes) { + int ret; + int protected_zorder = -1; + for (auto i = layers.begin(); i != layers.end();) { + if (!i->second->protected_usage()) { + ++i; + continue; + } + + ret = Emplace(composition, planes, DrmCompositionPlane::Type::kLayer, crtc, + i->first); + if (ret) + ALOGE("Failed to dedicate protected layer! Dropping it."); + + protected_zorder = i->first; + i = layers.erase(i); + } + + if (protected_zorder == -1) + return 0; + + // Add any layers below the protected content to the precomposition since we + // need to punch a hole through them. + for (auto i = layers.begin(); i != layers.end();) { + // Skip layers above the z-order of the protected content + if (i->first > static_cast(protected_zorder)) { + ++i; + continue; + } + + // If there's no precomp layer already queued, queue one now. + DrmCompositionPlane *precomp = GetPrecomp(composition); + if (precomp) { + precomp->source_layers().emplace_back(i->first); + } else { + if (!planes->empty()) { + DrmPlane *precomp_plane = planes->back(); + planes->pop_back(); + composition->emplace_back(DrmCompositionPlane::Type::kPrecomp, + precomp_plane, crtc, i->first); + } else { + ALOGE("Not enough planes to reserve for precomp fb"); + } + } + i = layers.erase(i); + } + return 0; +} + +int PlanStageGreedy::ProvisionPlanes( + std::vector *composition, + std::map &layers, DrmCrtc *crtc, + std::vector *planes) { + // Fill up the remaining planes + for (auto i = layers.begin(); i != layers.end(); i = layers.erase(i)) { + int ret = Emplace(composition, planes, DrmCompositionPlane::Type::kLayer, + crtc, i->first); + // We don't have any planes left + if (ret == -ENOENT) + break; + else if (ret) + ALOGE("Failed to emplace layer %zu, dropping it", i->first); + } + + // Put the rest of the layers in the precomp plane + DrmCompositionPlane *precomp = GetPrecomp(composition); + if (precomp) { + for (auto i = layers.begin(); i != layers.end(); i = layers.erase(i)) + precomp->source_layers().emplace_back(i->first); + } + + return 0; +} +} diff --git a/platform.h b/platform.h index 059c40e..da6b7cb 100644 --- a/platform.h +++ b/platform.h @@ -17,11 +17,15 @@ #ifndef ANDROID_DRM_PLATFORM_H_ #define ANDROID_DRM_PLATFORM_H_ +#include "drmdisplaycomposition.h" #include "drmhwcomposer.h" #include #include +#include +#include + namespace android { class DrmResources; @@ -46,5 +50,116 @@ class Importer { // implementation is responsible for ensuring thread safety. virtual int ReleaseBuffer(hwc_drm_bo_t *bo) = 0; }; + +class Planner { + public: + class PlanStage { + public: + virtual ~PlanStage() { + } + + virtual int ProvisionPlanes(std::vector *composition, + std::map &layers, + DrmCrtc *crtc, + std::vector *planes) = 0; + + protected: + // Removes and returns the next available plane from planes + static DrmPlane *PopPlane(std::vector *planes) { + if (planes->empty()) + return NULL; + DrmPlane *plane = planes->front(); + planes->erase(planes->begin()); + return plane; + } + + // Finds and returns the squash layer from the composition + static DrmCompositionPlane *GetPrecomp( + std::vector *composition) { + auto l = GetPrecompIter(composition); + if (l == composition->end()) + return NULL; + return &(*l); + } + + // Inserts the given layer:plane in the composition right before the precomp + // layer + static int Emplace(std::vector *composition, + std::vector *planes, + DrmCompositionPlane::Type type, DrmCrtc *crtc, + size_t source_layer) { + DrmPlane *plane = PopPlane(planes); + if (!plane) + return -ENOENT; + + auto precomp = GetPrecompIter(composition); + composition->emplace(precomp, type, plane, crtc, source_layer); + return 0; + } + + private: + static std::vector::iterator GetPrecompIter( + std::vector *composition) { + return std::find_if(composition->begin(), composition->end(), + [](const DrmCompositionPlane &p) { + return p.type() == DrmCompositionPlane::Type::kPrecomp; + }); + } + }; + + // Creates a planner instance with platform-specific planning stages + static std::unique_ptr CreateInstance(DrmResources *drm); + + // Takes a stack of layers and provisions hardware planes for them. If the + // entire stack can't fit in hardware, the Planner may place the remaining + // layers in a PRECOMP plane. Layers in the PRECOMP plane will be composited + // using GL. PRECOMP planes should be placed above any 1:1 layer:plane + // compositions. If use_squash_fb is true, the Planner should try to reserve a + // plane at the highest z-order with type SQUASH. + // + // @layers: a map of index:layer of layers to composite + // @use_squash_fb: reserve a squash framebuffer + // @primary_planes: a vector of primary planes available for this frame + // @overlay_planes: a vector of overlay planes available for this frame + // + // Returns: A tuple with the status of the operation (0 for success) and + // a vector of the resulting plan (ie: layer->plane mapping). + std::tuple> ProvisionPlanes( + std::map &layers, bool use_squash_fb, + DrmCrtc *crtc, std::vector *primary_planes, + std::vector *overlay_planes); + + template + void AddStage(A &&... args) { + stages_.emplace_back( + std::unique_ptr(new T(std::forward(args)...))); + } + + private: + std::vector GetUsablePlanes( + DrmCrtc *crtc, std::vector *primary_planes, + std::vector *overlay_planes); + + std::vector> stages_; +}; + +// This plan stage extracts all protected layers and places them on dedicated +// planes. +class PlanStageProtected : public Planner::PlanStage { + public: + int ProvisionPlanes(std::vector *composition, + std::map &layers, DrmCrtc *crtc, + std::vector *planes); +}; + +// This plan stage places as many layers on dedicated planes as possible (first +// come first serve), and then sticks the rest in a precomposition plane (if +// needed). +class PlanStageGreedy : public Planner::PlanStage { + public: + int ProvisionPlanes(std::vector *composition, + std::map &layers, DrmCrtc *crtc, + std::vector *planes); +}; } #endif diff --git a/platformdrmgeneric.cpp b/platformdrmgeneric.cpp index 099a5d9..90ba0e2 100644 --- a/platformdrmgeneric.cpp +++ b/platformdrmgeneric.cpp @@ -134,4 +134,12 @@ int DrmGenericImporter::ReleaseBuffer(hwc_drm_bo_t *bo) { } return 0; } + +#ifdef USE_DRM_GENERIC_IMPORTER +std::unique_ptr Planner::CreateInstance(DrmResources *) { + std::unique_ptr planner(new Planner); + planner->AddStage(); + return planner; +} +#endif } diff --git a/platformnv.cpp b/platformnv.cpp index 5b79826..db7ee36 100644 --- a/platformnv.cpp +++ b/platformnv.cpp @@ -183,4 +183,92 @@ int NvImporter::GrallocSetNvBuffer(buffer_handle_t handle, NvBuffer_t *buf) { GRALLOC_MODULE_PERFORM_SET_IMPORTER_PRIVATE, handle, NvGrallocRelease, buf); } + +#ifdef USE_NVIDIA_IMPORTER +// static +std::unique_ptr Planner::CreateInstance(DrmResources *) { + std::unique_ptr planner(new Planner); + planner->AddStage(); + planner->AddStage(); + planner->AddStage(); + return planner; +} +#endif + +static DrmPlane *GetCrtcPrimaryPlane(DrmCrtc *crtc, + std::vector *planes) { + for (auto i = planes->begin(); i != planes->end(); ++i) { + if ((*i)->GetCrtcSupported(*crtc)) { + DrmPlane *plane = *i; + planes->erase(i); + return plane; + } + } + return NULL; +} + +int PlanStageProtectedRotated::ProvisionPlanes( + std::vector *composition, + std::map &layers, DrmCrtc *crtc, + std::vector *planes) { + int ret; + int protected_zorder = -1; + for (auto i = layers.begin(); i != layers.end();) { + if (!i->second->protected_usage() || !i->second->transform) { + ++i; + continue; + } + + auto primary_iter = planes->begin(); + for (; primary_iter != planes->end(); ++primary_iter) { + if ((*primary_iter)->type() == DRM_PLANE_TYPE_PRIMARY) + break; + } + + // We cheat a little here. Since there can only be one primary plane per + // crtc, we know we'll only hit this case once. So we blindly insert the + // protected content at the beginning of the composition, knowing this path + // won't be taken a second time during the loop. + if (primary_iter != planes->end()) { + composition->emplace(composition->begin(), + DrmCompositionPlane::Type::kLayer, *primary_iter, + crtc, i->first); + planes->erase(primary_iter); + protected_zorder = i->first; + } else { + ALOGE("Could not provision primary plane for protected/rotated layer"); + } + i = layers.erase(i); + } + + if (protected_zorder == -1) + return 0; + + // Add any layers below the protected content to the precomposition since we + // need to punch a hole through them. + for (auto i = layers.begin(); i != layers.end();) { + // Skip layers above the z-order of the protected content + if (i->first > static_cast(protected_zorder)) { + ++i; + continue; + } + + // If there's no precomp layer already queued, queue one now. + DrmCompositionPlane *precomp = GetPrecomp(composition); + if (precomp) { + precomp->source_layers().emplace_back(i->first); + } else { + if (planes->size()) { + DrmPlane *precomp_plane = planes->back(); + planes->pop_back(); + composition->emplace_back(DrmCompositionPlane::Type::kPrecomp, + precomp_plane, crtc, i->first); + } else { + ALOGE("Not enough planes to reserve for precomp fb"); + } + } + i = layers.erase(i); + } + return 0; +} } diff --git a/platformnv.h b/platformnv.h index 652409b..a9e5d39 100644 --- a/platformnv.h +++ b/platformnv.h @@ -19,6 +19,7 @@ #include "drmresources.h" #include "platform.h" +#include "platformdrmgeneric.h" #include @@ -53,6 +54,23 @@ class NvImporter : public Importer { const gralloc_module_t *gralloc_; }; + +// This stage looks for any layers that contain transformed protected content +// and puts it in the primary plane since Tegra doesn't support planar rotation +// on the overlay planes. +// +// There are two caveats to this approach: 1- Protected content isn't +// necessarily planar, but it's usually a safe bet, and 2- This doesn't catch +// non-protected planar content. If we wanted to fix this, we'd need to import +// the buffer in this stage and peek at it's format. The overhead of doing this +// doesn't seem worth it since we'll end up displaying the right thing in both +// cases anyways. +class PlanStageProtectedRotated : public Planner::PlanStage { + public: + int ProvisionPlanes(std::vector *composition, + std::map &layers, DrmCrtc *crtc, + std::vector *planes); +}; } #endif -- 2.11.0