/* // Copyright (c) 2014 Intel 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 <HwcTrace.h> #include <common/RotationBufferProvider.h> #include <system/graphics-base.h> namespace android { namespace intel { #define CHECK_VA_STATUS_RETURN(FUNC) \ if (vaStatus != VA_STATUS_SUCCESS) {\ ETRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ return false;\ } #define CHECK_VA_STATUS_BREAK(FUNC) \ if (vaStatus != VA_STATUS_SUCCESS) {\ ETRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ break;\ } // With this display value, VA will hook VED driver insead of VSP driver for buffer rotation #define DISPLAYVALUE 0x56454450 RotationBufferProvider::RotationBufferProvider(Wsbm* wsbm) : mWsbm(wsbm), mVaInitialized(false), mVaDpy(0), mVaCfg(0), mVaCtx(0), mVaBufFilter(0), mSourceSurface(0), mDisplay(DISPLAYVALUE), mWidth(0), mHeight(0), mTransform(0), mRotatedWidth(0), mRotatedHeight(0), mRotatedStride(0), mTargetIndex(0), mTTMWrappers(), mBobDeinterlace(0) { for (int i = 0; i < MAX_SURFACE_NUM; i++) { mKhandles[i] = 0; mRotatedSurfaces[i] = 0; mDrmBuf[i] = NULL; } } RotationBufferProvider::~RotationBufferProvider() { } uint32_t RotationBufferProvider::getMilliseconds() { struct timeval ptimeval; gettimeofday(&ptimeval, NULL); return (uint32_t)((ptimeval.tv_sec * 1000) + (ptimeval.tv_usec / 1000)); } bool RotationBufferProvider::initialize() { if (NULL == mWsbm) return false; mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT); return true; } void RotationBufferProvider::deinitialize() { stopVA(); reset(); } void RotationBufferProvider::reset() { if (mTTMWrappers.size()) { invalidateCaches(); } } void RotationBufferProvider::invalidateCaches() { void *buf; for (size_t i = 0; i < mTTMWrappers.size(); i++) { buf = mTTMWrappers.valueAt(i); if (!mWsbm->destroyTTMBuffer(buf)) WTRACE("failed to free TTMBuffer"); } mTTMWrappers.clear(); } int RotationBufferProvider::transFromHalToVa(int transform) { if (transform == HAL_TRANSFORM_ROT_90) return VA_ROTATION_90; if (transform == HAL_TRANSFORM_ROT_180) return VA_ROTATION_180; if (transform == HAL_TRANSFORM_ROT_270) return VA_ROTATION_270; return 0; } int RotationBufferProvider::getStride(bool isTarget, int width) { int stride = 0; if (width <= 512) stride = 512; else if (width <= 1024) stride = 1024; else if (width <= 1280) { stride = 1280; if (isTarget) stride = 2048; } else if (width <= 2048) stride = 2048; else if (width <= 4096) stride = 4096; else stride = (width + 0x3f) & ~0x3f; return stride; } buffer_handle_t RotationBufferProvider::createWsbmBuffer(int width, int height, void **buf) { int size = width * height * 3 / 2; // YUV420 NV12 format int allignment = 16 * 2048; // tiling row stride aligned bool ret = mWsbm->allocateTTMBuffer(size, allignment, buf); if (ret == false) { ETRACE("failed to allocate TTM buffer"); return 0; } return (buffer_handle_t) mWsbm->getKBufHandle(*buf); } bool RotationBufferProvider::createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget) { VAStatus vaStatus; VASurfaceAttributeTPI attribTpi; VASurfaceAttributeTPI *vaSurfaceAttrib = &attribTpi; int stride; unsigned long buffers; VASurfaceID *surface; int width = 0, height = 0, bufferHeight = 0; if (isTarget) { if (transFromHalToVa(transform) == VA_ROTATION_180) { width = payload->width; height = payload->height; } else { width = payload->height; height = payload->width; } mRotatedWidth = width; mRotatedHeight = height; bufferHeight = (height + 0x1f) & ~0x1f; stride = getStride(isTarget, width); } else { width = payload->width; height = payload->height; bufferHeight = (payload->height + 0x1f) & ~0x1f; stride = payload->luma_stride; /* NV12 srouce buffer */ } if (!stride) { ETRACE("invalid stride value"); return false; } mBobDeinterlace = payload->bob_deinterlace; // adjust source target for Bob deinterlace if (!isTarget && mBobDeinterlace) { height >>= 1; bufferHeight >>= 1; stride <<= 1; } vaSurfaceAttrib->count = 1; vaSurfaceAttrib->width = width; vaSurfaceAttrib->height = height; vaSurfaceAttrib->pixel_format = payload->format; vaSurfaceAttrib->type = VAExternalMemoryKernelDRMBufffer; vaSurfaceAttrib->tiling = payload->tiling; vaSurfaceAttrib->size = (stride * bufferHeight * 3) / 2; vaSurfaceAttrib->luma_offset = 0; vaSurfaceAttrib->chroma_v_offset = stride * bufferHeight; vaSurfaceAttrib->luma_stride = vaSurfaceAttrib->chroma_u_stride = vaSurfaceAttrib->chroma_v_stride = stride; vaSurfaceAttrib->chroma_u_offset = vaSurfaceAttrib->chroma_v_offset; vaSurfaceAttrib->buffers = &buffers; if (isTarget) { buffer_handle_t khandle = createWsbmBuffer(stride, bufferHeight, &mDrmBuf[mTargetIndex]); if (khandle == 0) { ETRACE("failed to create buffer by wsbm"); return false; } mKhandles[mTargetIndex] = khandle; vaSurfaceAttrib->buffers[0] = (uintptr_t) khandle; mRotatedStride = stride; surface = &mRotatedSurfaces[mTargetIndex]; } else { vaSurfaceAttrib->buffers[0] = (uintptr_t) payload->khandle; surface = &mSourceSurface; /* set src surface width/height to video crop size */ if (payload->crop_width && payload->crop_height) { width = payload->crop_width; height = (payload->crop_height >> mBobDeinterlace); } else { VTRACE("Invalid cropping width or height"); payload->crop_width = width; payload->crop_height = height; } } vaStatus = vaCreateSurfacesWithAttribute(mVaDpy, width, height, VA_RT_FORMAT_YUV420, 1, surface, vaSurfaceAttrib); if (vaStatus != VA_STATUS_SUCCESS) { ETRACE("vaCreateSurfacesWithAttribute returns %d", vaStatus); ETRACE("Attributes: target: %d, width: %d, height %d, bufferHeight %d, tiling %d", isTarget, width, height, bufferHeight, payload->tiling); *surface = 0; return false; } return true; } bool RotationBufferProvider::startVA(VideoPayloadBuffer *payload, int transform) { bool ret = true; VAStatus vaStatus; VAEntrypoint *entryPoint; VAConfigAttrib attribDummy; int numEntryPoints; bool supportVideoProcessing = false; int majorVer = 0, minorVer = 0; // VA will hold a copy of the param pointer, so local varialbe doesn't work mVaDpy = vaGetDisplay(&mDisplay); if (NULL == mVaDpy) { ETRACE("failed to get VADisplay"); return false; } vaStatus = vaInitialize(mVaDpy, &majorVer, &minorVer); CHECK_VA_STATUS_RETURN("vaInitialize"); numEntryPoints = vaMaxNumEntrypoints(mVaDpy); if (numEntryPoints <= 0) { ETRACE("numEntryPoints value is invalid"); return false; } entryPoint = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * numEntryPoints); if (NULL == entryPoint) { ETRACE("failed to malloc memory for entryPoint"); return false; } vaStatus = vaQueryConfigEntrypoints(mVaDpy, VAProfileNone, entryPoint, &numEntryPoints); CHECK_VA_STATUS_RETURN("vaQueryConfigEntrypoints"); for (int i = 0; i < numEntryPoints; i++) if (entryPoint[i] == VAEntrypointVideoProc) supportVideoProcessing = true; free(entryPoint); entryPoint = NULL; if (!supportVideoProcessing) { ETRACE("VAEntrypointVideoProc is not supported"); return false; } vaStatus = vaCreateConfig(mVaDpy, VAProfileNone, VAEntrypointVideoProc, &attribDummy, 0, &mVaCfg); CHECK_VA_STATUS_RETURN("vaCreateConfig"); // create first target surface ret = createVaSurface(payload, transform, true); if (ret == false) { ETRACE("failed to create target surface with attribute"); return false; } vaStatus = vaCreateContext(mVaDpy, mVaCfg, payload->width, payload->height, 0, &mRotatedSurfaces[0], 1, &mVaCtx); CHECK_VA_STATUS_RETURN("vaCreateContext"); VAProcFilterType filters[VAProcFilterCount]; unsigned int numFilters = VAProcFilterCount; vaStatus = vaQueryVideoProcFilters(mVaDpy, mVaCtx, filters, &numFilters); CHECK_VA_STATUS_RETURN("vaQueryVideoProcFilters"); bool supportVideoProcFilter = false; for (unsigned int j = 0; j < numFilters; j++) if (filters[j] == VAProcFilterNone) supportVideoProcFilter = true; if (!supportVideoProcFilter) { ETRACE("VAProcFilterNone is not supported"); return false; } VAProcFilterParameterBuffer filter; filter.type = VAProcFilterNone; filter.value = 0; vaStatus = vaCreateBuffer(mVaDpy, mVaCtx, VAProcFilterParameterBufferType, sizeof(filter), 1, &filter, &mVaBufFilter); CHECK_VA_STATUS_RETURN("vaCreateBuffer"); VAProcPipelineCaps pipelineCaps; unsigned int numCaps = 1; vaStatus = vaQueryVideoProcPipelineCaps(mVaDpy, mVaCtx, &mVaBufFilter, numCaps, &pipelineCaps); CHECK_VA_STATUS_RETURN("vaQueryVideoProcPipelineCaps"); if (!(pipelineCaps.rotation_flags & (1 << transFromHalToVa(transform)))) { ETRACE("VA_ROTATION_xxx: 0x%08x is not supported by the filter", transFromHalToVa(transform)); return false; } mVaInitialized = true; return true; } bool RotationBufferProvider::setupRotationBuffer(VideoPayloadBuffer *payload, int transform) { #ifdef DEBUG_ROTATION_PERFROMANCE uint32_t setup_Begin = getMilliseconds(); #endif VAStatus vaStatus; int stride; bool ret = false; if (payload->format != VA_FOURCC_NV12 || payload->width == 0 || payload->height == 0) { WTRACE("payload data is not correct: format %#x, width %d, height %d", payload->format, payload->width, payload->height); return ret; } if (payload->width > 1280 && payload->width <= 2048) { payload->tiling = 1; } do { if (isContextChanged(payload->width, payload->height, transform)) { DTRACE("VA is restarted as rotation context changes"); if (mVaInitialized) { stopVA(); // need to re-initialize VA for new rotation config } mTransform = transform; mWidth = payload->width; mHeight = payload->height; } if (!mVaInitialized) { ret = startVA(payload, transform); if (ret == false) { vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; break; } } // start to create next target surface if (!mRotatedSurfaces[mTargetIndex]) { ret = createVaSurface(payload, transform, true); if (ret == false) { ETRACE("failed to create target surface with attribute"); vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; break; } } // create source surface ret = createVaSurface(payload, transform, false); if (ret == false) { ETRACE("failed to create source surface with attribute"); vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; break; } #ifdef DEBUG_ROTATION_PERFROMANCE uint32_t beginPicture = getMilliseconds(); #endif vaStatus = vaBeginPicture(mVaDpy, mVaCtx, mRotatedSurfaces[mTargetIndex]); CHECK_VA_STATUS_BREAK("vaBeginPicture"); VABufferID pipelineBuf; void *p; VAProcPipelineParameterBuffer *pipelineParam; vaStatus = vaCreateBuffer(mVaDpy, mVaCtx, VAProcPipelineParameterBufferType, sizeof(*pipelineParam), 1, NULL, &pipelineBuf); CHECK_VA_STATUS_BREAK("vaCreateBuffer"); vaStatus = vaMapBuffer(mVaDpy, pipelineBuf, &p); CHECK_VA_STATUS_BREAK("vaMapBuffer"); pipelineParam = (VAProcPipelineParameterBuffer*)p; pipelineParam->surface = mSourceSurface; pipelineParam->rotation_state = transFromHalToVa(transform); pipelineParam->filters = &mVaBufFilter; pipelineParam->num_filters = 1; pipelineParam->surface_region = NULL; pipelineParam->output_region = NULL; pipelineParam->num_forward_references = 0; pipelineParam->num_backward_references = 0; vaStatus = vaUnmapBuffer(mVaDpy, pipelineBuf); CHECK_VA_STATUS_BREAK("vaUnmapBuffer"); vaStatus = vaRenderPicture(mVaDpy, mVaCtx, &pipelineBuf, 1); CHECK_VA_STATUS_BREAK("vaRenderPicture"); vaStatus = vaEndPicture(mVaDpy, mVaCtx); CHECK_VA_STATUS_BREAK("vaEndPicture"); vaStatus = vaSyncSurface(mVaDpy, mRotatedSurfaces[mTargetIndex]); CHECK_VA_STATUS_BREAK("vaSyncSurface"); #ifdef DEBUG_ROTATION_PERFROMANCE ITRACE("time spent %dms from vaBeginPicture to vaSyncSurface", getMilliseconds() - beginPicture); #endif // Populate payload fields so that overlayPlane can flip the buffer payload->rotated_width = mRotatedStride; payload->rotated_height = mRotatedHeight; payload->rotated_buffer_handle = mKhandles[mTargetIndex]; // setting client transform to 0 to force re-generating rotated buffer whenever needed. payload->client_transform = 0; mTargetIndex++; if (mTargetIndex >= MAX_SURFACE_NUM) mTargetIndex = 0; } while (0); #ifdef DEBUG_ROTATION_PERFROMANCE ITRACE("time spent %dms for setupRotationBuffer", getMilliseconds() - setup_Begin); #endif if (mSourceSurface > 0) { vaStatus = vaDestroySurfaces(mVaDpy, &mSourceSurface, 1); if (vaStatus != VA_STATUS_SUCCESS) WTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); mSourceSurface = 0; } if (vaStatus != VA_STATUS_SUCCESS) { stopVA(); return false; // To not block HWC, just abort instead of retry } if (!payload->khandle) { WTRACE("khandle is reset by decoder, surface is invalid!"); return false; } return true; } bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt) { int chroma_offset, size; void *buf = NULL; payload->width = payload->crop_width = w; payload->height = payload->crop_height = h; payload->coded_width = ((w + 0xf) & ~0xf); payload->coded_height = ((h + 0xf) & ~0xf); payload->format = VA_FOURCC_NV12; payload->tiling = 1; payload->luma_stride = stride; payload->chroma_u_stride = stride; payload->chroma_v_stride = stride; payload->client_transform = 0; payload->bob_deinterlace = 0; chroma_offset = stride * h; size = stride * h + stride * h / 2; ssize_t index; index = mTTMWrappers.indexOfKey((uint64_t)user_pt); if (index < 0) { VTRACE("wrapped userPt as wsbm buffer"); bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt); if (ret == false) { ETRACE("failed to allocate TTM buffer"); return ret; } if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) { WTRACE("mTTMWrappers is unexpectedly full. Invalidate caches"); invalidateCaches(); } index = mTTMWrappers.add((uint64_t)user_pt, buf); } else { VTRACE("got wsbmBuffer in saved caches"); buf = mTTMWrappers.valueAt(index); } payload->khandle = (buffer_handle_t) mWsbm->getKBufHandle(buf); return true; } void RotationBufferProvider::freeVaSurfaces() { bool ret; VAStatus vaStatus; for (int i = 0; i < MAX_SURFACE_NUM; i++) { if (NULL != mDrmBuf[i]) { ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]); if (!ret) WTRACE("failed to free TTMBuffer"); mDrmBuf[i] = NULL; } } // remove wsbm buffer ref from VA for (int j = 0; j < MAX_SURFACE_NUM; j++) { if (0 != mRotatedSurfaces[j]) { vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1); if (vaStatus != VA_STATUS_SUCCESS) WTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); } mRotatedSurfaces[j] = 0; } } void RotationBufferProvider::stopVA() { freeVaSurfaces(); if (0 != mVaBufFilter) vaDestroyBuffer(mVaDpy, mVaBufFilter); if (0 != mVaCfg) vaDestroyConfig(mVaDpy,mVaCfg); if (0 != mVaCtx) vaDestroyContext(mVaDpy, mVaCtx); if (0 != mVaDpy) vaTerminate(mVaDpy); mVaInitialized = false; for (int i = 0; i < MAX_SURFACE_NUM; i++) { mKhandles[i] = 0; mRotatedSurfaces[i] = 0; mDrmBuf[i] = NULL; } // reset VA variable mVaDpy = 0; mVaCfg = 0; mVaCtx = 0; mVaBufFilter = 0; mSourceSurface = 0; mWidth = 0; mHeight = 0; mRotatedWidth = 0; mRotatedHeight = 0; mRotatedStride = 0; mTargetIndex = 0; mBobDeinterlace = 0; } bool RotationBufferProvider::isContextChanged(int width, int height, int transform) { // check rotation config if (height == mHeight && width == mWidth && transform == mTransform) { return false; } return true; } } // name space intel } // name space android