1 /* 2 // Copyright(c)2014 IntelCorporation 3 // 4 // LicensedundertheApacheLicense,Version2.0(the"License"); 5 // youmaynotusethisfileexceptincompliancewiththeLicense. 6 // YoumayobtainacopyoftheLicenseat 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unlessrequiredbyapplicablelaworagreedtoinwriting,software 11 // distributedundertheLicenseisdistributedonan"ASIS"BASIS, 12 // WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. 13 // SeetheLicenseforthespecificlanguagegoverningpermissionsand 14 // limitationsundertheLicense. 15 */ 16 17 #include <common/utils/HwcTrace.h> 18 #include <ips/common/RotationBufferProvider.h> 19 20 namespace android { 21 namespace intel { 22 23 #define CHECK_VA_STATUS_RETURN(FUNC) \ 24 if (vaStatus != VA_STATUS_SUCCESS) {\ 25 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ 26 return false;\ 27 } 28 29 #define CHECK_VA_STATUS_BREAK(FUNC) \ 30 if (vaStatus != VA_STATUS_SUCCESS) {\ 31 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\ 32 break;\ 33 } 34 35 // With this display value, VA will hook VED driver insead of VSP driver for buffer rotation 36 #define DISPLAYVALUE 0x56454450 37 38 RotationBufferProvider::RotationBufferProvider(Wsbm* wsbm) 39 : mWsbm(wsbm), 40 mVaInitialized(false), 41 mVaDpy(0), 42 mVaCfg(0), 43 mVaCtx(0), 44 mVaBufFilter(0), 45 mSourceSurface(0), 46 mDisplay(DISPLAYVALUE), 47 mWidth(0), 48 mHeight(0), 49 mTransform(0), 50 mRotatedWidth(0), 51 mRotatedHeight(0), 52 mRotatedStride(0), 53 mTargetIndex(0), 54 mTTMWrappers(), 55 mBobDeinterlace(0) 56 { 57 for (int i = 0; i < MAX_SURFACE_NUM; i++) { 58 mKhandles[i] = 0; 59 mRotatedSurfaces[i] = 0; 60 mDrmBuf[i] = NULL; 61 } 62 } 63 64 RotationBufferProvider::~RotationBufferProvider() 65 { 66 } 67 68 uint32_t RotationBufferProvider::getMilliseconds() 69 { 70 struct timeval ptimeval; 71 gettimeofday(&ptimeval, NULL); 72 return (uint32_t)((ptimeval.tv_sec * 1000) + (ptimeval.tv_usec / 1000)); 73 } 74 75 bool RotationBufferProvider::initialize() 76 { 77 if (NULL == mWsbm) 78 return false; 79 mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT); 80 return true; 81 } 82 83 void RotationBufferProvider::deinitialize() 84 { 85 stopVA(); 86 reset(); 87 } 88 89 void RotationBufferProvider::reset() 90 { 91 if (mTTMWrappers.size()) { 92 invalidateCaches(); 93 } 94 } 95 96 void RotationBufferProvider::invalidateCaches() 97 { 98 void *buf; 99 100 for (size_t i = 0; i < mTTMWrappers.size(); i++) { 101 buf = mTTMWrappers.valueAt(i); 102 if (!mWsbm->destroyTTMBuffer(buf)) 103 WLOGTRACE("failed to free TTMBuffer"); 104 } 105 mTTMWrappers.clear(); 106 } 107 108 int RotationBufferProvider::transFromHalToVa(int transform) 109 { 110 if (transform == HAL_TRANSFORM_ROT_90) 111 return VA_ROTATION_90; 112 if (transform == HAL_TRANSFORM_ROT_180) 113 return VA_ROTATION_180; 114 if (transform == HAL_TRANSFORM_ROT_270) 115 return VA_ROTATION_270; 116 return 0; 117 } 118 119 int RotationBufferProvider::getStride(bool isTarget, int width) 120 { 121 int stride = 0; 122 if (width <= 512) 123 stride = 512; 124 else if (width <= 1024) 125 stride = 1024; 126 else if (width <= 1280) { 127 stride = 1280; 128 if (isTarget) 129 stride = 2048; 130 } else if (width <= 2048) 131 stride = 2048; 132 else if (width <= 4096) 133 stride = 4096; 134 else 135 stride = (width + 0x3f) & ~0x3f; 136 return stride; 137 } 138 139 uint32_t RotationBufferProvider::createWsbmBuffer(int width, int height, void **buf) 140 { 141 int size = width * height * 3 / 2; // YUV420 NV12 format 142 int allignment = 16 * 2048; // tiling row stride aligned 143 bool ret = mWsbm->allocateTTMBuffer(size, allignment, buf); 144 145 if (ret == false) { 146 ELOGTRACE("failed to allocate TTM buffer"); 147 return 0; 148 } 149 150 return mWsbm->getKBufHandle(*buf); 151 } 152 153 bool RotationBufferProvider::createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget) 154 { 155 VAStatus vaStatus; 156 VASurfaceAttributeTPI attribTpi; 157 VASurfaceAttributeTPI *vaSurfaceAttrib = &attribTpi; 158 int stride; 159 unsigned long buffers; 160 VASurfaceID *surface; 161 int width = 0, height = 0, bufferHeight = 0; 162 163 if (isTarget) { 164 if (transFromHalToVa(transform) == VA_ROTATION_180) { 165 width = payload->width; 166 height = payload->height; 167 } else { 168 width = payload->height; 169 height = payload->width; 170 } 171 mRotatedWidth = width; 172 mRotatedHeight = height; 173 bufferHeight = (height + 0x1f) & ~0x1f; 174 stride = getStride(isTarget, width); 175 } else { 176 width = payload->width; 177 height = payload->height; 178 bufferHeight = payload->height; 179 stride = payload->luma_stride; /* NV12 srouce buffer */ 180 } 181 182 if (!stride) { 183 ELOGTRACE("invalid stride value"); 184 return false; 185 } 186 187 // adjust source target for Bob deinterlace 188 if (!isTarget && mBobDeinterlace) { 189 height >>= 1; 190 bufferHeight >>= 1; 191 stride <<= 1; 192 } 193 194 vaSurfaceAttrib->count = 1; 195 vaSurfaceAttrib->width = width; 196 vaSurfaceAttrib->height = height; 197 vaSurfaceAttrib->pixel_format = payload->format; 198 vaSurfaceAttrib->type = VAExternalMemoryKernelDRMBufffer; 199 vaSurfaceAttrib->tiling = payload->tiling; 200 vaSurfaceAttrib->size = (stride * bufferHeight * 3) / 2; 201 vaSurfaceAttrib->luma_offset = 0; 202 vaSurfaceAttrib->chroma_v_offset = stride * bufferHeight; 203 vaSurfaceAttrib->luma_stride = vaSurfaceAttrib->chroma_u_stride 204 = vaSurfaceAttrib->chroma_v_stride 205 = stride; 206 vaSurfaceAttrib->chroma_u_offset = vaSurfaceAttrib->chroma_v_offset; 207 vaSurfaceAttrib->buffers = &buffers; 208 209 if (isTarget) { 210 int khandle = createWsbmBuffer(stride, bufferHeight, &mDrmBuf[mTargetIndex]); 211 if (khandle == 0) { 212 ELOGTRACE("failed to create buffer by wsbm"); 213 return false; 214 } 215 216 mKhandles[mTargetIndex] = khandle; 217 vaSurfaceAttrib->buffers[0] = khandle; 218 mRotatedStride = stride; 219 surface = &mRotatedSurfaces[mTargetIndex]; 220 } else { 221 vaSurfaceAttrib->buffers[0] = payload->khandle; 222 surface = &mSourceSurface; 223 /* set src surface width/height to video crop size */ 224 if (payload->crop_width && payload->crop_height) { 225 width = payload->crop_width; 226 height = (payload->crop_height >> mBobDeinterlace); 227 } else { 228 VLOGTRACE("Invalid cropping width or height"); 229 payload->crop_width = width; 230 payload->crop_height = height; 231 } 232 } 233 234 vaStatus = vaCreateSurfacesWithAttribute(mVaDpy, 235 width, 236 height, 237 VA_RT_FORMAT_YUV420, 238 1, 239 surface, 240 vaSurfaceAttrib); 241 if (vaStatus != VA_STATUS_SUCCESS) { 242 ELOGTRACE("vaCreateSurfacesWithAttribute returns %d", vaStatus); 243 ELOGTRACE("Attributes: target: %d, width: %d, height %d, bufferHeight %d, tiling %d", 244 isTarget, width, height, bufferHeight, payload->tiling); 245 *surface = 0; 246 return false; 247 } 248 249 return true; 250 } 251 252 bool RotationBufferProvider::startVA(VideoPayloadBuffer *payload, int transform) 253 { 254 bool ret = true; 255 VAStatus vaStatus; 256 VAEntrypoint *entryPoint; 257 VAConfigAttrib attribDummy; 258 int numEntryPoints; 259 bool supportVideoProcessing = false; 260 int majorVer = 0, minorVer = 0; 261 262 // VA will hold a copy of the param pointer, so local varialbe doesn't work 263 mVaDpy = vaGetDisplay(&mDisplay); 264 if (NULL == mVaDpy) { 265 ELOGTRACE("failed to get VADisplay"); 266 return false; 267 } 268 269 vaStatus = vaInitialize(mVaDpy, &majorVer, &minorVer); 270 CHECK_VA_STATUS_RETURN("vaInitialize"); 271 272 numEntryPoints = vaMaxNumEntrypoints(mVaDpy); 273 274 if (numEntryPoints <= 0) { 275 ELOGTRACE("numEntryPoints value is invalid"); 276 return false; 277 } 278 279 entryPoint = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * numEntryPoints); 280 if (NULL == entryPoint) { 281 ELOGTRACE("failed to malloc memory for entryPoint"); 282 return false; 283 } 284 285 vaStatus = vaQueryConfigEntrypoints(mVaDpy, 286 VAProfileNone, 287 entryPoint, 288 &numEntryPoints); 289 CHECK_VA_STATUS_RETURN("vaQueryConfigEntrypoints"); 290 291 for (int i = 0; i < numEntryPoints; i++) 292 if (entryPoint[i] == VAEntrypointVideoProc) 293 supportVideoProcessing = true; 294 295 free(entryPoint); 296 entryPoint = NULL; 297 298 if (!supportVideoProcessing) { 299 ELOGTRACE("VAEntrypointVideoProc is not supported"); 300 return false; 301 } 302 303 vaStatus = vaCreateConfig(mVaDpy, 304 VAProfileNone, 305 VAEntrypointVideoProc, 306 &attribDummy, 307 0, 308 &mVaCfg); 309 CHECK_VA_STATUS_RETURN("vaCreateConfig"); 310 311 // create first target surface 312 ret = createVaSurface(payload, transform, true); 313 if (ret == false) { 314 ELOGTRACE("failed to create target surface with attribute"); 315 return false; 316 } 317 318 vaStatus = vaCreateContext(mVaDpy, 319 mVaCfg, 320 payload->width, 321 payload->height, 322 0, 323 &mRotatedSurfaces[0], 324 1, 325 &mVaCtx); 326 CHECK_VA_STATUS_RETURN("vaCreateContext"); 327 328 VAProcFilterType filters[VAProcFilterCount]; 329 unsigned int numFilters = VAProcFilterCount; 330 vaStatus = vaQueryVideoProcFilters(mVaDpy, mVaCtx, filters, &numFilters); 331 CHECK_VA_STATUS_RETURN("vaQueryVideoProcFilters"); 332 333 bool supportVideoProcFilter = false; 334 for (unsigned int j = 0; j < numFilters; j++) 335 if (filters[j] == VAProcFilterNone) 336 supportVideoProcFilter = true; 337 338 if (!supportVideoProcFilter) { 339 ELOGTRACE("VAProcFilterNone is not supported"); 340 return false; 341 } 342 343 VAProcFilterParameterBuffer filter; 344 filter.type = VAProcFilterNone; 345 filter.value = 0; 346 347 vaStatus = vaCreateBuffer(mVaDpy, 348 mVaCtx, 349 VAProcFilterParameterBufferType, 350 sizeof(filter), 351 1, 352 &filter, 353 &mVaBufFilter); 354 CHECK_VA_STATUS_RETURN("vaCreateBuffer"); 355 356 VAProcPipelineCaps pipelineCaps; 357 unsigned int numCaps = 1; 358 vaStatus = vaQueryVideoProcPipelineCaps(mVaDpy, 359 mVaCtx, 360 &mVaBufFilter, 361 numCaps, 362 &pipelineCaps); 363 CHECK_VA_STATUS_RETURN("vaQueryVideoProcPipelineCaps"); 364 365 if (!(pipelineCaps.rotation_flags & (1 << transFromHalToVa(transform)))) { 366 ELOGTRACE("VA_ROTATION_xxx: 0x%08x is not supported by the filter", 367 transFromHalToVa(transform)); 368 return false; 369 } 370 371 mBobDeinterlace = payload->bob_deinterlace; 372 mVaInitialized = true; 373 374 return true; 375 } 376 377 bool RotationBufferProvider::setupRotationBuffer(VideoPayloadBuffer *payload, int transform) 378 { 379 #ifdef DEBUG_ROTATION_PERFROMANCE 380 uint32_t setup_Begin = getMilliseconds(); 381 #endif 382 VAStatus vaStatus; 383 int stride; 384 bool ret = false; 385 386 if (payload->format != VA_FOURCC_NV12 || payload->width == 0 || payload->height == 0) { 387 WLOGTRACE("payload data is not correct: format %#x, width %d, height %d", 388 payload->format, payload->width, payload->height); 389 return ret; 390 } 391 392 if (payload->width > 1280) { 393 payload->tiling = 1; 394 } 395 396 do { 397 if (isContextChanged(payload->width, payload->height, transform)) { 398 DLOGTRACE("VA is restarted as rotation context changes"); 399 400 if (mVaInitialized) { 401 stopVA(); // need to re-initialize VA for new rotation config 402 } 403 mTransform = transform; 404 mWidth = payload->width; 405 mHeight = payload->height; 406 } 407 408 if (!mVaInitialized) { 409 ret = startVA(payload, transform); 410 if (ret == false) { 411 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; 412 break; 413 } 414 } 415 416 // start to create next target surface 417 if (!mRotatedSurfaces[mTargetIndex]) { 418 ret = createVaSurface(payload, transform, true); 419 if (ret == false) { 420 ELOGTRACE("failed to create target surface with attribute"); 421 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; 422 break; 423 } 424 } 425 426 // create source surface 427 ret = createVaSurface(payload, transform, false); 428 if (ret == false) { 429 ELOGTRACE("failed to create source surface with attribute"); 430 vaStatus = VA_STATUS_ERROR_OPERATION_FAILED; 431 break; 432 } 433 434 #ifdef DEBUG_ROTATION_PERFROMANCE 435 uint32_t beginPicture = getMilliseconds(); 436 #endif 437 vaStatus = vaBeginPicture(mVaDpy, mVaCtx, mRotatedSurfaces[mTargetIndex]); 438 CHECK_VA_STATUS_BREAK("vaBeginPicture"); 439 440 VABufferID pipelineBuf; 441 void *p; 442 VAProcPipelineParameterBuffer *pipelineParam; 443 vaStatus = vaCreateBuffer(mVaDpy, 444 mVaCtx, 445 VAProcPipelineParameterBufferType, 446 sizeof(*pipelineParam), 447 1, 448 NULL, 449 &pipelineBuf); 450 CHECK_VA_STATUS_BREAK("vaCreateBuffer"); 451 452 vaStatus = vaMapBuffer(mVaDpy, pipelineBuf, &p); 453 CHECK_VA_STATUS_BREAK("vaMapBuffer"); 454 455 pipelineParam = (VAProcPipelineParameterBuffer*)p; 456 pipelineParam->surface = mSourceSurface; 457 pipelineParam->rotation_state = transFromHalToVa(transform); 458 pipelineParam->filters = &mVaBufFilter; 459 pipelineParam->num_filters = 1; 460 vaStatus = vaUnmapBuffer(mVaDpy, pipelineBuf); 461 CHECK_VA_STATUS_BREAK("vaUnmapBuffer"); 462 463 vaStatus = vaRenderPicture(mVaDpy, mVaCtx, &pipelineBuf, 1); 464 CHECK_VA_STATUS_BREAK("vaRenderPicture"); 465 466 vaStatus = vaEndPicture(mVaDpy, mVaCtx); 467 CHECK_VA_STATUS_BREAK("vaEndPicture"); 468 469 vaStatus = vaSyncSurface(mVaDpy, mRotatedSurfaces[mTargetIndex]); 470 CHECK_VA_STATUS_BREAK("vaSyncSurface"); 471 472 #ifdef DEBUG_ROTATION_PERFROMANCE 473 ILOGTRACE("time spent %dms from vaBeginPicture to vaSyncSurface", 474 getMilliseconds() - beginPicture); 475 #endif 476 477 // Populate payload fields so that overlayPlane can flip the buffer 478 payload->rotated_width = mRotatedStride; 479 payload->rotated_height = mRotatedHeight; 480 payload->rotated_buffer_handle = mKhandles[mTargetIndex]; 481 // setting client transform to 0 to force re-generating rotated buffer whenever needed. 482 payload->client_transform = 0; 483 mTargetIndex++; 484 if (mTargetIndex >= MAX_SURFACE_NUM) 485 mTargetIndex = 0; 486 487 } while (0); 488 489 #ifdef DEBUG_ROTATION_PERFROMANCE 490 ILOGTRACE("time spent %dms for setupRotationBuffer", 491 getMilliseconds() - setup_Begin); 492 #endif 493 494 if (mSourceSurface > 0) { 495 vaStatus = vaDestroySurfaces(mVaDpy, &mSourceSurface, 1); 496 if (vaStatus != VA_STATUS_SUCCESS) 497 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); 498 mSourceSurface = 0; 499 } 500 501 if (vaStatus != VA_STATUS_SUCCESS) { 502 stopVA(); 503 return false; // To not block HWC, just abort instead of retry 504 } 505 506 if (!payload->khandle) { 507 WLOGTRACE("khandle is reset by decoder, surface is invalid!"); 508 return false; 509 } 510 511 return true; 512 } 513 514 bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt) 515 { 516 int chroma_offset, size; 517 void *buf = NULL; 518 519 payload->width = payload->crop_width = w; 520 payload->height = payload->crop_height = h; 521 payload->format = VA_FOURCC_NV12; 522 payload->tiling = 1; 523 payload->luma_stride = stride; 524 payload->chroma_u_stride = stride; 525 payload->chroma_v_stride = stride; 526 payload->client_transform = 0; 527 528 chroma_offset = stride * h; 529 size = stride * h + stride * h / 2; 530 531 ssize_t index; 532 index = mTTMWrappers.indexOfKey((uint64_t)user_pt); 533 if (index < 0) { 534 VLOGTRACE("wrapped userPt as wsbm buffer"); 535 bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt); 536 if (ret == false) { 537 ELOGTRACE("failed to allocate TTM buffer"); 538 return ret; 539 } 540 541 if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) { 542 WLOGTRACE("mTTMWrappers is unexpectedly full. Invalidate caches"); 543 invalidateCaches(); 544 } 545 546 index = mTTMWrappers.add((uint64_t)user_pt, buf); 547 } else { 548 VLOGTRACE("got wsbmBuffer in saved caches"); 549 buf = mTTMWrappers.valueAt(index); 550 } 551 552 payload->khandle = mWsbm->getKBufHandle(buf); 553 return true; 554 } 555 556 void RotationBufferProvider::freeVaSurfaces() 557 { 558 bool ret; 559 VAStatus vaStatus; 560 561 for (int i = 0; i < MAX_SURFACE_NUM; i++) { 562 if (NULL != mDrmBuf[i]) { 563 ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]); 564 if (!ret) 565 WLOGTRACE("failed to free TTMBuffer"); 566 mDrmBuf[i] = NULL; 567 } 568 } 569 570 // remove wsbm buffer ref from VA 571 for (int j = 0; j < MAX_SURFACE_NUM; j++) { 572 if (0 != mRotatedSurfaces[j]) { 573 vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1); 574 if (vaStatus != VA_STATUS_SUCCESS) 575 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus); 576 } 577 mRotatedSurfaces[j] = 0; 578 } 579 } 580 581 void RotationBufferProvider::stopVA() 582 { 583 freeVaSurfaces(); 584 585 if (0 != mVaBufFilter) 586 vaDestroyBuffer(mVaDpy, mVaBufFilter); 587 if (0 != mVaCfg) 588 vaDestroyConfig(mVaDpy,mVaCfg); 589 if (0 != mVaCtx) 590 vaDestroyContext(mVaDpy, mVaCtx); 591 if (0 != mVaDpy) 592 vaTerminate(mVaDpy); 593 594 mVaInitialized = false; 595 596 // reset VA variable 597 mVaDpy = 0; 598 mVaCfg = 0; 599 mVaCtx = 0; 600 mVaBufFilter = 0; 601 mSourceSurface = 0; 602 603 mWidth = 0; 604 mHeight = 0; 605 mRotatedWidth = 0; 606 mRotatedHeight = 0; 607 mRotatedStride = 0; 608 mTargetIndex = 0; 609 } 610 611 bool RotationBufferProvider::isContextChanged(int width, int height, int transform) 612 { 613 // check rotation config 614 if (height == mHeight && 615 width == mWidth && 616 transform == mTransform) { 617 return false; 618 } 619 620 return true; 621 } 622 623 } // name space intel 624 } // name space android 625