/* * Copyright (c) 2024 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 #ifdef __ANDROID__ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include #include using android::base::StringAppendF; #endif #include #include #include #include #include #include #include "V2DComposer.h" V2DComposer::V2DComposer(int displayId, int devFd) : IComposer() { mEnable = false; mDisplayID = displayId; mDevFd = devFd; mInputFbs.clear(); mOutputFb.reset(); } V2DComposer::~V2DComposer() { mInputFbs.clear(); mOutputFb.reset(); if (mDevFd > 0) close(mDevFd); } bool V2DComposer::isFbsSupport( std::vector> & fbs __unused, std::vector> & overlayfbs __unused, int composeIdx __unused) { return true; } int32_t V2DComposer::prepare() { mOverlayFbs.clear(); return 0; } int32_t V2DComposer::addInputs( std::vector> & fbs, std::vector> & overlayfbs, int composeIdx) { mInputFbs = fbs; mOverlayFbs = overlayfbs; bool force_v2d = DebugHelper::getInstance().enableForceV2D(); if (mInputFbs.empty()) return 0; auto it = mInputFbs.begin(); if ((*it)->getTransform() > 0 || mInputFbs.size() > 1 || composeIdx > 0 || force_v2d) { if (mInputFbs.size() == 1) { int32_t transform = (*it)->getTransform(); if (transform == TRANSFORM_FLIP_H || transform == TRANSFORM_FLIP_V || transform == TRANSFORM_ROT_180) { MESON_LOGV("V2DComposer(%d) %s transform %d VideoDisplay pass-through for 180 rotation or flip", mDevFd, __func__, transform); mInputFbs.clear(); return 0; } } /* input fbs need do composition */ for (; it != mInputFbs.end(); it++) { (*it)->setCompositionType(getType()); } } else { mInputFbs.clear(); } return 0; } int32_t V2DComposer::getOverlyFbs( std::vector> & overlays) { overlays = mOverlayFbs; return 0; } int32_t V2DComposer::setOutput(std::shared_ptr & fb, int composeIdx) { UNUSED(composeIdx); UNUSED(fb); return 0; } int32_t V2DComposer::start(int composeIdx) { #ifdef __ANDROID__ ATRACE_CALL(); #endif UNUSED(composeIdx); bool update = false; int i = 0; int ret = -1; float alpha; drm_rect_t crtcRect; drm_rect_t srcRect; frame_info_t * vFrameInfo; DrmFramebufferBase* fb = nullptr; if (mInputFbs.empty()) { MESON_LOGV("V2DComposer(%d), input buffers is empty", mDevFd); return 0; } if (needSkipRotationInComposer()) { mOutputFb = mInputFbs[0]; MESON_LOGV("V2DComposer(%d) %s DPSS pass-through, skip V2D composer, layerId %" PRIu64, mDevFd, __func__, mOutputFb->getUniqueId()); return 0; } if (mInputFbs[0]->getCompositionType() != getType()) { /* mInputFbs have only one element and it not rotated */ mOutputFb = mInputFbs[0]; return 0; } if (!mEnable) { MESON_LOGE("V2DComposer(%d), composer not enable", mDevFd); return 0; } if (!mOutputFb.get()) { MESON_LOGE("V2DComposer(%d), not create output buffer", mDevFd); return 0; } if (mOutputFb->getFbType() != DRM_FB_VIDEO_COMPOSE_OUTPUT) { mOutputFb = std::make_shared(); mOutputFb->setFbType(DRM_FB_VIDEO_COMPOSE_OUTPUT); } for (i = 0; i < mInputFbs.size(); i++) { DrmFramebufferBase* fb = (DrmFramebufferBase*) (mInputFbs[i].get()); if (fb->isFbUpdated()) { update = true; break; } } if (!update) { MESON_LOGV("V2DComposer(%d), input buffers are not update", mDevFd); return 0; } memset(&mVideoFramesInfo, 0, sizeof(mVideoFramesInfo)); mVideoFramesInfo.out_buffer.fd = -1; mVideoFramesInfo.out_buffer.fence_fd = -1; for (i = 0; i < mInputFbs.size(); i++) { uint32_t width; uint32_t height; uint32_t format; uint32_t stride; uint32_t alignHeight; DrmFramebufferBase* fb = (DrmFramebufferBase*) (mInputFbs[i].get()); if (mVideoFramesInfo.frame_count >= MAX_LAYER_COUNT) { MESON_LOGW("V2DComposer(%d) start, More than Max composition video " "layers, skip %" PRIu64 ", !!!", mDevFd, fb->getUniqueId()); continue; } if (fb->getBufferFd() < 0) { MESON_LOGD("V2DComposer(%d) setframes layerId(%" PRIu64 ") type(%s) invalid fd", mDevFd, fb->getUniqueId(), drmFbTypeToString(fb->getFbType())); continue; } vFrameInfo = &mVideoFramesInfo.input_buffer[mVideoFramesInfo.frame_count]; mVideoFramesInfo.frame_count++; crtcRect = fb->getDisplayFrame(); srcRect = fb->getSourceCrop(); alpha = fb->getPlaneAlpha(); fb->getBufferInfo(width, height, stride, alignHeight, format); vFrameInfo->fd = fb->getBufferFd(); vFrameInfo->fence_fd = fb->getAcquireFenceFd(); vFrameInfo->buffer_w = width; vFrameInfo->buffer_h = height; vFrameInfo->align_w = stride; vFrameInfo->align_h = alignHeight; vFrameInfo->buffer_format = format; vFrameInfo->dst_x = crtcRect.left; vFrameInfo->dst_y = crtcRect.top; vFrameInfo->dst_w = crtcRect.right - crtcRect.left; vFrameInfo->dst_h = crtcRect.bottom - crtcRect.top; vFrameInfo->crop_x = srcRect.left; vFrameInfo->crop_y = srcRect.top; vFrameInfo->crop_w = srcRect.right - srcRect.left; vFrameInfo->crop_h = srcRect.bottom - srcRect.top; vFrameInfo->zorder = fb->getZorder(); vFrameInfo->transform = fb->getTransform(); vFrameInfo->alpha = int32_t(alpha * (MAX_ALPHA_VALUE - MIN_ALPHA_VALUE) + MIN_ALPHA_VALUE); MESON_LOGV("V2DComposer(%d) setframes layerId(%" PRIu64 ") z(%d) type(%s) fd(%d)" "inFence(%d) wxh(%dx%d) alignW(%d) f(%d) t(%d) axis(%s) crop(%s)", mDevFd, fb->getUniqueId(), vFrameInfo->zorder, drmFbTypeToString(fb->getFbType()), vFrameInfo->fd, vFrameInfo->fence_fd, width, height, stride, format, vFrameInfo->transform,crtcRect.toString().c_str(), crtcRect.toString().c_str()); #ifdef __ANDROID__ String8 layerInfo; layerInfo.appendFormat("layerInfo(%" PRIu64 " %s %u)", fb->getUniqueId(), drmFbTypeToString(fb->getFbType()), fb->getVideoTimestamp()); ATRACE_NAME(layerInfo.c_str()); #endif } if (mVideoFramesInfo.frame_count == 0) { MESON_LOGV("V2DComposer(%d) has no frame count", mDevFd); return -EINVAL; } ret = ioctl(mDevFd, V2D_IOCTL_SET_FRAMES, &mVideoFramesInfo); for (int i = 0; i < mVideoFramesInfo.frame_count; i++) { vFrameInfo = &mVideoFramesInfo.input_buffer[i]; if (vFrameInfo->fence_fd >= 0) { close(vFrameInfo->fence_fd); vFrameInfo->fence_fd = -1; } } if (ret != 0) { MESON_LOGE("v2d composer ioctl error, %s(%d), mDevFd = %d", strerror(errno), errno, mDevFd); return -1; } fillOutputFrameBuffer(mVideoFramesInfo.out_buffer); for (i = 0; i < mInputFbs.size(); i++) { fb = (DrmFramebufferBase*) (mInputFbs[i].get()); fb->setDiProcessorFd(-1); } return 0; } std::shared_ptr V2DComposer::getOutput(int composeIdx) { UNUSED(composeIdx); return mOutputFb; } int32_t V2DComposer::fillOutputFrameBuffer(frame_info_t & buffer) { #ifdef __ANDROID__ ATRACE_CALL(); #endif drm_rect_t crtcRect; drm_rect_t srcRect; crtcRect.left = buffer.dst_x; crtcRect.top = buffer.dst_y; crtcRect.right = buffer.dst_x + buffer.dst_w; crtcRect.bottom = buffer.dst_y + buffer.dst_h; srcRect.left = buffer.crop_x; srcRect.top = buffer.crop_y; srcRect.right = buffer.crop_x + buffer.crop_w; srcRect.bottom = buffer.crop_y + buffer.crop_h; mOutputFb->setCompositionType(getType()); uint64_t id = 0; auto iter = mInputFbs.begin(); if (iter != mInputFbs.end() && mInputFbs.size() == 1) { id = (*iter)->getUniqueId(); } mOutputFb->setUniqueId(id); mOutputFb->setBufferInfo(buffer.fd, buffer.fence_fd); mOutputFb->setBufferInfo(buffer.buffer_w, buffer.buffer_h, buffer.align_w, buffer.align_h, DRM_FORMAT_NV21); mOutputFb->setSourceCrop(srcRect); mOutputFb->setDisplayFrame(crtcRect); mOutputFb->setPlaneAlpha(1); MESON_LOGV("V2DComposer(%d) output buffer: fd:%d, fence_Fd:%d, " "w/h:(%dx%d), alignW:%d, crop(%s) displayFrame(%s)", mDevFd, buffer.fd, buffer.fence_fd, buffer.buffer_w, buffer.buffer_h, buffer.align_w, srcRect.toString().c_str(), crtcRect.toString().c_str()); return 0; } int32_t V2DComposer::setReleaseFence() { #ifdef __ANDROID__ ATRACE_CALL(); #endif int releaseFence = -1; int presentFence = -1; int32_t ret = 0; if (needSkipRotationInComposer()) return ret; if (!mOutputFb.get() || !mOutputFb->mUpdated || (mOutputFb->getBufferFd() < 0)) return ret; presentFence = mOutputFb->getPresentFence(); releaseFence = mOutputFb->getCurReleaseFence(); for (auto it = mInputFbs.begin(); it != mInputFbs.end(); it++) { int present_fence = (presentFence >= 0 ? dup(presentFence) : -1); int release_fence = (releaseFence >= 0 ? dup(releaseFence) : -1); MESON_LOGV("V2DComposer(%d) set presentFence %d and releseFence %d for layerId %" PRIu64, mDevFd, present_fence, release_fence, (*it)->getUniqueId()); (*it)->setPresentFence(present_fence); (*it)->setCurReleaseFence(release_fence); } release_info_t info = { .fd = mOutputFb->getBufferFd(), .fence_fd = releaseFence, }; if (ioctl(mDevFd, V2D_IOCTL_SET_FENCE, &info) != 0) { MESON_LOGE("V2DComposer(%d) error, set release fence error, %s(%d)", mDevFd, strerror(errno), errno); ret = errno; } else { MESON_LOGV("V2DComposer(%d) set release fence %d for fd %d to V2D", mDevFd, info.fence_fd, info.fd); } if (releaseFence >= 0) close(releaseFence); if (presentFence >= 0) close(presentFence); mOutputFb->mUpdated = false; return ret; } int32_t V2DComposer::enable(bool enable) { if (enable && needSkipRotationInComposer()) { if (mEnable) { int32_t val = 0; MESON_LOGD("V2DComposer(%d) disable", mDevFd); if (ioctl(mDevFd, V2D_IOCTL_SET_ENABLE, &val) != 0) { MESON_LOGE("%s V2DComposer(%d) error, %s(%d)", __func__, mDevFd, strerror(errno), errno); return -errno; } mEnable = false; } MESON_LOGV("V2DComposer(%d) %s DPSS pass-through, skip V2D hardware enable.", mDevFd, __func__); return 1; } if (mEnable != enable) { int32_t val = enable ? 1 : 0; MESON_LOGD("V2DComposer(%d) %s", mDevFd, enable ? "enable" : "disable"); if (ioctl(mDevFd, V2D_IOCTL_SET_ENABLE, &val) != 0) { MESON_LOGE("%s V2DComposer(%d) error, %s(%d)", enable ? "enable" : "disable", mDevFd, strerror(errno), errno); return -errno; } mEnable = enable; if (mEnable) { mOutputFb = std::make_shared(); mOutputFb->setFbType(DRM_FB_VIDEO_COMPOSE_OUTPUT); } } if (!enable) { /* Any disable() clears composer-held refs. Covers: * (1) DPSS pass-through: first enable(true) returns early and mEnable stays false; * later enable(false) does not enter mEnable!=enable, but must still clear * mInputFbs / mOutputFb (output may alias input). * (2) Normal path: true->false after ioctl also hits this block. */ mInputFbs.clear(); mOutputFb.reset(); } return 1; } bool V2DComposer::needSkipRotationInComposer() { if (mInputFbs.size() != 1) return false; if ((*mInputFbs.begin())->isRotated()) { if ((*mInputFbs.begin())->isDpssTransformFlagSet()) { (*mInputFbs.begin())->setCompositionType(MESON_COMPOSITION_PLANE_HWCVIDEO); return true; } else { (*mInputFbs.begin())->setCompositionType(getType()); } } return false; }