Files

1893 lines
66 KiB
C++

/*
* Copyright (c) 2018 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#define LOG_NDEBUG 1
#include <algorithm>
#include "CompositionStrategyBase.h"
#include <DrmTypes.h>
#include <fcntl.h>
#include <DebugHelper.h>
/* Constructor function */
CompositionStrategyBase::CompositionStrategyBase() {
init();
mOsdPlaneNum = 0;
mVideoPlaneNum = 0;
mVideoComposerNum = 0;
mScaleValue = 0;
mCompositionFlag = 0;
memset(&mOsdDisplayFrame, 0, sizeof(mOsdDisplayFrame));
memset(&mDisplayMode, 0, sizeof(mDisplayMode));
}
/* Clean FrameBuffer, Composer and Plane. */
void CompositionStrategyBase::init() {
/* Reset Flags */
mHDRMode = false;
mHideSecureLayer = false;
mForceClientComposer = false;
mForceVideoComposer = false;
mHaveClient = false;
mInsideVideoFbsFlag = false;
mIsSideBandDisable = false;
mMosaicEnabled = false;
m4Mosaic = false;
mIsVideoComposerV2D = false;
mHideVideoForHdcpDev = false;
/*crtc scale info.*/
mDisplayRefFb.reset();
//memset(&mOsdDisplayFrame, 0, sizeof(mOsdDisplayFrame));
mCrtc.reset();
/* Clean FrameBuffer */
mFramebuffers.clear();
/* Clean Composer */
mDummyComposer.reset();
mClientComposer.reset();
mVideoComposers.clear();
mOtherComposers.clear();
/* Clean Plane */
mOsdPlanes.clear();
mHwcVideoPlanes.clear();
mOtherPlanes.clear();
/* Clean Composition members */
mComposer.reset();
mOverlayFbs.clear();
mComposerFbs.clear();
mVideoComposerFbs.clear();
mDisplayPairs.clear();
mMinComposerZorder = INVALID_ZORDER;
mMaxComposerZorder = INVALID_ZORDER;
mMinVideoZorder = INVALID_ZORDER;
mMaxVideoZorder = INVALID_ZORDER;
mDumpStr.clear();
}
int CompositionStrategyBase::deleteFbFromVectorFbs(
std::vector<std::shared_ptr<DrmFramebufferBase>> & inputFbs,
std::shared_ptr<DrmFramebufferBase> & fb) {
std::vector<std::shared_ptr<DrmFramebufferBase>>::iterator itErase;
if (!fb.get())
return 0;
/* remove from list */
itErase = std::find(inputFbs.begin(), inputFbs.end(), fb);
if (itErase != inputFbs.end()) {
inputFbs.erase(itErase);
return 0;
}
return 1;
}
int CompositionStrategyBase::chooseVideoFbsBaseNoOverlap(
std::vector<std::shared_ptr<DrmFramebufferBase>> & inputVideoFbs,
std::vector<std::shared_ptr<DrmFramebufferBase>> & outputVideoFbs) {
drm_rect_t dispFrame, dispFrame1;
bool is_overlap;
std::shared_ptr<DrmFramebufferBase> fb, fb1;
for (auto it = inputVideoFbs.begin(); it != inputVideoFbs.end(); it++) {
fb = *it;
is_overlap = false;
/* find one that did not overlap with others */
dispFrame = fb->getDisplayFrame();
for (auto it1 = it; it1 != inputVideoFbs.end(); it1++) {
fb1 = *it1;
if (fb == fb1)
continue;
dispFrame1 = fb1->getDisplayFrame();
if (std::max(0, std::min(dispFrame.right, dispFrame1.right) -
std::max(dispFrame.left, dispFrame1.left)) *
std::max(0, std::min(dispFrame.bottom, dispFrame1.bottom) -
std::max(dispFrame.top, dispFrame1.top)) != 0) {
is_overlap = true;
break;
}
}
if (!is_overlap) {
MESON_LOGV("[%s] Layer(%s) buffer is not overlap with others",
__func__, fb->getDebugInfo());
outputVideoFbs.push_back(fb);
}
}
return 0;
}
int CompositionStrategyBase::chooseOneVideoFbBaseBiggestWindow(
std::vector<std::shared_ptr<DrmFramebufferBase>> & inputVideoFbs,
std::shared_ptr<DrmFramebufferBase> & outputVideoFb) {
drm_rect_t dispFrame;
std::shared_ptr<DrmFramebufferBase> fb = nullptr;
std::shared_ptr<DrmFramebufferBase> large_fb = nullptr;
int region = 0, max_region = 0;
outputVideoFb.reset();
if (inputVideoFbs.empty())
return 0;
for (auto it = inputVideoFbs.begin(); it != inputVideoFbs.end(); it++) {
fb = *it;
dispFrame = fb->getDisplayFrame();
region = (dispFrame.right - dispFrame.left) * (dispFrame.bottom - dispFrame.top);
if (!large_fb) {
large_fb = fb;
max_region = region;
continue;
}
/* find the largest the frame */
if (region > max_region) {
large_fb = fb;
max_region = region;
} else if (region == max_region) {
if (fb->getZorder() > large_fb->getZorder())
large_fb = fb;
}
}
outputVideoFb = large_fb;
dispFrame = large_fb->getDisplayFrame();
MESON_LOGV("[%s] Layer(%s) return large region buffer (%d %d %d %d)",
__func__, large_fb->getDebugInfo(),
dispFrame.left, dispFrame.top, dispFrame.right, dispFrame.bottom);
return 0;
}
int CompositionStrategyBase::chooseOneVideoFbBaseVideoType(
std::vector<std::shared_ptr<DrmFramebufferBase>> & inputVideoFbs,
std::shared_ptr<DrmFramebufferBase> & outputVideoFb) {
int video_type;
std::vector<std::shared_ptr<DrmFramebufferBase>> fbs;
std::vector<MESON_VIDEO_TYPE> video_types {MESON_VIDEO_8K, MESON_VIDEO_DI_POST,
MESON_VIDEO_SECURE, MESON_VIDEO_DV, MESON_VIDEO_4K, MESON_VIDEO_AFBC,
MESON_VIDEO_HDR, MESON_VIDEO_HDR10_PLUS, MESON_VIDEO_HLG};
for (auto it = inputVideoFbs.begin(); it != inputVideoFbs.end(); it++) {
video_type = (*it)->getVideoType();
if (video_type > 0) fbs.push_back(*it);
MESON_LOGV("[%s] Layer(%s) videoType:0x%x",
__func__, (*it)->getDebugInfo(), video_type);
}
if (!fbs.empty()) {
auto type_it = video_types.begin();
for (; type_it != video_types.end(); type_it++) {
bool have_type = false;
for (auto fbIt = fbs.begin(); fbIt != fbs.end(); fbIt++) {
video_type = (*fbIt)->getVideoType();
if ((video_type & *type_it) == *type_it) {
have_type = true;
break;
}
}
if (have_type) {
for (auto fbIt = fbs.begin(); fbIt != fbs.end();) {
video_type = (*fbIt)->getVideoType();
if ((video_type & *type_it) == *type_it)
fbIt++;
else
fbIt = fbs.erase(fbIt);
}
}
if (fbs.size() == 1)
break;
}
}
outputVideoFb.reset();
if (fbs.size() == 1) {
outputVideoFb = fbs[0];
} else if (fbs.size() >= 1) {
chooseOneVideoFbBaseBiggestWindow(fbs, outputVideoFb);
}
if (outputVideoFb.get())
MESON_LOGV("[%s] choose the fb (%s), videoType:0x%x",
__func__, outputVideoFb->getDebugInfo(),
outputVideoFb->getVideoType());
return 0;
}
bool CompositionStrategyBase::chooseOneVideoFbBasePriority(
std::vector<std::shared_ptr<DrmFramebufferBase>> & inputVideoFbs,
std::shared_ptr<DrmFramebufferBase> & outputVideoFb) {
std::vector<uint32_t> videoPrioritys;
if (inputVideoFbs.empty())
return false;
for (auto it = inputVideoFbs.begin(); it != inputVideoFbs.end(); it ++) {
videoPrioritys.push_back((*it)->getVideoPriority() & 0xFFFF);
}
if (videoPrioritys.size() <= 1)
return false;
std::sort(videoPrioritys.begin(), videoPrioritys.end());
if (std::find((++videoPrioritys.begin()), videoPrioritys.end(),
*(videoPrioritys.begin())) != videoPrioritys.end()) {
return false;
}
std::map<uint32_t, std::shared_ptr<DrmFramebufferBase>, std::less<uint32_t>> bufferPrioritys;
for (auto it = inputVideoFbs.begin(); it != inputVideoFbs.end(); it ++) {
bufferPrioritys.insert(make_pair((*it)->getVideoPriority(), *it));
}
outputVideoFb = bufferPrioritys.begin()->second;
return true;
}
int CompositionStrategyBase::chooseOneVideoFb(
std::shared_ptr<DrmFramebufferBase> & outputVideoFb,
bool bVideoCompose) {
std::vector<std::shared_ptr<DrmFramebufferBase>> noOverlayFbs;
outputVideoFb.reset();
// no Video Fbs
if (mVideoComposerFbs.empty())
return -EINVAL;
if (!bVideoCompose) {
chooseOneVideoFbBaseVideoType(mVideoComposerFbs, outputVideoFb);
if (!outputVideoFb.get())
chooseOneVideoFbBaseBiggestWindow(mVideoComposerFbs, outputVideoFb);
} else {
bool bHavePriority = chooseOneVideoFbBasePriority(mVideoComposerFbs, outputVideoFb);
if (!bHavePriority) {
chooseVideoFbsBaseNoOverlap(mVideoComposerFbs, noOverlayFbs);
/* at least one buffer in noOverlayFbs vector */
if (noOverlayFbs.size() == 1) {
outputVideoFb = *noOverlayFbs.begin();
} else {
chooseOneVideoFbBaseVideoType(noOverlayFbs, outputVideoFb);
if (!outputVideoFb.get()) {
chooseOneVideoFbBaseBiggestWindow(noOverlayFbs, outputVideoFb);
}
}
}
}
if (outputVideoFb.get()) {
MESON_LOGV("[%s]: choose the fb (%s)", __func__, outputVideoFb->getDebugInfo());
deleteFbFromVectorFbs(mVideoComposerFbs, outputVideoFb);
}
return 0;
}
/*TODO: only one legacy sideband supported now if hw support sideband video */
int CompositionStrategyBase::chooseVideoFbFromLegacySideband(
std::vector<std::shared_ptr<DrmFramebufferBase>> & sidebandFbs,
std::shared_ptr<DrmFramebufferBase> & outputVideoFb) {
outputVideoFb = nullptr;
if (sidebandFbs.empty())
return 0;
auto it = sidebandFbs.begin();
if (!mIsSideBandDisable) {
outputVideoFb = (*it);
it ++;
}
for (; it != sidebandFbs.end(); it ++) {
MESON_LOGD("%s, too many legacy sideband, skip (%s)",
__func__, (*it)->getDebugInfo());
(*it)->setCompositionType(MESON_COMPOSITION_DUMMY);
}
return 0;
}
/*
* If all the 4 video meet the following requirements, then all of then can send to vd1.
* 1. each video is within its own 1/4 window
* 2. The horizontal border distance from each video to its 1/4 window must be equal
* 3. 4 video must be all afbc
* 4. The distance to the border must be a multiple of 8
* 5. only support when refresh rate <= 60hz
*/
bool CompositionStrategyBase::checkAndHandle4Mosaic(uint32_t videoZorder) {
if (!mMosaicEnabled)
return false;
// first check if hwc video plane (VD1) support mosaic feature
HwDisplayPlaneBase *plane = (HwDisplayPlaneBase *)(mHwcVideoPlanes[0].get());
if (!plane)
return false;
if (plane->isSupportMosaic() == false)
return false;
// mosaic need disable when output refresh rate > 60hz
if (mDisplayMode.refreshRate > DEFAULT_REFRESH_RATE)
return false;
// check 4 video playback
if (mVideoComposerFbs.empty() || mVideoComposerFbs.size() != 4)
return false;
std::shared_ptr<DrmFramebufferBase> fb;
uint32_t halfWidth = mDisplayMode.pixelW/2;
uint32_t halfHeight = mDisplayMode.pixelH/2;
// 2*2 mosaic check
int leftWindow = 0;
int topWindow = 0;
for (auto it = mVideoComposerFbs.begin(); it != mVideoComposerFbs.end(); it++) {
fb = *it;
drm_rect_t displayFrame = fb->getDisplayFrame();
uint32_t frameWidth = displayFrame.right - displayFrame.left;
uint32_t frameHeight = displayFrame.bottom - displayFrame.top;
// check all afbc
if (!(fb->getVideoType() & MESON_VIDEO_AFBC))
return false;
// check if the width and height <= 1/2 vinfo */
if (frameWidth > halfWidth || frameHeight > halfHeight)
return false;
// check the border distance
if (displayFrame.left < halfWidth) {
leftWindow++;
if (displayFrame.left != halfWidth - displayFrame.right)
return false;
// check border distance
if (displayFrame.left % 8 != 0)
return false;
} else {
if (displayFrame.left - halfWidth != mDisplayMode.pixelW - displayFrame.right)
return false;
// check border distance
if ((displayFrame.left - halfWidth) % 8 != 0)
return false;
}
if (displayFrame.top < halfHeight) {
topWindow++;
}
}
// check each video within its own 1/4 window
if (topWindow != leftWindow)
return false;
m4Mosaic = true;
/*set video composer*/
std::vector<std::shared_ptr<DrmFramebufferBase>> nofbs;
auto composer = mVideoComposers[0];
composer->prepare();
composer->addInputs(mVideoComposerFbs, nofbs, 0);
/*-----set buffer to displaypair------*/
mDisplayPairs.push_back(DisplayPair{
(uint32_t)mOsdPlaneNum, videoZorder, std::move(composer), mVideoComposerFbs, std::move(mHwcVideoPlanes[0])});
mHwcVideoPlanes.erase(mHwcVideoPlanes.begin());
mVideoComposers.erase(mVideoComposers.begin());
return true;
}
int CompositionStrategyBase:: processVideoFbs() {
bool bVideoCompose = false;
int videoFbNum = 0;
uint32_t usedPlanes = 0;
uint32_t minVideoZ = INVALID_ZORDER;
uint32_t maxVideoZ = INVALID_ZORDER;
uint32_t fbZorder = INVALID_ZORDER;
meson_composition_t compositionType = MESON_COMPOSITION_DUMMY;
drm_fb_type_t fbType = DRM_FB_UNDEFINED;
std::shared_ptr<DrmFramebufferBase> fb = nullptr;
std::vector<std::shared_ptr<DrmFramebufferBase>> sidebandFbs;
for (auto fbIt = mFramebuffers.begin(); fbIt != mFramebuffers.end(); ++fbIt) {
bool bSideband = false;
fb = fbIt->second;
/* skip dummy */
compositionType = (meson_composition_t)fb->getCompositionType();
if (compositionType == MESON_COMPOSITION_DUMMY)
continue;
fbType = fb->getFbType();
fbZorder = fb->getZorder();
switch (fbType) {
case DRM_FB_VIDEO_OVERLAY:
case DRM_FB_VIDEO_SIDEBAND:
case DRM_FB_VIDEO_SIDEBAND_TV:
bSideband = true;
[[clang::fallthrough]];
case DRM_FB_VIDEO_DMABUF:
case DRM_FB_VIDEO_UVM_DMA:
case DRM_FB_VIDEO_TUNNEL_SIDEBAND:
if (mHideVideoForHdcpDev) {
fb->setCompositionType(MESON_COMPOSITION_DUMMY);
break;
}
if (mVideoPlaneNum != 0) {
if (bSideband) {
sidebandFbs.push_back(fb);
} else if (fb->isBufferValidate()) {
mVideoComposerFbs.push_back(fb);
} else {
fb->setCompositionType(MESON_COMPOSITION_DUMMY);
continue;
}
} else {
fb->setCompositionType(MESON_COMPOSITION_DUMMY);
continue;
}
videoFbNum++;
if (minVideoZ == INVALID_ZORDER || fbZorder < minVideoZ)
minVideoZ = fbZorder;
if (maxVideoZ == INVALID_ZORDER || fbZorder > maxVideoZ)
maxVideoZ = fbZorder;
break;
default:
break;
};
}
/*
* Composition: only 1 + .. + N mode now
* For vd1:
* 1. sideband.
* 2. special video types: AFBC/DV/HDR/HDR10/HLG/SECURE/DIPOST.
* 3. no overlap & biggest one.
* For the last vd:
* 1. compose always happened on the last vd.
* 2. the last vd compose always output fullscreen buffer.
* 3. vd1 should on top of vd2 when have video composed.
*/
videoFbNum = (sidebandFbs.size() > 0 ? 1 : 0) + (videoFbNum - sidebandFbs.size());
bVideoCompose = videoFbNum > mVideoPlaneNum ? true : false;
if (!bVideoCompose) {
/*clear hold buffer resource*/
auto it = mVideoComposers.begin();
for (; it != mVideoComposers.end(); it++)
(*it)->prepare();
/* do not disable composer in here,
* disable composer need after disable video plane */
}
if (videoFbNum == 0) return 0;
fb.reset();
chooseVideoFbFromLegacySideband(sidebandFbs, fb);
sidebandFbs.clear();
if (fb.get()) {
/* have legacy sideband video need to display */
if (mVideoPlaneNum == 1) {
mDisplayPairs.push_back(DisplayPair{
(uint32_t)mOsdPlaneNum, fb->getZorder(), nullptr, {fb}, mHwcVideoPlanes[0]});
fb->setCompositionType(MESON_COMPOSITION_PLANE_HWCVIDEO);
if (mVideoComposerFbs.size() > 0) {
for (auto it = mVideoComposerFbs.begin(); it != mVideoComposerFbs.end(); it++)
(*it)->setCompositionType(MESON_COMPOSITION_DUMMY);
}
mHwcVideoPlanes.erase(mHwcVideoPlanes.begin());
return 0;
}
} else if (checkAndHandle4Mosaic(minVideoZ)) {
/* there are no legacy sideband video */
return 0;
} else if (mForceVideoComposer) {
/*set video to composer, only support V2D composer */
std::vector<std::shared_ptr<DrmFramebufferBase>> nofbs;
auto composer = mVideoComposers[0];
composer->prepare();
composer->addInputs(mVideoComposerFbs, nofbs, 1);
/*-----set buffer to displaypair------*/
mDisplayPairs.push_back(DisplayPair{(uint32_t)mOsdPlaneNum,
minVideoZ, std::move(composer), std::move(mVideoComposerFbs), std::move(mHwcVideoPlanes[0])});
mHwcVideoPlanes.erase(mHwcVideoPlanes.begin());
mVideoComposers.erase(mVideoComposers.begin());
return 0;
}
for (int i = 0; i < videoFbNum; i++) {
std::vector<std::shared_ptr<DrmFramebufferBase>> nofbs;
std::vector<std::shared_ptr<DrmFramebufferBase>> inputFbs;
std::shared_ptr<IComposer> composer = nullptr;
if (mIsVideoComposerV2D) {
composer = mVideoComposers[usedPlanes];
composer->prepare();
}
usedPlanes++;
/* Fbs set to the last hwcVideoPlane */
if (i >= mVideoPlaneNum - 1) {
if (mVideoComposerFbs.size() > 0) {
if (bVideoCompose) {
/* make sure Z-Order of the last VD is the minimum */
fbZorder = minVideoZ;
if (!composer.get()) {
/* composer is MESON_COMPOSITION_DI*/
composer = mVideoComposers[0];
composer->prepare();
}
/*set dicomposer and get output video.*/
composer->addInputs(mVideoComposerFbs, nofbs, i);
/*-----set buffer to displaypair------*/
mDisplayPairs.push_back(DisplayPair{(uint32_t)mOsdPlaneNum + i,
fbZorder, std::move(composer), std::move(mVideoComposerFbs), std::move(mHwcVideoPlanes[i])});
} else {
fb = *mVideoComposerFbs.begin();
fbZorder = fb->getZorder();
fb->setCompositionType(MESON_COMPOSITION_PLANE_HWCVIDEO);
inputFbs.push_back(std::move(fb));
if (composer.get())
composer->addInputs(inputFbs, nofbs);
/*-----set buffer to displaypair------*/
mDisplayPairs.push_back(DisplayPair{(uint32_t)mOsdPlaneNum + i,
fbZorder, std::move(composer), std::move(inputFbs), std::move(mHwcVideoPlanes[i])});
}
}
break;
}
if (!fb.get()) {
chooseOneVideoFb(fb, bVideoCompose);
if (!fb.get())
return 0;
}
fb->setCompositionType(MESON_COMPOSITION_PLANE_HWCVIDEO);
if (bVideoCompose) {
/* make sure Z-Order of the last VD is the minimum */
fbZorder = fb->getZorder() + 1;
} else {
fbZorder = fb->getZorder();
}
inputFbs.push_back(fb);
if (composer.get())
composer->addInputs(inputFbs, nofbs);
mDisplayPairs.push_back(DisplayPair{
(uint32_t)mOsdPlaneNum + i, fbZorder, std::move(composer), std::move(inputFbs), std::move(mHwcVideoPlanes[i])});
fb.reset();
}
/* removed used planes from mHwcVideoPlanes */
for (uint32_t i = 0; i < usedPlanes; i++)
mHwcVideoPlanes.erase(mHwcVideoPlanes.begin());
/* removed used videoComposer from mVideoComposers */
if (mIsVideoComposerV2D) {
for (uint32_t i = 0; i < usedPlanes; i++)
mVideoComposers.erase(mVideoComposers.begin());
} else if (bVideoCompose) {
mVideoComposers.clear();
} else {
// nothing to do;
}
return 0;
}
/* Apply flag with secure and forceClient Fbs. */
int CompositionStrategyBase::applyCompositionFlags() {
if (!mHideSecureLayer && !mForceClientComposer) {
return 0;
}
std::shared_ptr<DrmFramebufferBase> fb;
auto fbIt = mFramebuffers.begin();
for (; fbIt != mFramebuffers.end(); ++fbIt) {
fb = fbIt->second;
if (fb->getCompositionType() == MESON_COMPOSITION_UNDETERMINED) {
if (mHideSecureLayer && fb->getSecureFlag()) {
fb->setCompositionType(MESON_COMPOSITION_DUMMY);
} else if (mForceClientComposer) {
fb->setCompositionType(MESON_COMPOSITION_CLIENT);
}
}
}
return 0;
}
int CompositionStrategyBase::processGfxFbs() {
/* Remove dummy and video Fbs for later osd composition.
* Pickout OSD Fbs.
* Save client flag.
*/
pickoutOsdFbs();
if (!mInsideVideoFbsFlag) {
handleOsdComposition();
} else {
handleOsdCompositionWithVideo();
}
/* record overlayFbs and start to compose */
if (mComposer.get()) {
mComposer->prepare();
mComposer->addInputs(mComposerFbs, mOverlayFbs);
}
return 0;
}
/* Delete dummy and video Fbs, then pickout OSD Fbs. */
int CompositionStrategyBase::pickoutOsdFbs() {
std::shared_ptr<DrmFramebufferBase> fb;
std::vector<std::shared_ptr<DrmFramebufferBase>> dummyFbs;
bool bRemove = false;
bool bClientLayer = false;
bool mHaveVirLayer = false;
uint32_t fbZorder = -1;
meson_composition_t compositionType = MESON_COMPOSITION_DUMMY;
auto fbIt = mFramebuffers.begin();
for (; fbIt != mFramebuffers.end(); ) {
fb = fbIt->second;
bRemove = false;
bClientLayer = false;
if (!fb->isBufferValidate())
fb->setCompositionType(MESON_COMPOSITION_DUMMY);
compositionType = (meson_composition_t)fb->getCompositionType();
fbZorder = fb->getZorder();
switch (compositionType) {
case MESON_COMPOSITION_DUMMY:
dummyFbs.push_back(fb);
bRemove = true;
break;
case MESON_COMPOSITION_PLANE_HWCVIDEO:
case MESON_COMPOSITION_DI:
case MESON_COMPOSITION_V2D:
MESON_LOGV("collect overlay fb (%s)", fb->getDebugInfo());
mOverlayFbs.push_back(fb);
bRemove = true;
break;
case MESON_COMPOSITION_CLIENT:
mHaveClient = true;
bClientLayer = true;
[[clang::fallthrough]];
case MESON_COMPOSITION_UNDETERMINED:
{
if (mOsdPlaneNum == 0) {
fb->setCompositionType(MESON_COMPOSITION_DUMMY);
dummyFbs.push_back(fb);
bRemove = true;
break;
}
if (bClientLayer || !hasPlaneSupport(fb)) {
if (mMinComposerZorder == INVALID_ZORDER ||
mMaxComposerZorder == INVALID_ZORDER) {
mMinComposerZorder = fbZorder;
mMaxComposerZorder = fbZorder;
} else {
if (mMinComposerZorder > fbZorder)
mMinComposerZorder = fbZorder;
if (mMaxComposerZorder < fbZorder)
mMaxComposerZorder = fbZorder;
}
}
}
break;
default:
MESON_LOGE("Unknown composition type %s",
compositionTypeToString(fb->getCompositionType()));
bRemove = true;
break;
}
if (bRemove)
fbIt = mFramebuffers.erase(fbIt);
else
++ fbIt;
}
if (dummyFbs.size() > 0) {
std::vector<std::shared_ptr<DrmFramebufferBase>> dummyOverlayFbs;
if (mDummyComposer.get())
mDummyComposer->addInputs(dummyFbs, dummyOverlayFbs);
}
/* Only support one legacy video in current times. */
#if ENABLE_LEGACY_VIDEO
if (!mOverlayFbs.empty() && !mFramebuffers.empty()) {
auto osdFbIt = mFramebuffers.begin();
uint32_t minOsdFbZorder = osdFbIt->second->getZorder();
/* Current only input one Legacy video fb. */
std::shared_ptr<DrmFramebufferBase> legacyVideoFb = *(mOverlayFbs.begin());
if (legacyVideoFb->getZorder() > minOsdFbZorder) {
mMinComposerZorder = mFramebuffers.begin()->second->getZorder();
/* Legacy video is always on the bottom.
* SO, all fbs below legacyVideo zorder need to compose.
* Set maxClientZorder = legacyVideoZorder
*/
if (mMaxComposerZorder == INVALID_ZORDER || legacyVideoFb->getZorder() > mMaxComposerZorder) {
mMaxComposerZorder = legacyVideoFb->getZorder();
}
}
}
#else // If only one legacy video in current times, don't need to check inside video flag.
/* 1. check mInsideVideoFbsFlag = false
* 2. for HDR mode, adjust compose range.
*/
if (!mOverlayFbs.empty() && !mFramebuffers.empty()) {
bool bVideoCompose = mOverlayFbs.size() > 2 ? true : false;
auto uiFbIt = mFramebuffers.begin();
uint32_t uiFbMinZorder = uiFbIt->second->getZorder();
uiFbIt = mFramebuffers.end();
uiFbIt --;
uint32_t uiFbMaxZorder = uiFbIt->second->getZorder();
int videoOnTop = 0;
for (auto videoFbIt = mOverlayFbs.begin(); videoFbIt != mOverlayFbs.end(); ) {
std::shared_ptr<DrmFramebufferBase> videoFb = *videoFbIt;
fbZorder = videoFb->getZorder();
if (fbZorder >= uiFbMinZorder && fbZorder <= uiFbMaxZorder) {
if (mHDRMode || bVideoCompose) {
/* For hdr composition: it's hw limit.
* video should top or bottom.
* when video layer is not top, make all the lower framebuffers composed.
* For fixed planemode, video is always bottom.
*/
mMinComposerZorder = mFramebuffers.begin()->second->getZorder();
if (mMaxComposerZorder == INVALID_ZORDER || fbZorder > mMaxComposerZorder) {
mMaxComposerZorder = fbZorder;
}
} else {
mInsideVideoFbsFlag = true;
}
++videoFbIt;
} else {
/*only one video on top of ui */
if (fbZorder > uiFbMaxZorder) {
if (videoOnTop >= 1 || bVideoCompose) {
++videoFbIt;
continue;
}
videoOnTop ++;
}
MESON_LOGV("%s remove overlay fb (%s)", __func__, videoFb->getDebugInfo());
videoFbIt = mOverlayFbs.erase(videoFbIt);
}
}
}
#endif
return 0;
}
bool CompositionStrategyBase::hasPlaneSupport(std::shared_ptr<DrmFramebufferBase> fb) {
for (const auto& plane : mOsdPlanes) {
if (plane && plane->isFbSupport(fb)) {
return true;
}
}
return false;
}
bool CompositionStrategyBase::tryAllocate(int idx, const std::vector<std::shared_ptr<DrmFramebufferBase>>& layers,
std::vector<std::shared_ptr<HwDisplayPlaneBase>>& planes, std::vector<bool>& usablePlanes,
std::unordered_map<uint32_t, uint32_t>& results) {
if (idx == layers.size()) {
return true;
}
const auto& curLayer = layers[idx];
for (int i = 0; i < planes.size(); ++i) {
if (!usablePlanes[i] && planes[i]->isFbSupport(curLayer)) {
usablePlanes[i] = true;
results[curLayer->getZorder()] = planes[i]->getId();
if (tryAllocate(idx + 1, layers, planes, usablePlanes, results)) {
return true;
}
results.erase(curLayer->getZorder());
usablePlanes[i] = false;
}
}
return false;
}
void CompositionStrategyBase::decidePlaneAssignment() {
mLayerAssignment.clear();
if (mFramebuffers.empty()) {
MESON_LOGV("%s No framebuffers available for plane assignment", __func__);
return;
}
while (true) {
std::vector<std::shared_ptr<DrmFramebufferBase>> targetLayers;
std::vector<std::shared_ptr<HwDisplayPlaneBase>> availablePlanes;
//Step1: Collect layers that require device composition into targetLayers.
if (mMinComposerZorder == INVALID_ZORDER && mMaxComposerZorder == INVALID_ZORDER) {
targetLayers.reserve(mFramebuffers.size());
for (const auto& [zorder, buf] : mFramebuffers) {
targetLayers.push_back(buf);
}
} else {
for (const auto& [zorder, buf] : mFramebuffers) {
if (zorder < mMinComposerZorder || zorder > mMaxComposerZorder) {
targetLayers.push_back(buf);
}
}
}
//Step2: Collect available planes based on whether client composition is required;
//if client composition is involved, reserve the primary plane.
bool hasClient = mMinComposerZorder != INVALID_ZORDER && mMaxComposerZorder != INVALID_ZORDER;
for (const auto& plane : mOsdPlanes) {
if (hasClient) {
if (!(plane->getCapabilities() & PLANE_PRIMARY)) {
availablePlanes.push_back(plane);
}
} else {
availablePlanes.push_back(plane);
}
}
//Step3: Attempt to assign the layer to a plane. If successful, break the loop;
//otherwise, expand the search scope and retry.
std::vector<bool> usablePlanes(availablePlanes.size(), false);
if (tryAllocate(0, targetLayers, availablePlanes, usablePlanes, mLayerAssignment)) {
break;
}
//Step4 :Fall back to client composition for more layers if plane assignment fails.
bool expanded = false;
if (mMinComposerZorder == INVALID_ZORDER || mMaxComposerZorder == INVALID_ZORDER) {
mMinComposerZorder = mFramebuffers.begin()->first;
mMaxComposerZorder = mFramebuffers.begin()->first;
expanded = true;
} else {
auto itUpper = mFramebuffers.upper_bound(mMaxComposerZorder);
auto itLower = mFramebuffers.lower_bound(mMinComposerZorder);
//Since the whiteboard layer is on top, prioritize expanding
//the client-side compositing range downward.
if (itLower != mFramebuffers.begin()) {
--itLower;
mMinComposerZorder = itLower->first;
expanded = true;
} else if (itUpper != mFramebuffers.end()){
mMaxComposerZorder = itUpper->first;
expanded = true;
}
}
if (!expanded) {
// Fallback solution: full GPU composition.
mMinComposerZorder = mFramebuffers.begin()->first;
mMaxComposerZorder = mFramebuffers.rbegin()->first;
mLayerAssignment.clear();
MESON_LOGV("%s: Final fallback to full GPU composition", __func__);
break;
}
}
if (mMaxComposerZorder != INVALID_ZORDER &&
mMinComposerZorder != INVALID_ZORDER) {
/*will use compose fb as reffb.*/
return ;
}
/*select base fb and set base scale info.*/
int32_t minXOffset = -1, minYOffset = -1;
int32_t compositionTargetW = 0, compositionTargetH = 0;
for (auto it = mFramebuffers.begin(); it != mFramebuffers.end(); it++) {
drm_rect_t dispFrame = it->second->getDisplayFrame();
if (minXOffset == -1 || minXOffset > dispFrame.left)
minXOffset = dispFrame.left;
if (minYOffset == -1 || minYOffset > dispFrame.top)
minYOffset = dispFrame.top;
if (dispFrame.right > compositionTargetW)
compositionTargetW = dispFrame.right;
if (dispFrame.bottom > compositionTargetH)
compositionTargetH = dispFrame.bottom;
}
/*choose base fb, the scale is smallest bigger.*/
compositionTargetW = compositionTargetW - minXOffset;
compositionTargetH = compositionTargetH - minYOffset;
drm_rect_t scaleInput = {0, 0,
OSD_SCALER_INPUT_MAX_WIDTH, OSD_SCALER_INPUT_MAX_HEIGHT};
drm_rect_t scaleOutput = {0, 0, compositionTargetW, compositionTargetH};
if (1 == mFramebuffers.size()) {
if (!mDisplayRefFb)
mDisplayRefFb = mFramebuffers.begin()->second;
} else {
/*choose the scale > targetW/MAX_INPUT*/
for (auto it = mFramebuffers.begin(); it != mFramebuffers.end(); it++) {
std::shared_ptr<DrmFramebufferBase> fb = it->second;
drm_rect_t dispFrame = fb->getDisplayFrame();
drm_rect_t sourceCrop = fb->getSourceCrop();
drm_rect_t refDispFrame;
drm_rect_t refSourceCrop;
int32_t ret = compareFbScale(sourceCrop, dispFrame, scaleInput, scaleOutput);
if (0 == ret) {
mDisplayRefFb = fb;
break;
} else if (1 == ret) {
if (!mDisplayRefFb)
mDisplayRefFb = fb;
else {
refDispFrame = mDisplayRefFb->getDisplayFrame();
refSourceCrop = mDisplayRefFb->getSourceCrop();
if (-1 == compareFbScale(sourceCrop, dispFrame,
refSourceCrop, refDispFrame)) {
mDisplayRefFb = fb;
}
}
}
}
}
/*no suitable fb, we need do composition*/
if (!mDisplayRefFb) {
/* All Scale Fbs and no default composer range, select begin Fbs to composer. */
mMinComposerZorder = mFramebuffers.begin()->second->getZorder();
mMaxComposerZorder = mMinComposerZorder;
} else {
/*set display offset, the offset will be updated when commit() if reffb is composed*/
mOsdDisplayFrame.crtc_display_x = minXOffset;
mOsdDisplayFrame.crtc_display_y = minYOffset;
}
}
/*Count below, inside and up client Fbs number.
********************************************************************
** *
** |----------------------- *
** upClientNum--{----------------------- *
** |----------------------- *
** ----maxClientZorder----| *
** -----------------------} --insideClientNum *
** ----minClientZorder----| *
** |----------------------- *
** belowClientNum--{----------------------- *
** |----------------------- *
** *
********************************************************************
*/
int CompositionStrategyBase::countComposerFbs(int &belowClientNum, int &upClientNum, int &insideClientNum) {
uint32_t fbZorder = -1;
if (mMinComposerZorder == INVALID_ZORDER) {
belowClientNum = 0;
upClientNum = 0;
insideClientNum = 0;
return 0;
}
std::shared_ptr<DrmFramebufferBase> fb;
auto fbIt = mFramebuffers.begin();
for (; fbIt != mFramebuffers.end(); ++fbIt) {
fb = fbIt->second;
fbZorder = fb->getZorder();
if (fbZorder < mMinComposerZorder)
belowClientNum++;
else if (fbZorder > mMaxComposerZorder)
upClientNum++;
else
insideClientNum++;
}
return 1;
}
int CompositionStrategyBase::confirmComposerRange() {
std::shared_ptr<DrmFramebufferBase> fb;
uint32_t osdFbsNum = mFramebuffers.size();
uint32_t osdPlanesNum = mOsdPlanes.size();
int belowClientNum = 0;
int upClientNum = 0;
int insideClientNum = 0;
if (osdFbsNum == 0 || osdPlanesNum == 0) {
return 0;
}
int ret = countComposerFbs(belowClientNum, upClientNum, insideClientNum);
UNUSED(ret);
if (osdFbsNum > osdPlanesNum) { // CASE 1: osdFbsNum > osdPlanesNum , need compose more fbs.*/
/* CASE 1_1: mMinComposerZorder != INVALID_ZORDER, need do compose. */
int minNeedComposedFbs = osdFbsNum - insideClientNum - osdPlanesNum + 1; // minimum OSD Fbs need to be composered
if (mMinComposerZorder != INVALID_ZORDER) {
/* If minNeedComposedFbs = 0, use client range to compose */
if (minNeedComposedFbs > 0) {
/* compose more fbs from minimum zorder first. */
auto fbIt = mFramebuffers.begin();
if (belowClientNum > 0 && minNeedComposedFbs <= belowClientNum) {
int noComposedFbs = belowClientNum - minNeedComposedFbs;
if (noComposedFbs > 0) {
for (; fbIt != mFramebuffers.end(); ++fbIt) {
noComposedFbs -- ;
if (noComposedFbs < 0)
break;
}
}
minNeedComposedFbs = 0;
} else {
minNeedComposedFbs -= belowClientNum;
}
/* confirm the minimum zorder value. */
if (fbIt != mFramebuffers.end())
mMinComposerZorder = fbIt->second->getZorder();
/* compose fb from maximum zorder. */
if (minNeedComposedFbs > 0) {
MESON_ASSERT(upClientNum > 0, "upClientNum should > 0.");
fbIt = mFramebuffers.upper_bound(mMaxComposerZorder);
for (; fbIt != mFramebuffers.end(); ++ fbIt) {
minNeedComposedFbs --;
if (minNeedComposedFbs <= 0)
break;
}
/* we can confirm the maximum zorder value. */
if (fbIt != mFramebuffers.end())
mMaxComposerZorder = fbIt->second->getZorder();
}
}
} else {
/* CASE 1_2: no fb set to compose before, free to choose new fbs to compose */
int countComposerFbs = 0;
for (auto it = mFramebuffers.begin(); it != mFramebuffers.end(); ++it) {
fb = it->second;
countComposerFbs++;
if (countComposerFbs == minNeedComposedFbs) {
mMaxComposerZorder = fb->getZorder();
mMinComposerZorder = (mFramebuffers.begin()->second)->getZorder();
break;
}
}
}
}
return 0;
}
int32_t CompositionStrategyBase::compareFbScale(
drm_rect_t & aSrc,
drm_rect_t & aDst,
drm_rect_t & bSrc,
drm_rect_t & bDst) {
int32_t aDisplayWidth = aDst.right - aDst.left;
int32_t aDisplayHeight = aDst.bottom - aDst.top;
int32_t aSrcWidth = aSrc.right - aSrc.left;
int32_t aSrcHeight = aSrc.bottom - aSrc.top;
int32_t bDisplayWidth = bDst.right - bDst.left;
int32_t bDisplayHeight = bDst.bottom - bDst.top;
int32_t bSrcWidth = bSrc.right - bSrc.left;
int32_t bSrcHeight = bSrc.bottom - bSrc.top;
int widthCompare = aDisplayWidth*bSrcWidth - bDisplayWidth*aSrcWidth;
int heightCompare = aDisplayHeight *bSrcHeight - bDisplayHeight * aSrcHeight;
if (widthCompare == 0 && heightCompare == 0)
return 0;
else if (widthCompare > 0 && heightCompare > 0)
return 1;
else {
//MESON_LOGW("compareFbScale failed %d ,%d, %d, %d",
//widthCompare, heightCompare,
//bDisplayWidth, bDisplayHeight);
return -1;
}
}
/* Set DisplayPairs between UI(OSD) Fbs with plane. */
int CompositionStrategyBase::setOsdFbs2PlanePairs() {
if (mFramebuffers.size() == 0)
return 0;
std::shared_ptr<HwDisplayPlaneBase> osdTemp;
for (uint32_t i = 1; i < mOsdPlanes.size(); i++) {
if (mOsdPlanes[i]->getCapabilities() & PLANE_PRIMARY) {
osdTemp = mOsdPlanes[0];
mOsdPlanes[0] = mOsdPlanes[i];
mOsdPlanes[i] = std::move(osdTemp);
break;
}
}
uint32_t usedPlanes = 0;
if (mDisplayRefFb.get()) {
/*baseFb always post to din0*/
mDisplayPairs.push_back(
DisplayPair{usedPlanes, mDisplayRefFb->getZorder(), nullptr, {mDisplayRefFb}, mOsdPlanes[usedPlanes]});
/* Not composed fb, set to osd composition. */
if (mDisplayRefFb->getCompositionType() == MESON_COMPOSITION_UNDETERMINED)
mDisplayRefFb->setCompositionType(MESON_COMPOSITION_PLANE_OSD);
}
for (auto fbIt = mFramebuffers.begin(); fbIt != mFramebuffers.end(); ++fbIt) {
std::shared_ptr<DrmFramebufferBase> fb = fbIt->second;
if (fb == mDisplayRefFb)
continue;
/* fb is unscaled or is represent of composer output,
* we pick primary display.
*/
/* Pick any available plane */
if (usedPlanes != mOsdPlanes.size() - 1) {
usedPlanes++;
}
std::shared_ptr<HwDisplayPlaneBase> targetPlane = nullptr;
auto it = mLayerAssignment.find(fb->getZorder());
if (it != mLayerAssignment.end()) {
uint32_t targetId = it->second;
auto planeIt = std::find_if(mOsdPlanes.begin(), mOsdPlanes.end(),
[targetId](const auto& p) { return p->getId() == targetId; });
if (planeIt != mOsdPlanes.end()) {
targetPlane = *planeIt;
}
}
if (targetPlane != nullptr) {
mDisplayPairs.push_back(
DisplayPair{usedPlanes, fb->getZorder(), nullptr, {fb}, targetPlane});
} else {
MESON_LOGD("%s: Skip adding FB (Zorder: %u) to DisplayPairs", __func__, fb->getZorder());
}
/* Not composed fb, set to osd composition. */
if (fb->getCompositionType() == MESON_COMPOSITION_UNDETERMINED)
fb->setCompositionType(MESON_COMPOSITION_PLANE_OSD);
}
/* removed used planes from mOsdPlanes */
for (uint32_t i = 0; i <= usedPlanes; i++) {
mOsdPlanes.erase(mOsdPlanes.begin());
}
return 0;
}
/* Select composer */
int CompositionStrategyBase::selectComposer() {
if (mComposerFbs.size() == 0) {
if (mClientComposer.get())
mClientComposer->prepare();
return 0;
}
if (!mHaveClient) {
auto composerIt = mOtherComposers.begin();
for (; composerIt != mOtherComposers.end(); ++composerIt) {
if ((*composerIt)->isFbsSupport(mComposerFbs, mOverlayFbs)) {
mComposer = *composerIt;
break;
}
}
}
if (mComposer.get() == NULL)
mComposer = mClientComposer;
if (!mComposer.get())
return 0;
/* set composed fb composition type */
for (auto it = mComposerFbs.begin(); it != mComposerFbs.end(); ++it) {
(*it)->setCompositionType(mComposer->getType());
}
return 0;
}
/* Find out which fb need to compose and push DisplayPair */
int CompositionStrategyBase::fillComposerFbs() {
std::shared_ptr<DrmFramebufferBase> fb;
if (mMaxComposerZorder != INVALID_ZORDER &&
mMinComposerZorder != INVALID_ZORDER) {
auto fbIt = mFramebuffers.begin();
auto lastComposeFbIt = mFramebuffers.end();
while (fbIt != mFramebuffers.end()) {
fb = fbIt->second;
if (IS_FB_COMPOSED(fb)) {
mComposerFbs.push_back(fb);
if (lastComposeFbIt != mFramebuffers.end()) {
mFramebuffers.erase(lastComposeFbIt);
}
lastComposeFbIt = fbIt;
}
++ fbIt;
}
MESON_ASSERT(lastComposeFbIt != mFramebuffers.end(), "no last composer fb !");
MESON_ASSERT(mDisplayRefFb.get() == NULL, "reffb should be NULL.");
mDisplayRefFb = lastComposeFbIt->second;
}
for (auto videoFbIt = mOverlayFbs.begin(); videoFbIt != mOverlayFbs.end();) {
std::shared_ptr<DrmFramebufferBase> videoFb = *videoFbIt;
if (IS_FB_COMPOSED(videoFb)) {
++videoFbIt;
} else {
MESON_LOGV("%s remove overlay fb (%s)", __func__, videoFb->getDebugInfo());
videoFbIt = mOverlayFbs.erase(videoFbIt);
}
}
return 0;
}
/* Update present zorder.
* If videoZ ~ (mMinComposerZorder, mMaxComposerZorder), set maxVideoZ = maxVideoZ - 1
*/
void CompositionStrategyBase::handleOverlayVideoZorder() {
auto it = mDisplayPairs.begin();
for (; it != mDisplayPairs.end(); ++it) {
auto fbIt = it->fbs.begin();
for (; fbIt != it->fbs.end(); fbIt++) {
if (IS_FB_COMPOSED((*fbIt))) {
it->presentZorder = it->presentZorder - 1;
}
}
}
}
void CompositionStrategyBase::handleVPUOddScaleLimit() {
drm_rect_t sourceCrop = {0, 0, 0,0};
uint32_t fbZorder = 0;
if (!(mCrtc->getWrFlag() & (1 << (int32_t) MesonDrmWrId::GFCD_ODD_SIZE))) {
return;
}
if (mFramebuffers.size() <= 1) {
return;
}
int deviceNum = 0;
int clientNum = 0;
bool oddInput = false;
std::shared_ptr<DrmFramebufferBase> scaleFb;
for (auto fbIt = mFramebuffers.begin(); fbIt != mFramebuffers.end(); ++fbIt) {
auto fb = fbIt->second;
sourceCrop = fb->getSourceCrop();
if ((sourceCrop.right - sourceCrop.left) % 2 != 0) {
oddInput = true;
}
// osdComposed layers
fbZorder = fb->getZorder();
if (fbZorder < mMinComposerZorder || fbZorder > mMaxComposerZorder) {
scaleFb = std::move(fb);
deviceNum++;
} else {
clientNum++;
}
}
// no device Composition or no odd input , no need check
if (deviceNum == 0 || !oddInput) {
return;
}
drm_rect_t scaleInput = {0,0,0,0};
drm_rect_t scaleOutput = {0, 0, 0,0};
// has Client composition
if (clientNum != 0) {
// input width is not odd
if (!scaleFb) {
sourceCrop = scaleFb->getSourceCrop();
if ((sourceCrop.right - sourceCrop.left) % 2 == 0) {
return;
}
}
scaleInput = {0, 0, mOsdDisplayFrame.framebuffer_w, mOsdDisplayFrame.framebuffer_h};
scaleOutput = {mOsdDisplayFrame.crtc_display_x, mOsdDisplayFrame.crtc_display_y,
mOsdDisplayFrame.crtc_display_w, mOsdDisplayFrame.crtc_display_h};
// all device compostion
} else {
auto firstIt = mFramebuffers.begin();
auto secondIt = std::next(firstIt);
if (secondIt == mFramebuffers.end()) {
return;
}
scaleInput = secondIt->second->getSourceCrop();
scaleOutput = secondIt->second->getDisplayFrame();
scaleFb = firstIt->second;
}
// need check the scale limit
drm_rect_t scaleDispFrame = scaleFb->getDisplayFrame();
sourceCrop = scaleFb->getSourceCrop();
auto scaleRet = compareFbScale(sourceCrop, scaleDispFrame, scaleInput, scaleOutput);
// force to Client compose
if (scaleRet != 0) {
for (auto fbIt = mFramebuffers.begin(); fbIt != mFramebuffers.end(); ++fbIt) {
auto fb = fbIt->second;
fbZorder = fb->getZorder();
if (mMinComposerZorder == INVALID_ZORDER || mMinComposerZorder > fbZorder) {
mMinComposerZorder = fbZorder;
}
if (mMaxComposerZorder == INVALID_ZORDER || mMaxComposerZorder < fbZorder) {
mMaxComposerZorder = fbZorder;
}
}
}
}
/*
* Scale Limitation:
* 1. VPU only support composed 2 non afbc osd layers
* 2. For afbc layers, exp_h = SourceFrame_height * 1/3 * 1.1;
* when DisplayFrame_height < exp_h then only support two layers.
*/
void CompositionStrategyBase::handleVPUScaleLimit() {
uint32_t nonAfbcFbsNumb = 0;
uint32_t limitCount = 0;
int belowClientNum = 0;
int upClientNum = 0;
int insideClientNum = 0;
std::shared_ptr<DrmFramebufferBase> fb;
for (auto fbIt = mFramebuffers.begin(); fbIt != mFramebuffers.end(); ++fbIt) {
fb = fbIt->second;
float scaleLimit = DebugHelper::getInstance().getScaleLimit();
if (scaleLimit == 0)
scaleLimit = OSD_SCALER_INPUT_MARGIN / OSD_SCALER_INPUT_FACTOR;
drm_rect_t dispFrame = fb->getDisplayFrame();
drm_rect_t sourceCrop = fb->getSourceCrop();
uint32_t fbZorder = fb->getZorder();
float expHeight = (sourceCrop.bottom - sourceCrop.top) * scaleLimit;
float dispHeight = (dispFrame.bottom - dispFrame.top) * mScaleValue;
/* osdComposed layers */
if (fbZorder < mMinComposerZorder || fbZorder > mMaxComposerZorder) {
/* it is non afbc layer */
if (!fb->isAfbcBuffer()) {
nonAfbcFbsNumb++;
}
if (expHeight > dispHeight) {
limitCount++;
if (fbZorder > mMaxComposerZorder)
upClientNum++;
else
belowClientNum++;
}
} else {
insideClientNum++;
}
}
/* minus one layer for VPU composer */
if (limitCount >= 1 || nonAfbcFbsNumb > 2) {
if (upClientNum > 0) {
auto fbIt = mFramebuffers.upper_bound(mMaxComposerZorder);
if (fbIt != mFramebuffers.end())
mMaxComposerZorder = fbIt->second->getZorder();
} else if (belowClientNum > 0) {
if (mMinComposerZorder == INVALID_ZORDER) {
/* free to select one */
mMinComposerZorder = mFramebuffers.begin()->second->getZorder();
mMaxComposerZorder = mMinComposerZorder;
} else {
auto fbIt = mFramebuffers.lower_bound(mMinComposerZorder);
if (fbIt != mFramebuffers.begin() && fbIt != mFramebuffers.end()) {
fbIt--;
mMinComposerZorder = fbIt->second->getZorder();
}
}
}
}
}
/*
Limitation:
1. scale input should smaller than 1080P.
2. din0 should input the base fb.
*/
void CompositionStrategyBase::handleVPULimit(bool video) {
UNUSED(video);
//MESON_ASSERT(video == false, "handleVPULimit havenot support video");
if (mFramebuffers.size() == 0)
return ;
handleVPUScaleLimit();
}
void CompositionStrategyBase::handleDisplayLayerZorder() {
int topVideoNum = 0;
uint32_t maxOsdZorder = INVALID_ZORDER;
for (auto it = mDisplayPairs.begin(); it != mDisplayPairs.end(); ++it) {
std::shared_ptr<HwDisplayPlaneBase> plane = it->plane;
if (OSD_PLANE == plane->getType()) {
if (maxOsdZorder == INVALID_ZORDER) {
maxOsdZorder = it->presentZorder;
} else {
if (maxOsdZorder < it->presentZorder)
maxOsdZorder = it->presentZorder;
}
it->presentZorder = it->presentZorder + OSD_FB_BEGIN_ZORDER; // osd zorder: 65 - 128
}
}
for (auto it = mDisplayPairs.begin(); it != mDisplayPairs.end(); ++it) {
std::shared_ptr<HwDisplayPlaneBase> plane = it->plane;
std::shared_ptr<DrmFramebufferBase> fb = *(it->fbs.begin());
if (HWC_VIDEO_PLANE == plane->getType() ||
DRM_VIDEO_PLANE == plane->getType()) {
if (fb->getZorder() > maxOsdZorder && topVideoNum != 1) {
it->presentZorder = it->presentZorder + TOP_VIDEO_FB_BEGIN_ZORDER; // top video zorder: 129 - 192
topVideoNum++;
} else {
it->presentZorder = it->presentZorder + BOTTOM_VIDEO_FB_BEGIN_ZORDER; // bottom video zorder: 1 - 64
}
#if ENABLE_LEGACY_VIDEO
/* legacy video need on bottom */
if (it->presentZorder >= TOP_VIDEO_FB_BEGIN_ZORDER)
it->presentZorder -= (TOP_VIDEO_FB_BEGIN_ZORDER - BOTTOM_VIDEO_FB_BEGIN_ZORDER);
#else
/* keep top video when only one video commit
* tmp solution for SWPL-116333
* */
if (mDisplayPairs.size() == 1)
it->presentZorder = it->presentZorder + TOP_VIDEO_FB_BEGIN_ZORDER;
#if KEEP_VIDEO_BOTTOM
if (it->presentZorder >= TOP_VIDEO_FB_BEGIN_ZORDER)
it->presentZorder -= (TOP_VIDEO_FB_BEGIN_ZORDER - BOTTOM_VIDEO_FB_BEGIN_ZORDER);
#endif
#endif
}
}
}
/* Handle OSD Fbs and set OsdFbs2Plane pairs. */
int CompositionStrategyBase::handleOsdComposition() {
/* Step 1:
* Judge whether compose or not.
* If need to compose, confirm max/min client zorder.
*/
confirmComposerRange();
handleVPULimit(false);
decidePlaneAssignment();
/* Step 2:
* Push composers to cache mComposerFbs to build OSD2Plane pair.
*/
fillComposerFbs();
/* Step 3:
* Select composer.
*/
selectComposer();
/* Step 4:
* Set DisplayPairs between OSD Fbs with plane.
*/
setOsdFbs2PlanePairs();
return 0;
}
int CompositionStrategyBase::handleOsdCompositionWithVideo() {
std::shared_ptr<DrmFramebufferBase> fb;
/* STEP 1: handle two video fbs. */
if (mOverlayFbs.size() > 1) {
MESON_ASSERT(mOverlayFbs.size() <= 3, "Only support 3 video layers now.");
/* CASE VIDEO_1: have two video fbs between ui fbs. */
/* Calculate max/min video zorder. */
mMinVideoZorder = INVALID_ZORDER;
mMaxVideoZorder = INVALID_ZORDER;
auto videoIt = mOverlayFbs.begin();
for (; videoIt != mOverlayFbs.end(); ++ videoIt) {
fb = *videoIt;
uint32_t fbZorder = fb->getZorder();
if (mMinVideoZorder == INVALID_ZORDER) {
mMinVideoZorder = fbZorder;
mMaxVideoZorder = fbZorder;
} else {
if (mMinVideoZorder > fbZorder)
mMinVideoZorder = fbZorder;
if (mMaxVideoZorder < fbZorder)
mMaxVideoZorder = fbZorder;
}
}
/* Judge whether double video are neighbour or not */
bool bNeighbourVideo = true;
auto fbIt = mFramebuffers.lower_bound(mMinVideoZorder);
for (; fbIt != mFramebuffers.end(); ++ fbIt) {
if (fbIt->second->getZorder() > mMinVideoZorder && fbIt->second->getZorder() < mMaxVideoZorder) {
bNeighbourVideo = false;
break;
}
}
if (!bNeighbourVideo) {
/* CASE VIDEO_1_1: two video fbs are not neighbour. */
if (mMinComposerZorder != INVALID_ZORDER) {
/* CASE VIDEO_1_1_1: have default compose range */
/* Change compose range to cover two video fbs. */
/*
case 1: Both up and below client has video |case 2_1: Only up client has video |case 2_2: Only below client has video
maxClientZorder from 5-->7 | maxClientZorder from 5-->7 | maxClientZorder not change
minClientZorder from 4-->3 | minClientZorder not change | minClientZorder from 4-->3
zorder: 8 -- osd --------- | 8 -- osd --------- | 8 -- osd ---------
7 -- maxVideo ---- maxClient(composer) | 7 -- maxVideo ---- maxClient(composer) | 7 -- client osd -- maxClient(composer)
6 -- osd --------- | 6 -- osd --------- | 6 -- maxVideo ----
5 -- client osd -- | 5 -- client osd -- | 5 -- client osd --
4 -- client osd -- | 4 -- client osd -- | 4 -- client osd --
3 -- osd --------- minClient(composer) | 3 -- minVideo ---- | 3 -- osd --------- minClient(composer)
2 -- minVideo ---- | 2 -- client osd -- minClient(composer) | 2 -- minVideo ----
1 -- osd --------- | 1 -- osd --------- | 1 -- osd ---------
*/
if (mMinComposerZorder > mMinVideoZorder) {
mMinComposerZorder = mMinVideoZorder + 1; // +1 to not compose the video
}
if (mMaxComposerZorder < mMaxVideoZorder) {
mMaxComposerZorder = mMaxVideoZorder;
}
} else {
/* CASE VIDEO_1_1_2:no default compose range
* Compose the fbs below minVideoZ, so the minVideoZ will be bottom.
* CASE: two video inside osd ui without client(default) range
* set maxClientZorder = 2
* set minClientZorder = 1
* zorder: 5 -- osd -------
* 4 -- maxVideo --
* 3 -- osd -------
* 2 -- minVideo -- maxClient(composer)
* 1 -- osd ------- minClient(composer)
*/
mMaxComposerZorder = mMinVideoZorder;
mMinComposerZorder = mFramebuffers.begin()->first;
}
/* goto case VIDEO_2 */
} else {
/* CASE VIDEO_1_2: two video fbs are neighbour, treat as one video. */
/* goto case VIDEO_2 */
}
}
/* STEP 2: now only one video fb or two neighbour video fbs.
* pick ui fbs to compose.
*/
confirmComposerRange();
handleVPULimit(true);
decidePlaneAssignment();
/* Push composers to cache mComposerFbs to build OSD2Plane pair. */
fillComposerFbs();
/* Step 3:
* Select composer.
*/
selectComposer();
setOsdFbs2PlanePairs();
return 0;
}
/* The public setup interface.
* layers: UI(include OSD and VIDEO) layer from SurfaceFlinger.
* composers: Composer style.
* planes: Get OSD and VIDEO planes from HwDisplayManager.
*/
void CompositionStrategyBase::setup(
std::vector<std::shared_ptr<DrmFramebufferBase>> & layers,
std::vector<std::shared_ptr<IComposer>> & composers,
std::vector<std::shared_ptr<HwDisplayPlaneBase>> & planes,
std::shared_ptr<HwDisplayCrtcBase> & crtc,
uint32_t reqFlag,
float scaleValue,
drm_mode_info_t mode) {
init();
mCrtc = crtc;
mCompositionFlag = reqFlag;
mScaleValue = scaleValue;
mDisplayMode = mode;
#if OSD_OUTPUT_ONE_CHANNEL
mHDRMode = true;
#else
if (reqFlag & COMPOSE_WITH_HDR_VIDEO) {
mHDRMode = true;
}
#endif
if (reqFlag & COMPOSE_HIDE_SECURE_FB) {
mHideSecureLayer = true;
}
if (reqFlag & COMPOSE_FORCE_CLIENT) {
mForceClientComposer = true;
}
#if ENABLE_GFX_HRR
if (mDisplayMode.refreshRate > 165.0f) {
mForceClientComposer = true;
}
#else
if (mDisplayMode.refreshRate > DEFAULT_REFRESH_RATE) {
mForceClientComposer = true;
}
#endif
if (reqFlag & COMPOSE_DISABLE_SIDEBAND) {
mIsSideBandDisable = true;
}
if (reqFlag & COMPOSE_ENABLE_MOSAIC_MODE) {
mMosaicEnabled = true;
}
if (reqFlag & COMPOSE_FORCE_VIDEO) {
mForceVideoComposer = true;
}
if (mCompositionFlag & COMPOSE_HIDE_VIDEO_FOR_HDCP_DEV) {
mHideVideoForHdcpDev = true;
}
/* add layers */
auto layerIt = layers.begin();
for (; layerIt != layers.end(); ++layerIt) {
std::shared_ptr<DrmFramebufferBase> layer = *layerIt;
mFramebuffers.insert(make_pair(layer->getZorder(), layer));
}
/* collect composers */
auto composerIt = composers.begin();
for (; composerIt != composers.end(); ++composerIt) {
std::shared_ptr<IComposer> composer = *composerIt;
switch (composer->getType()) {
case MESON_COMPOSITION_DUMMY:
if (mDummyComposer == NULL)
mDummyComposer = std::move(composer);
break;
case MESON_COMPOSITION_CLIENT:
if (mClientComposer == NULL)
mClientComposer = std::move(composer);
break;
case MESON_COMPOSITION_DI:
if (mVideoComposers.empty())
mVideoComposers.push_back(std::move(composer));
break;
case MESON_COMPOSITION_V2D:
mIsVideoComposerV2D = true;
mVideoComposers.push_back(std::move(composer));
break;
default:
mOtherComposers.push_back(std::move(composer));
break;
}
}
/* collect planes */
auto planeIt = planes.begin();
for (; planeIt != planes.end(); ++planeIt) {
std::shared_ptr<HwDisplayPlaneBase> plane = *planeIt;
if (!plane->isAvailable()) {
mOtherPlanes.push_back(std::move(plane));
continue;
}
switch (plane->getType()) {
case OSD_PLANE:
mOsdPlanes.push_back(std::move(plane));
break;
case DRM_VIDEO_PLANE:
case HWC_VIDEO_PLANE:
mHwcVideoPlanes.push_back(std::move(plane));
break;
default:
mOtherPlanes.push_back(std::move(plane));
break;
}
}
mVideoPlaneNum = mHwcVideoPlanes.size();
mVideoComposerNum = mVideoComposers.size();
mOsdPlaneNum = mOsdPlanes.size();
if (mIsVideoComposerV2D)
MESON_ASSERT(mVideoPlaneNum == mVideoComposerNum, "video composer count error !!!");
}
void CompositionStrategyBase::updateComposition() {
mDumpStr.clear();
}
/* Decide to choose which Fbs and how to build OsdFbs2Plane pairs. */
int CompositionStrategyBase::decideComposition() {
int ret = 0;
if (mFramebuffers.empty()) {
MESON_LOGV("No layers to compose, exit.");
if (mClientComposer != NULL) {
std::shared_ptr<DrmFramebufferBase> fb = nullptr;
mClientComposer->prepare();
mClientComposer->setOutput(fb, 1);
}
fillDebugInfoStr();
return ret;
}
/* handle VIDEO fbs. */
processVideoFbs();
/* handle HDR mode, hide secure layer, and force client. */
applyCompositionFlags();
/* handle graphic fbs.*/
processGfxFbs();
mDisplayPairs.sort();
handleOverlayVideoZorder();
handleDisplayLayerZorder();
fillDebugInfoStr();
return ret;
}
/* debug info for composition strategy */
void CompositionStrategyBase::fillDebugInfoStr() {
/* replace composer output with din0 Pair. */
std::shared_ptr<DrmFramebufferBase> composerOutput;
if (mComposer.get()) {
mComposer->start();
composerOutput = mComposer->getOutput();
}
for (auto displayIt = mDisplayPairs.begin(); displayIt != mDisplayPairs.end(); ++displayIt) {
uint32_t presentZorder = displayIt->presentZorder;
std::shared_ptr<DrmFramebufferBase> fb = *(displayIt->fbs.begin());
std::shared_ptr<HwDisplayPlaneBase> plane = displayIt->plane;
int blankFlag = (mHideSecureLayer && fb->getSecureFlag()) ?
BLANK_FOR_SECURE_CONTENT : UNBLANK;
meson_composition_t compType = fb->getCompositionType();
if (composerOutput.get() &&
compType == mComposer->getType()) {
presentZorder = mMaxComposerZorder + OSD_FB_BEGIN_ZORDER;
bool bDumpPlane = true;
for (auto it = mComposerFbs.begin(); it != mComposerFbs.end(); ++it) {
if (bDumpPlane) {
dumpFbAndPlane(*it, plane, presentZorder, blankFlag);
bDumpPlane = false;
} else {
dumpComposedFb(*it);
}
}
} else if (compType == MESON_COMPOSITION_DI || compType == MESON_COMPOSITION_V2D) {
bool bDumpVideoComposerBuffer = false;
for (auto it = displayIt->fbs.begin(); it != displayIt->fbs.end(); ++it) {
if (!bDumpVideoComposerBuffer) {
dumpFbAndPlane(*it, plane, presentZorder, blankFlag);
bDumpVideoComposerBuffer = true;
} else {
dumpComposedFb(*it);
}
}
} else {
dumpFbAndPlane(fb, plane, presentZorder, blankFlag);
}
}
/* Blank un-used plane. */
auto planeIt = mHwcVideoPlanes.begin();
for (; planeIt != mHwcVideoPlanes.end(); ++planeIt)
mOtherPlanes.push_back(*planeIt);
planeIt = mOsdPlanes.begin();
for (; planeIt != mOsdPlanes.end(); ++planeIt)
mOtherPlanes.push_back(*planeIt);
planeIt = mOtherPlanes.begin();
for (; planeIt != mOtherPlanes.end(); ++planeIt)
dumpUnusedPlane(*planeIt, BLANK_FOR_NO_CONTENT);
}
int CompositionStrategyBase::getDisplayPair(std::list<DisplayPair> & displayPairs) {
displayPairs = mDisplayPairs;
return 0;
}
void CompositionStrategyBase::dump(std::string & dumpstr) {
char str[64] = {0};
snprintf(str, 63, "Composition (%s):(0x%x)\n", getName(), mCompositionFlag);
dumpstr.append(str);
dumpstr.append("--------------------------------------------------------------\n");
dumpstr.append("| FbZ | Comp Type | Plane | PlaneZ | BlankStat |\n");
dumpstr.append(mDumpStr);
dumpstr.append("--------------------------------------------------------------\n");
}