/* * Copyright (C) 2024 Amlogic Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "MesonDisplay.h" #include "MesonLayer.h" #include "DispAdptLog.h" MesonLayer::MesonLayer() : DrmFramebufferBase() { init(); mDisplayId = 0; } MesonLayer::MesonLayer(uint32_t dispId) : DrmFramebufferBase() { init(); mDisplayId = dispId; } MesonLayer::~MesonLayer() { if (mFd >= 0) close(mFd); mFd = -1; if (mDifd >= 0) close(mDifd); mDifd = -1; if (isVtBuffer()) { releaseVtResource(); releaseUvmResourceLock(); } else releaseResource(); } void MesonLayer::init() { mFd = -1; mDifd = -1; mTunnelFd = -1; mNeedReleaseVtResource = false; mTunnelIdUpdate = false; mVtRefreshed = false; mNeedAskRefresh = false; mDataUpdate = false; mVtUpdate = false; mQueuedFrames = 0; mGameMode = false; mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW; mVideoStatus = VT_VIDEO_STATUS_SHOW; mPreUvmBufferFd = -1; mTunnelId = -1; mExpectedPresentTime = 0; memset(&mVtSourceCrop, 0, sizeof(mVtSourceCrop)); memset(&mVtDisplayFrame, 0, sizeof(mVtDisplayFrame)); } int32_t MesonLayer::setTunnelId(int tunnelId, std::shared_ptr observer) { mTunnelId = tunnelId; if (tunnelId >= 128) { setFbType(DRM_FB_VIDEO_UVM_DMA); mTunnelFd = meson_vt_open_by_mode(0); meson_vt_connect(mTunnelFd, mTunnelId, 1); if (observer.get()) observer.reset(); } else if (tunnelId < 128 && tunnelId >= 0) { setFbType(DRM_FB_VIDEO_TUNNEL_SIDEBAND); mNeedReleaseVtResource = true; mVtTransform = 0; VtInstanceMgr::getInstance().lockInstancesMutex(); if (!mDisplayObserver.get()) mDisplayObserver = std::move(observer); int ret = registerConsumer(); if (ret >= 0) { DISPLAY_LOGD("%s [%" PRId64 "] register consumer for videotunnel %d succeeded", __func__, mId, tunnelId); mQueueItems.clear(); mTunnelIdUpdate = true; } else { DISPLAY_LOGE("%s [%" PRId64 "] register consumer for videotunnel %d failed, error %d", __func__, mId, tunnelId, ret); mTunnelId = -1; VtInstanceMgr::getInstance().unlockInstancesMutex(); return -1; } VtInstanceMgr::getInstance().unlockInstancesMutex(); } /* fbtype is not tunnel sideband */ if (mFbType != DRM_FB_VIDEO_TUNNEL_SIDEBAND) releaseUvmResourceLock(); return 0; } int32_t MesonLayer::setIndex(int index) { mIndex = index; setVideoPriority(mIndex); return 0; } int32_t MesonLayer::getIndex() { return mIndex; } int32_t MesonLayer::setZorder(uint32_t z) { mCurrZorder = z; return 0; } int32_t MesonLayer::getCurZorder() { return mCurrZorder; } void MesonLayer::updateZorder() { if (mCurrZorder != -1 && (uint32_t)mCurrZorder == mZorder) return; DrmFramebufferBase::setZorder(mCurrZorder); mDataUpdate = true; } int32_t MesonLayer::setTransform(int32_t transform) { if (mTransform == transform) return 0; mTransform = transform; mDataUpdate = true; return 0; } int32_t MesonLayer::setDisplayFrame(drm_rect_t dispFrame) { if (mDisplayFrame.left == dispFrame.left && mDisplayFrame.top == dispFrame.top && mDisplayFrame.right == dispFrame.right && mDisplayFrame.bottom == dispFrame.bottom) return 0; if (dispFrame.left <= 0 && dispFrame.top <= 0) setVideoPriority(0); else setVideoPriority(mIndex); mDataUpdate = true; return DrmFramebufferBase::setDisplayFrame(dispFrame); } int32_t MesonLayer::setCurReleaseFence(int32_t fenceFd) { mCurReleaseFence.reset(new DrmFence(fenceFd)); return 0; } int32_t MesonLayer::getCurReleaseFence() { return (mCurReleaseFence.get() ? mCurReleaseFence->dup() : -1); } int32_t MesonLayer::setProcessFence(int32_t fenceFd) { mProcessFence.reset(new DrmFence(fenceFd)); return 0; } int32_t MesonLayer::getProcessFence() { return (mProcessFence.get() ? mProcessFence->dup() : -1); } int32_t MesonLayer::onLayerDisplayed(int32_t releaseFence, int32_t processFence) { if (releaseFence < 0 || processFence < 0) { int32_t fenceFd = (releaseFence < 0) ? processFence : releaseFence; mCurReleaseFence.reset(new DrmFence(fenceFd)); } else { mCurReleaseFence = DrmFence::merge( "FBProcessor", std::make_shared(releaseFence), std::make_shared(processFence)); } return 0; } void MesonLayer::registerCallback(void *context, meson_callback_t *cb) { mContext = context; mCb = cb; } void MesonLayer::setPageFlipData(uint64_t id, void *context, adpt_page_flip_callback *cb, int fd) { std::lock_guard lock(mFlipDataMutex); struct meson_page_flip_data dat = { id, context, cb, fd }; mFlipData.emplace(id, dat); } void MesonLayer::registerPageFlipCallBack(void *context, adpt_page_flip_callback *cb) { setPageFlipData(mId, context, cb, 0); } std::map MesonLayer::getPageFlipData() { std::lock_guard lock(mFlipDataMutex); return mFlipData; } void MesonLayer::setFlipDataToUserData(void *userData) { std::lock_guard lock(mFlipDataMutex); if (userData == nullptr) return; int fd = -1; auto fData = mFlipData.find(mId); if (fData != mFlipData.end()) { if (mPrevBufferItem.get()) fd = mPrevBufferItem->mBufferFd; if (mCurBufferItem.get()) fd = mCurBufferItem->mBufferFd; fData->second.fd = fd; } MesonPageFlip *flip = (MesonPageFlip *)userData; for (const auto& pair : mFlipData) flip->pushData(pair.second); flip->incIndex(); } void MesonLayer::setPresentTime(nsecs_t expectedPresentTime) { std::lock_guard lock(mMutex); nsecs_t previousExpectedTime; previousExpectedTime = ((mExpectedPresentTime == 0) ? ns2us(expectedPresentTime) : mExpectedPresentTime); mExpectedPresentTime = ns2us(expectedPresentTime); mVsyncInterval = mExpectedPresentTime - previousExpectedTime; DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] vsyncTimeStamp:%" PRId64 ", diffExpected:%" PRId64, __func__, mDisplayId, mId, mExpectedPresentTime, mExpectedPresentTime - previousExpectedTime); } bool MesonLayer::isVtBuffer() { return mFbType == DRM_FB_VIDEO_TUNNEL_SIDEBAND; } bool MesonLayer::isFbUpdated() { std::lock_guard lock(mMutex); if (isVtBuffer()) return (shouldPresentNow(mCurBufferItem) && mVtUpdate) || mVtRefreshed; return mDataUpdate; } bool MesonLayer::shouldPresentNow(const std::shared_ptr item) { if (!item.get()) return false; if (item->mTimeStamp == -1) return true; const bool isDue = item->mTimeStamp < mExpectedPresentTime; // Ignore timestamps more than a second in the future, timestamp is us const bool isPlausible = item->mTimeStamp < (mExpectedPresentTime + s2ns(1)/1000); return isDue || !isPlausible; } bool MesonLayer::shouldPresentNow(const std::shared_ptr item) { if (!item.get()) return false; if (item->mTimeStamp == -1) return true; const bool isDue = item->mTimeStamp < mExpectedPresentTime; // Ignore timestamps more than a second in the future, timestamp is us const bool isPlausible = item->mTimeStamp < (mExpectedPresentTime + s2ns(1)/1000); return isDue || !isPlausible; } void MesonLayer::dropExpiredFrame(bool drop) { int expiredItemCount = 0; int dropCount = 0; for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) { if (((*it)->mTimeStamp > 0 && shouldPresentNow((*it))) || mVideoDisplayStatus == VT_VIDEO_STATUS_IMMEDIATELY_OUTPUT) { if (drop) expiredItemCount++; } } // drop expiredItemCount - 1 buffers dropCount = expiredItemCount - 1; while (true) { auto bufItem = mQueueItems.front(); if (dropCount <= 0) { break; } DISPLAY_LOGD("%s[%d]: mId: %lld, buffer (%s) dropped!", __func__, __LINE__, mId, bufItem->toString().c_str()); meson_vt_release_buffer(mTunnelFd, mTunnelId, bufItem->mBufferFd, -1); if (mCb != NULL && mCb->frame_dropped) mCb->frame_dropped(mContext, bufItem->mBufferFd); mQueueItems.pop_front(); dropCount--; } } bool MesonLayer::interruptProcess(bool change) { std::lock_guard lock(mMutex); if (isVtBuffer()) return false; if (mVideoDisplayStatus == VT_VIDEO_STATUS_HIDE || mVideoDisplayStatus == VT_VIDEO_STATUS_BLANK) { releaseResourceLocked(); if (change) mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW; return true; } return false; } bool MesonLayer::keepFrame() { return mVideoStatus == VT_VIDEO_STATUS_HOLD_FRAME; } void MesonLayer::onVideoCmd() { vt_cmd_t cmd; vt_cmd_data_t cmd_data; while (meson_vt_recv_cmd(mTunnelFd, mTunnelId, &cmd, &cmd_data) == 0) { switch (cmd) { case VT_CMD_SET_VIDEO_STATUS: mVideoStatus = cmd_data.data; if (mVideoStatus == VT_VIDEO_STATUS_BLANK || mVideoStatus == VT_VIDEO_STATUS_HIDE) mVideoDisplayStatus = cmd_data.data; DISPLAY_LOGV("%s[%d]: status: %d", __func__, __LINE__, mVideoStatus); break; case VT_CMD_SET_COLOR_BLACK: { struct uvm_fd_info info = { 0 }; struct vt_meta_data meta_data = { 0 }; VtAllocSolidColorBuffer::getInstance().getBufferInfo(SOLID_COLOR_FOR_BLACK, info); if (info.fd >= 0) { std::shared_ptr item = std::make_shared(info.fd, info.timestamp, info, meta_data); mQueueItems.push_back(std::move(item)); } break; } default: break; } } } void MesonLayer::updateBuffer() { std::lock_guard lock(mMutex); if (!isVtBuffer()) { int buffer_fd = 0; int fence_fd = 0; int64_t present_time = 0; struct vt_meta_data meta_data = { 0 }; while (meson_vt_acquire_buffer(mTunnelFd, mTunnelId, &buffer_fd, &fence_fd, &present_time, &meta_data) == 0) { if (mVideoStatus == VT_VIDEO_STATUS_HIDE || mVideoStatus == VT_VIDEO_STATUS_HOLD_FRAME) { meson_vt_release_buffer(mTunnelFd, mTunnelId, buffer_fd, -1); } else { struct uvm_fd_info info = { 0 }; if (!mCropItems.empty()) { info.crop_left = mCropItems[0].left; info.crop_right = mCropItems[0].right; info.crop_top = mCropItems[0].top; info.crop_bottom = mCropItems[0].bottom; info.buf_width = info.crop_right - info.crop_left; info.buf_height = info.crop_bottom - info.crop_top; mCropItems.pop_front(); } std::shared_ptr item = std::make_shared(buffer_fd, present_time, info, meta_data); DISPLAY_LOGD("%s[%d]: mId: %lld, buffer (%s) add!", __func__, __LINE__, mId, item->toString().c_str()); mQueueItems.push_back(std::move(item)); mDropEnable = ((mVsyncInterval / 1000) * 1000 < present_time - mTimeStamp); mTimeStamp = present_time; } } onVideoCmd(); if (mQueueItems.empty()) return; } dropExpiredFrame(mDropEnable); mUpdated = true; mDataUpdate = true; mCurBufferItem = mQueueItems[0]; if (mCb != NULL && mCb->frame_displayed) mCb->frame_displayed(mContext, mCurBufferItem->mBufferFd, DispAdptGetCurrentTimeUs()); DISPLAY_LOGD("%s[%d]: mId: %lld, buffer (%s) displayed!", __func__, __LINE__, mId, mCurBufferItem->toString().c_str()); if (shouldPresentNow(mCurBufferItem)) { struct uvm_fd_info info = mCurBufferItem->mInfo; if (info.buf_width == 0 || info.buf_height == 0) { int width = mSourceCrop.right - mSourceCrop.left; int height = mSourceCrop.bottom - mSourceCrop.top; setBufferInfo(width, height, VIDEO_ALIGN(width), height, DRM_FORMAT_NV21); } else { setBufferInfo(info.buf_width, info.buf_height, VIDEO_ALIGN(info.buf_width), info.buf_height, DRM_FORMAT_NV21); } } } void MesonLayer::setDiProcessorFd(int32_t fd) { std::lock_guard lock(mMutex); // do not dup fd if (mDifd >= 0) close(mDifd); if (fd >= 0) DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] set %d to DIFd (%d)", __func__, mDisplayId, mId, getBufferFdLocked(), fd); mDifd = fd; } int32_t MesonLayer::getBufferFdLocked(source_type_t & sourceType) { int curFd = -1; int prevFd = -1; int32_t fd = -1; if (mFbType == DRM_FB_VIDEO_TUNNEL_SIDEBAND) sourceType = DTV_FIX_TUNNEL; else sourceType = NORMAL_TYPE; if (mDifd >= 0) { fd = mDifd; } else if (mFbType == DRM_FB_VIDEO_DMABUF || mFbType == DRM_FB_VIDEO_UVM_DMA || mFbType == DRM_FB_VIDEO_TUNNEL_SIDEBAND) { source_type_t curType = DTV_FIX_TUNNEL; source_type_t prevType = DTV_FIX_TUNNEL; if (mCurBufferItem.get()) { curFd = mCurBufferItem->mBufferFd; if (mCurBufferItem->mIsSolidBuffer) curType = HWC_CREAT_ION; } if (mPrevBufferItem.get()) { prevFd = mPrevBufferItem->mBufferFd; if (mPrevBufferItem->mIsSolidBuffer) prevType = HWC_CREAT_ION; } if (shouldPresentNow(mCurBufferItem)) { fd = (mUpdated || mVtUpdate) ? curFd : prevFd; sourceType = (mUpdated || mVtUpdate) ? curType : prevType; } else { fd = prevFd; sourceType = prevType; } } else if (mFbType == DRM_FB_VIDEO_COMPOSE_OUTPUT) { fd = mFd; } return fd; } int32_t MesonLayer::getBufferFdLocked() { source_type_t type; return getBufferFdLocked(type); } int32_t MesonLayer::getBufferFd(source_type_t & sourceType) { std::lock_guard lock(mMutex); return getBufferFdLocked(sourceType); } int32_t MesonLayer::getBufferFd() { std::lock_guard lock(mMutex); return getBufferFdLocked(); } int32_t MesonLayer::releaseBuffer() { std::lock_guard lock(mMutex); int32_t ret = 0; if (!mCurBufferItem.get() && mPrevBufferItem.get()) mPrevBufferItem->setReleaseFence(mCurReleaseFence); if (!mUpdated) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] vt buffer not update", __func__, mDisplayId, mId); return -EAGAIN; } if (!mCurBufferItem.get()) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer item is nullprt !!", __func__, mDisplayId, mId); return -EAGAIN; } if (shouldPresentNow(mCurBufferItem) == false) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer not present, timestamp not meet", __func__, mDisplayId, mId); return -EAGAIN; } if (mQueueItems.empty()) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] Queued vtbuffer is empty!!", __func__, mDisplayId, mId); return -EAGAIN; } if (!mCurReleaseFence.get() && !mPrevBufferItem.get()) { /* TODO: this first buffer not send to display, need refresh */ DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] first buffer not display", __func__, mDisplayId, mId); return -EAGAIN; } // remove it from the queueItems mQueueItems.pop_front(); mCurBufferItem->setReleaseFence(mCurReleaseFence); mUpdated = false; mDataUpdate = false; setCurReleaseFence(-1); // First release vt buffer, save to the previous if (!mPrevBufferItem.get()) { mPrevBufferItem = mCurBufferItem; mCurBufferItem.reset(); return ret; } if (mPrevBufferItem.get()) { meson_vt_release_buffer( mTunnelFd, mTunnelId, mPrevBufferItem->mBufferFd, mPrevBufferItem->getReleaseFenceFd()); DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, mPrevBufferItem->toString().c_str()); } else { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] previous buffer is null, it is impossible", __func__, mDisplayId, mId); } mPrevBufferItem = mCurBufferItem; mCurBufferItem.reset(); return ret; } int32_t MesonLayer::releaseResourceLocked() { if (mPrevBufferItem.get()) { meson_vt_release_buffer( mTunnelFd, mTunnelId, mPrevBufferItem->mBufferFd, mPrevBufferItem->getReleaseFenceFd()); DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, mPrevBufferItem->toString().c_str()); mPrevBufferItem.reset(); } if (mCurBufferItem.get()) { meson_vt_release_buffer( mTunnelFd, mTunnelId, mCurBufferItem->mBufferFd, mCurBufferItem->getReleaseFenceFd()); mQueueItems.pop_front(); DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, mCurBufferItem->toString().c_str()); mCurBufferItem.reset(); } if (!isVtBuffer()) { int buffer_fd = 0; int fence_fd = 0; int64_t present_time = 0; struct vt_meta_data meta_data = { 0 }; while (meson_vt_acquire_buffer(mTunnelFd, mTunnelId, &buffer_fd, &fence_fd, &present_time, &meta_data) == 0) { struct uvm_fd_info info = { 0 }; std::shared_ptr item = std::make_shared(buffer_fd, present_time, info, meta_data); mQueueItems.push_back(std::move(item)); } onVideoCmd(); } // release the buffers in mQueueItems for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) { meson_vt_release_buffer( mTunnelFd, mTunnelId, (*it)->mBufferFd, (*it)->getReleaseFenceFd()); DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, (*it)->toString().c_str()); } mQueueItems.clear(); mCropItems.clear(); mUpdated = false; return 0; } int32_t MesonLayer::releaseResource() { std::lock_guard lock(mMutex); return releaseResourceLocked(); } int32_t MesonLayer::getAcquireFenceFd() { return (mAcquireFence.get() ? mAcquireFence->dup() : -1); } int32_t MesonLayer::setAcquireFence(int32_t fenceFd) { mAcquireFence.reset(new DrmFence(fenceFd)); return 0; } void MesonLayer::setBufferInfo(int fd, int32_t acquireFence) { if (mFd >= 0) close(mFd); mFd = fd; setAcquireFence(acquireFence); mUpdated = true; mDataUpdate = true; } int32_t MesonLayer::getBufferInfo( uint32_t & width, uint32_t & height, uint32_t & stride, uint32_t & format) { width = mBufferWidth; height = mBufferHeight; stride = mBufferStride; format = mBufferFormat; DISPLAY_LOGV("[%s] [%" PRIu64 "] width:%d height:%d stride:%d format:%d", __func__, mId, width, height, stride, format); return 0; } int32_t MesonLayer::getBufferInfo( uint32_t & width, uint32_t & height, uint32_t & stride, uint32_t & alignHeight, uint32_t & format) { width = mBufferWidth; height = mBufferHeight; stride = mBufferStride; format = mBufferFormat; alignHeight = mBufferAlignHeight; DISPLAY_LOGV("[%s] [%" PRIu64 "] width:%d height:%d stride:%d format:%d", __func__, mId, width, height, stride, format); return 0; } void MesonLayer::setBufferInfo( uint32_t width, uint32_t height, uint32_t stride, uint32_t format) { mBufferWidth = width; mBufferHeight = height; mBufferStride = stride; mBufferFormat = format; } /* videotunnel api */ int32_t MesonLayer::onVtFrameAvailable( std::vector> & items) { mMutex.lock(); bool haveSolidBuffer = false; /* if mNeedReleaseVtResource is true that means * this sideband layer receive blank frame. and layer's * type change from sideband to color. * don't return error. */ if (!isVtBuffer() && !mNeedReleaseVtResource) { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] not videotunnel type", __func__, mDisplayId, mId); mMutex.unlock(); return -EINVAL; } if (mTunnelId < 0) { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] mTunnelId %d error", __func__, mDisplayId, mId, mTunnelId); mMutex.unlock(); return -EINVAL; } for (auto it = items.begin(); it != items.end(); it ++) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] mTunnelId %d, buffer(%s)", __func__, mDisplayId, mId, mTunnelId, (*it)->toString().c_str()); mQueueItems.push_back(*it); mQueuedFrames++; if (!(*it)->mIsSolidBuffer) attachUvmBuffer((*it)->mBufferFd); else haveSolidBuffer = true; } if (mQueueItems.empty()) { mMutex.unlock(); return -EAGAIN; } mMutex.unlock(); if (mGameMode || haveSolidBuffer) mDisplayObserver->onFrameAvailable(); return 0; } int32_t MesonLayer::registerConsumer() { int32_t ret = -1; if (mTunnelId < 0) { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] tunnelId is less than 0, cannot register consumer", __func__, mDisplayId, mId); return ret; } if (mVtConsumer) return 0; mVtConsumer = std::make_shared (mTunnelId, mDisplayId, mId); if (!mContentListener) mContentListener = std::make_shared(this); ret = mVtConsumer->setVtContentListener(mContentListener); if (ret < 0) { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] set %d content listener failed ", __func__, mDisplayId, mId, mTunnelId); return ret; } ret = VtInstanceMgr::getInstance().connectInstanceLocked(mTunnelId, mVtConsumer); if (ret >= 0) DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] connect to instance %d seccussed", __func__, mDisplayId, mId, mTunnelId); else DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] connect to instance %d failed", __func__, mDisplayId, mId, mTunnelId); askVtRefresh(false); return ret; } /* need lock VtInstanceMgr.mInstanceMutex before */ int32_t MesonLayer::unregisterConsumer() { int32_t ret = -1; if (mTunnelId < 0) { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] tunnelId is less than 0, cannot unregister consumer", __func__, mDisplayId, mId); return ret; } if (!mVtConsumer) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] consumer %d not create", __func__, mDisplayId, mId, mTunnelId); return ret; } memset(&mVtSourceCrop, 0, sizeof(mVtSourceCrop)); memset(&mVtDisplayFrame, 0, sizeof(mVtDisplayFrame)); ret = VtInstanceMgr::getInstance().disconnectInstanceLocked(mTunnelId, mVtConsumer); mVtConsumer.reset(); mVtConsumer = nullptr; mTunnelId = -1; return ret; } void MesonLayer::askVtRefresh(bool flag) { VideoTunnelDev::getInstance().askRefresh(mTunnelId, flag); } /* layer will destroy or layer FbType changed to non-videotunnel */ int32_t MesonLayer::releaseVtResource() { int32_t ret = 0; VtInstanceMgr::getInstance().lockInstancesMutex(); std::lock_guard lock(mMutex); ret = releaseVtResourceLocked(); VtInstanceMgr::getInstance().unlockInstancesMutex(); return ret; } /* need hold VtInstanceMgr::mInstanceMutex and MesonLayer::mMutex before call */ int32_t MesonLayer::releaseVtResourceLocked(bool needDisconnect) { if (isVtBuffer() || mNeedReleaseVtResource) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "]", __func__, mDisplayId, mId); if (!mVtConsumer) return 0; if (needDisconnect && mNeedAskRefresh ) { askVtRefresh(true); mNeedAskRefresh = false; } if (mPrevBufferItem.get()) { onVtFrameDisplayed(mPrevBufferItem); mQueuedFrames--; DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) queuedFrames(%d)", __func__, mDisplayId, mId, mPrevBufferItem->toString().c_str(), mQueuedFrames); mPrevBufferItem.reset(); } if (mCurBufferItem.get()) { onVtFrameDisplayed(mCurBufferItem); mQueuedFrames--; DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) queuedFrames(%d)", __func__, mDisplayId, mId, mCurBufferItem->toString().c_str(), mQueuedFrames); mQueueItems.pop_front(); mCurBufferItem.reset(); } // release the buffers in mQueueItems for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) { onVtFrameDisplayed((*it)); mQueuedFrames--; DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) queuedFrames(%d)", __func__, mDisplayId, mId, (*it)->toString().c_str(), mQueuedFrames); } mQueueItems.clear(); mVtUpdate = false; if (mTunnelId >= 0 && needDisconnect) { DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] Hwc2Layer release disconnect(%d) queuedFrames(%d)", __func__, mDisplayId, mId, mTunnelId, mQueuedFrames); unregisterConsumer(); } mNeedReleaseVtResource = false; } return 0; } int32_t MesonLayer::onVtFrameDisplayed(std::shared_ptr & item) { int32_t ret = -1; if (!mVtConsumer) return ret; ret = mVtConsumer->onVtFrameDisplayed(item); if (ret) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) error" , __func__, mDisplayId, mId, item->toString().c_str()); } return ret; } bool MesonLayer::newGameBuffer() { std::lock_guard lock(mMutex); bool ret = false; if (isVtBuffer() && mGameMode) { if (!mCurReleaseFence.get() && !mPrevBufferItem.get()) { ret = false; } else { ret = (mQueueItems.size() > 0) ? true : false; } DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] return %d", __func__, mDisplayId, mId, ret); } /* not game mode */ return ret; } /* refresh vt layer */ void MesonLayer::vtRefresh() { mVtRefreshed = true; } void MesonLayer::setVtDisplayFrame(drm_rect_t & rect) { std::lock_guard lock(mMutex); mVtDisplayFrame.left = rect.left; mVtDisplayFrame.top = rect.top; mVtDisplayFrame.right = rect.right; mVtDisplayFrame.bottom = rect.bottom; mUpdated = true; vtRefresh(); } void MesonLayer::setVtSourceCrop(int32_t l, int32_t t, int32_t r, int32_t b) { if (mVtSourceCrop.left == l && mVtSourceCrop.top == t && mVtSourceCrop.right == r && mVtSourceCrop.bottom == b) { return; } mVtSourceCrop.left = l; mVtSourceCrop.top = t; mVtSourceCrop.right = r; mVtSourceCrop.bottom = b; mUpdated = true; vtRefresh(); } void MesonLayer::onVtVideoGameMode(int data) { if (data != 1 && data != 0) { DISPLAY_LOGW("[%s] [%d] [%" PRIu64 "] get an invalid param for gameMode", __func__, mDisplayId, mId); } else { mGameMode = (data == 1 ? true : false); DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] %s video game mode", __func__, mDisplayId, mId, mGameMode ? "enable" : "disable"); } } void MesonLayer::updateVtBuffer() { mMutex.lock(); bool haveSolidBuffer = false; bool isVideoTypeChange = false; int expiredItemCount = 0; int dropCount = 0; int64_t diffAdded = 0; int64_t prevTimestamp = 0; if (!isVtBuffer()) { mMutex.unlock(); return; } /* * getBufferFd might drop the some buffers at the head of the queue * if there is a buffer behind them which is timely to be presented. */ if (mQueueItems.empty()) { mMutex.unlock(); return; } if (mPrevBufferItem.get()) prevTimestamp = mPrevBufferItem->mTimeStamp; for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) { if ((*it)->mIsSolidBuffer) haveSolidBuffer = true; if ((*it)->mTimeStamp > 0 && shouldPresentNow((*it))) { // do not drop frame in game mode if (!mGameMode) expiredItemCount++; } } // drop expiredItemCount-1 buffers dropCount = expiredItemCount - 1; while (true) { auto bufItem = mQueueItems.front(); if (haveSolidBuffer) { if (bufItem->mIsSolidBuffer) break; } else if (dropCount <= 0) { break; } diffAdded = bufItem->mTimeStamp - prevTimestamp; prevTimestamp = bufItem->mTimeStamp; DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] vt buffer (%s) expired dropped " "expectedPresent time(%" PRId64 ") diffAdded(%" PRId64 ") queuedFrames(%d)", __func__, mDisplayId,mId, bufItem->toString().c_str(), mExpectedPresentTime, diffAdded, mQueuedFrames); collectUvmBuffer(bufItem); onVtFrameDisplayed(bufItem); mQueuedFrames--; mQueueItems.pop_front(); dropCount--; } // update current mVtBuffer mVtUpdate = true; mCurBufferItem = mQueueItems[0]; if (shouldPresentNow(mCurBufferItem)) { struct uvm_fd_info info = mCurBufferItem->mInfo; mTunnelIdUpdate = false; if (info.buf_width == 0 || info.buf_height == 0) { int width = mSourceCrop.right - mSourceCrop.left; int height = mSourceCrop.bottom - mSourceCrop.top; setBufferInfo(width, height, VIDEO_ALIGN(width), height, info.buf_format); setVtSourceCrop(mSourceCrop.left, mSourceCrop.top, mSourceCrop.right, mSourceCrop.bottom); } else { setBufferInfo(info.buf_width, info.buf_height, info.buf_stride, info.buf_height, info.buf_format); setVtSourceCrop(info.crop_left, info.crop_top, info.crop_right, info.crop_bottom); } } diffAdded = mCurBufferItem->mTimeStamp - prevTimestamp; DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] currentBuffer(%s) expectedPresentTime(%" PRId64 " us) diffAdded(%" PRId64 " us) shouldPresent:%d, queueFrameSize:%zu, isVideoTypeChange:%d", __func__, mDisplayId, mId, mCurBufferItem->toString().c_str(),mExpectedPresentTime, diffAdded, shouldPresentNow(mCurBufferItem), mQueueItems.size(), isVideoTypeChange); mMutex.unlock(); } void MesonLayer::setBufferInfo( uint32_t width, uint32_t height, uint32_t stride, uint32_t alignHeight, uint32_t format) { mBufferWidth = width; mBufferHeight = height; mBufferStride = stride; mBufferFormat = format; mBufferAlignHeight = alignHeight; DISPLAY_LOGD("%s[%d]: w: %d, h: %d, s: %d, f: %d", __func__, __LINE__, width, height, stride, format); } int32_t MesonLayer::setSourceCrop(drm_rect_t crop) { std::lock_guard lock(mMutex); mCropItems.push_back(crop); mSourceCrop = crop; mUpdated = true; return 0; } drm_rect_t MesonLayer::getSourceCrop() { std::lock_guard lock(mMutex); if (isVtBuffer()) return mSourceCrop; struct uvm_fd_info curInfo = { 0 }; struct uvm_fd_info prevInfo = { 0 }; struct uvm_fd_info info = { 0 }; if (mCurBufferItem.get()) curInfo = mCurBufferItem->mInfo; if (mPrevBufferItem.get()) prevInfo = mPrevBufferItem->mInfo; if (shouldPresentNow(mCurBufferItem)) { info = (mUpdated || mVtUpdate) ? curInfo : prevInfo; } else { info = prevInfo; } if (info.crop_bottom != 0 && info.crop_right != 0) { mSourceCrop.left = info.crop_left; mSourceCrop.top = info.crop_top; mSourceCrop.right = info.crop_right; mSourceCrop.bottom = info.crop_bottom; } return mSourceCrop; } int32_t MesonLayer::releaseVtBuffer() { std::lock_guard lock(mMutex); int32_t ret = 0; if (!isVtBuffer()) { DISPLAY_LOGD("layer:%" PRId64 " is not videotunnel layer", getUniqueId()); return -EINVAL; } detachUvmBuffer(); if (mVtRefreshed) { mVtRefreshed = false; if (!shouldPresentNow(mCurBufferItem) || (shouldPresentNow(mCurBufferItem) && !mVtUpdate)) { DISPLAY_LOGV("[%s] [%" PRIu64 "] mVtRefreshed", __func__, mId); /* set release fence */ if (mPrevBufferItem.get()) mPrevBufferItem->setReleaseFence(mCurReleaseFence); return ret; } } if (!mVtUpdate) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] vt buffer not update", __func__, mDisplayId, mId); return -EAGAIN; } if (!mCurBufferItem.get()) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer item is nullprt !!", __func__, mDisplayId, mId); return -EAGAIN; } if (shouldPresentNow(mCurBufferItem) == false) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer(%s) not present, timestamp not meet", __func__, mDisplayId, mId, mCurBufferItem->toString().c_str()); return -EAGAIN; } if (mQueueItems.empty()) { DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] Queued vtbuffer is empty!!", __func__, mDisplayId, mId); return -EAGAIN; } if (!mCurReleaseFence.get() && !mPrevBufferItem.get()) { /* TODO: this first buffer not send to display, need refresh */ DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] first buffer not display", __func__, mDisplayId, mId); return -EAGAIN; } // remove it from the queueItems mQueueItems.pop_front(); mCurBufferItem->setReleaseFence(mCurReleaseFence); mVtUpdate = false; setCurReleaseFence(-1); // First release vt buffer, save to the previous if (!mPrevBufferItem.get()) { mPrevBufferItem = mCurBufferItem; mCurBufferItem.reset(); return ret; } if (mPrevBufferItem.get()) { collectUvmBuffer(mPrevBufferItem); DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] releaseBuffer(%s), curBuffer(%s), queuedFrames(%d)", __func__, mDisplayId, mId,mPrevBufferItem->toString().c_str(), mCurBufferItem->toString().c_str(), mQueuedFrames); ret = onVtFrameDisplayed(mPrevBufferItem); } else { DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] previous buffer is null, it is impossible", __func__, mDisplayId, mId); } mQueuedFrames--; mPrevBufferItem = mCurBufferItem; mCurBufferItem.reset(); return ret; } bool MesonLayer::interruptVtProcess(bool change) { std::lock_guard lock(mMutex); if (!isVtBuffer()) return false; if (mVideoDisplayStatus != VT_VIDEO_STATUS_SHOW) { releaseVtResourceLocked(false); releaseUvmResourceLock(); if (change) mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW; return true; } if (getBufferFdLocked() < 0 && !mTunnelIdUpdate) return true; return false; } void MesonLayer::onVtVideoStatus(vt_video_status_t status) { std::lock_guard lock(mMutex); DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] status: %s", __func__, mDisplayId, mId, vtVideoStatusToString(status)); mVideoDisplayStatus = status; } /* ========================== Uvm Attach =================================== */ int32_t MesonLayer::uvmDetachEnable(bool enable) { std::lock_guard lock(mMutex); releaseUvmResourceLock(); if (mUvmDetach) return mUvmDetach->setEnable(enable); return 0; } int32_t MesonLayer::attachUvmBuffer(const int bufferFd) { if (!mUvmDetach) mUvmDetach = std::make_shared(mId); return mUvmDetach->attachUvmBuffer(bufferFd); } int32_t MesonLayer::attachUvmBuffer(const std::shared_ptr item){ if (!item->mIsSolidBuffer) return attachUvmBuffer(item->mBufferFd); return 0; } int32_t MesonLayer::detachUvmBuffer() { if (mUvmDetach) return mUvmDetach->detachUvmBuffer(); return 0; } int32_t MesonLayer::collectUvmBuffer(const int fd, const int fence) { if (mUvmDetach) { return mUvmDetach->collectUvmBuffer(fd, fence); } else { if (fd >= 0) close(fd); if (fence >= 0 ) close(fence); } return 0; } int32_t MesonLayer::collectUvmBuffer(const std::shared_ptr item) { if (!item) return 0; if (!item->mIsSolidBuffer) { int bufferFd = dup(item->mBufferFd); int fenceFd = item->getReleaseFenceFd(); collectUvmBuffer(bufferFd, fenceFd); } return 0; } int32_t MesonLayer::releaseUvmResourceLock() { if (mPreUvmBufferFd >= 0) close(mPreUvmBufferFd); mPreUvmBufferFd = -1; if (mUvmDetach) return mUvmDetach->releaseUvmResource(); return 0; }