422 lines
13 KiB
C++
422 lines
13 KiB
C++
/*
|
|
* 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 <utils/Trace.h>
|
|
#include <android-base/stringprintf.h>
|
|
using android::base::StringAppendF;
|
|
#endif
|
|
|
|
#include <drm_fourcc.h>
|
|
#include <sys/ioctl.h>
|
|
#include <BasicTypes.h>
|
|
#include <MesonLog.h>
|
|
#include <DrmFramebufferBase.h>
|
|
#include <DebugHelper.h>
|
|
#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<std::shared_ptr<DrmFramebufferBase>> & fbs __unused,
|
|
std::vector<std::shared_ptr<DrmFramebufferBase>> & overlayfbs __unused,
|
|
int composeIdx __unused) {
|
|
return true;
|
|
}
|
|
|
|
int32_t V2DComposer::prepare() {
|
|
mOverlayFbs.clear();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t V2DComposer::addInputs(
|
|
std::vector<std::shared_ptr<DrmFramebufferBase>> & fbs,
|
|
std::vector<std::shared_ptr<DrmFramebufferBase>> & 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<std::shared_ptr<DrmFramebufferBase>> & overlays) {
|
|
overlays = mOverlayFbs;
|
|
return 0;
|
|
}
|
|
|
|
int32_t V2DComposer::setOutput(std::shared_ptr<DrmFramebufferBase> & 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<DrmFramebufferBase>();
|
|
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<DrmFramebufferBase> 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<DrmFramebufferBase>();
|
|
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;
|
|
}
|