/* * 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 #include "CompositionStrategyBase.h" #include #include #include /* Constructor function */ CompositionStrategyBase::CompositionStrategyBase() { init(); mOsdPlaneNum = 0; mVideoPlaneNum = 0; mVideoComposerNum = 0; mScaleValue = 0; mCompositionFlag = 0; memset(&mOsdDisplayFrame, 0, sizeof(mOsdDisplayFrame)); } /* 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> & inputFbs, std::shared_ptr & fb) { std::vector>::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> & inputVideoFbs, std::vector> & outputVideoFbs) { drm_rect_t dispFrame, dispFrame1; bool is_overlap; std::shared_ptr 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> & inputVideoFbs, std::shared_ptr & outputVideoFb) { drm_rect_t dispFrame; std::shared_ptr fb = nullptr; std::shared_ptr 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> & inputVideoFbs, std::shared_ptr & outputVideoFb) { int video_type; std::vector> fbs; std::vector 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 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> & inputVideoFbs, std::shared_ptr & outputVideoFb) { std::vector 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, std::less> 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 & outputVideoFb, bool bVideoCompose) { std::vector> 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> & sidebandFbs, std::shared_ptr & 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 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> nofbs; auto composer = mVideoComposers[0]; composer->prepare(); composer->addInputs(mVideoComposerFbs, nofbs, 0); /*-----set buffer to displaypair------*/ mDisplayPairs.push_back(DisplayPair{ (uint32_t)mOsdPlaneNum, videoZorder, composer, mVideoComposerFbs, 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 fb = nullptr; std::vector> 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> nofbs; auto composer = mVideoComposers[0]; composer->prepare(); composer->addInputs(mVideoComposerFbs, nofbs, 1); /*-----set buffer to displaypair------*/ mDisplayPairs.push_back(DisplayPair{(uint32_t)mOsdPlaneNum, minVideoZ, composer, mVideoComposerFbs, mHwcVideoPlanes[0]}); mHwcVideoPlanes.erase(mHwcVideoPlanes.begin()); mVideoComposers.erase(mVideoComposers.begin()); return 0; } for (int i = 0; i < videoFbNum; i++) { std::vector> nofbs; std::vector> inputFbs; std::shared_ptr 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, composer, mVideoComposerFbs, mHwcVideoPlanes[i]}); } else { fb = *mVideoComposerFbs.begin(); fbZorder = fb->getZorder(); fb->setCompositionType(MESON_COMPOSITION_PLANE_HWCVIDEO); inputFbs.push_back(fb); if (composer.get()) composer->addInputs(inputFbs, nofbs); /*-----set buffer to displaypair------*/ mDisplayPairs.push_back(DisplayPair{(uint32_t)mOsdPlaneNum + i, fbZorder, composer, inputFbs, 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, composer, inputFbs, 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 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 fb; std::vector> 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> 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 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 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 fb) { for (const auto& plane : mOsdPlanes) { if (plane && plane->isFbSupport(fb)) { return true; } } return false; } bool CompositionStrategyBase::tryAllocate(int idx, const std::vector>& layers, std::vector>& planes, std::vector& usablePlanes, std::unordered_map& 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> targetLayers; std::vector> 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 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 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 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 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 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] = 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 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 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 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 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 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 = 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 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(); handleVPUOddScaleLimit(); } void CompositionStrategyBase::handleDisplayLayerZorder() { int topVideoNum = 0; uint32_t maxOsdZorder = INVALID_ZORDER; for (auto it = mDisplayPairs.begin(); it != mDisplayPairs.end(); ++it) { std::shared_ptr 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 plane = it->plane; std::shared_ptr 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 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> & layers, std::vector> & composers, std::vector> & planes, std::shared_ptr & 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 layer = *layerIt; mFramebuffers.insert(make_pair(layer->getZorder(), layer)); } /* collect composers */ auto composerIt = composers.begin(); for (; composerIt != composers.end(); ++composerIt) { std::shared_ptr composer = *composerIt; switch (composer->getType()) { case MESON_COMPOSITION_DUMMY: if (mDummyComposer == NULL) mDummyComposer = composer; break; case MESON_COMPOSITION_CLIENT: if (mClientComposer == NULL) mClientComposer = composer; break; case MESON_COMPOSITION_DI: if (mVideoComposers.empty()) mVideoComposers.push_back(composer); break; case MESON_COMPOSITION_V2D: mIsVideoComposerV2D = true; mVideoComposers.push_back(composer); break; default: mOtherComposers.push_back(composer); break; } } /* collect planes */ auto planeIt = planes.begin(); for (; planeIt != planes.end(); ++planeIt) { std::shared_ptr plane = *planeIt; if (!plane->isAvailable()) { mOtherPlanes.push_back(plane); continue; } switch (plane->getType()) { case OSD_PLANE: mOsdPlanes.push_back(plane); break; case DRM_VIDEO_PLANE: case HWC_VIDEO_PLANE: mHwcVideoPlanes.push_back(plane); break; default: mOtherPlanes.push_back(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 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 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 fb = *(displayIt->fbs.begin()); std::shared_ptr 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 & 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"); }