1 /*
2 // Copyright (c) 2014 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16
17 #include <common/utils/HwcTrace.h>
18 #include <ips/common/RotationBufferProvider.h>
19 #include <system/graphics-base.h>
20
21 namespace android {
22 namespace intel {
23
24 #define CHECK_VA_STATUS_RETURN(FUNC) \
25 if (vaStatus != VA_STATUS_SUCCESS) {\
26 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\
27 return false;\
28 }
29
30 #define CHECK_VA_STATUS_BREAK(FUNC) \
31 if (vaStatus != VA_STATUS_SUCCESS) {\
32 ELOGTRACE(FUNC" failed. vaStatus = %#x", vaStatus);\
33 break;\
34 }
35
36 // With this display value, VA will hook VED driver insead of VSP driver for buffer rotation
37 #define DISPLAYVALUE 0x56454450
38
RotationBufferProvider(Wsbm * wsbm)39 RotationBufferProvider::RotationBufferProvider(Wsbm* wsbm)
40 : mWsbm(wsbm),
41 mVaInitialized(false),
42 mVaDpy(0),
43 mVaCfg(0),
44 mVaCtx(0),
45 mVaBufFilter(0),
46 mSourceSurface(0),
47 mDisplay(DISPLAYVALUE),
48 mWidth(0),
49 mHeight(0),
50 mTransform(0),
51 mRotatedWidth(0),
52 mRotatedHeight(0),
53 mRotatedStride(0),
54 mTargetIndex(0),
55 mTTMWrappers(),
56 mBobDeinterlace(0)
57 {
58 for (int i = 0; i < MAX_SURFACE_NUM; i++) {
59 mKhandles[i] = 0;
60 mRotatedSurfaces[i] = 0;
61 mDrmBuf[i] = NULL;
62 }
63 }
64
~RotationBufferProvider()65 RotationBufferProvider::~RotationBufferProvider()
66 {
67 }
68
getMilliseconds()69 uint32_t RotationBufferProvider::getMilliseconds()
70 {
71 struct timeval ptimeval;
72 gettimeofday(&ptimeval, NULL);
73 return (uint32_t)((ptimeval.tv_sec * 1000) + (ptimeval.tv_usec / 1000));
74 }
75
initialize()76 bool RotationBufferProvider::initialize()
77 {
78 if (NULL == mWsbm)
79 return false;
80 mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT);
81 return true;
82 }
83
deinitialize()84 void RotationBufferProvider::deinitialize()
85 {
86 stopVA();
87 reset();
88 }
89
reset()90 void RotationBufferProvider::reset()
91 {
92 if (mTTMWrappers.size()) {
93 invalidateCaches();
94 }
95 }
96
invalidateCaches()97 void RotationBufferProvider::invalidateCaches()
98 {
99 void *buf;
100
101 for (size_t i = 0; i < mTTMWrappers.size(); i++) {
102 buf = mTTMWrappers.valueAt(i);
103 if (!mWsbm->destroyTTMBuffer(buf))
104 WLOGTRACE("failed to free TTMBuffer");
105 }
106 mTTMWrappers.clear();
107 }
108
transFromHalToVa(int transform)109 int RotationBufferProvider::transFromHalToVa(int transform)
110 {
111 if (transform == HAL_TRANSFORM_ROT_90)
112 return VA_ROTATION_90;
113 if (transform == HAL_TRANSFORM_ROT_180)
114 return VA_ROTATION_180;
115 if (transform == HAL_TRANSFORM_ROT_270)
116 return VA_ROTATION_270;
117 return 0;
118 }
119
getStride(bool isTarget,int width)120 int RotationBufferProvider::getStride(bool isTarget, int width)
121 {
122 int stride = 0;
123 if (width <= 512)
124 stride = 512;
125 else if (width <= 1024)
126 stride = 1024;
127 else if (width <= 1280) {
128 stride = 1280;
129 if (isTarget)
130 stride = 2048;
131 } else if (width <= 2048)
132 stride = 2048;
133 else if (width <= 4096)
134 stride = 4096;
135 else
136 stride = (width + 0x3f) & ~0x3f;
137 return stride;
138 }
139
createWsbmBuffer(int width,int height,void ** buf)140 uint32_t RotationBufferProvider::createWsbmBuffer(int width, int height, void **buf)
141 {
142 int size = width * height * 3 / 2; // YUV420 NV12 format
143 int allignment = 16 * 2048; // tiling row stride aligned
144 bool ret = mWsbm->allocateTTMBuffer(size, allignment, buf);
145
146 if (ret == false) {
147 ELOGTRACE("failed to allocate TTM buffer");
148 return 0;
149 }
150
151 return mWsbm->getKBufHandle(*buf);
152 }
153
createVaSurface(VideoPayloadBuffer * payload,int transform,bool isTarget)154 bool RotationBufferProvider::createVaSurface(VideoPayloadBuffer *payload, int transform, bool isTarget)
155 {
156 VAStatus vaStatus;
157 VASurfaceAttributeTPI attribTpi;
158 VASurfaceAttributeTPI *vaSurfaceAttrib = &attribTpi;
159 int stride;
160 unsigned long buffers;
161 VASurfaceID *surface;
162 int width = 0, height = 0, bufferHeight = 0;
163
164 if (isTarget) {
165 if (transFromHalToVa(transform) == VA_ROTATION_180) {
166 width = payload->width;
167 height = payload->height;
168 } else {
169 width = payload->height;
170 height = payload->width;
171 }
172 mRotatedWidth = width;
173 mRotatedHeight = height;
174 bufferHeight = (height + 0x1f) & ~0x1f;
175 stride = getStride(isTarget, width);
176 } else {
177 width = payload->width;
178 height = payload->height;
179 bufferHeight = payload->height;
180 stride = payload->luma_stride; /* NV12 srouce buffer */
181 }
182
183 if (!stride) {
184 ELOGTRACE("invalid stride value");
185 return false;
186 }
187
188 // adjust source target for Bob deinterlace
189 if (!isTarget && mBobDeinterlace) {
190 height >>= 1;
191 bufferHeight >>= 1;
192 stride <<= 1;
193 }
194
195 vaSurfaceAttrib->count = 1;
196 vaSurfaceAttrib->width = width;
197 vaSurfaceAttrib->height = height;
198 vaSurfaceAttrib->pixel_format = payload->format;
199 vaSurfaceAttrib->type = VAExternalMemoryKernelDRMBufffer;
200 vaSurfaceAttrib->tiling = payload->tiling;
201 vaSurfaceAttrib->size = (stride * bufferHeight * 3) / 2;
202 vaSurfaceAttrib->luma_offset = 0;
203 vaSurfaceAttrib->chroma_v_offset = stride * bufferHeight;
204 vaSurfaceAttrib->luma_stride = vaSurfaceAttrib->chroma_u_stride
205 = vaSurfaceAttrib->chroma_v_stride
206 = stride;
207 vaSurfaceAttrib->chroma_u_offset = vaSurfaceAttrib->chroma_v_offset;
208 vaSurfaceAttrib->buffers = &buffers;
209
210 if (isTarget) {
211 int khandle = createWsbmBuffer(stride, bufferHeight, &mDrmBuf[mTargetIndex]);
212 if (khandle == 0) {
213 ELOGTRACE("failed to create buffer by wsbm");
214 return false;
215 }
216
217 mKhandles[mTargetIndex] = khandle;
218 vaSurfaceAttrib->buffers[0] = khandle;
219 mRotatedStride = stride;
220 surface = &mRotatedSurfaces[mTargetIndex];
221 } else {
222 vaSurfaceAttrib->buffers[0] = payload->khandle;
223 surface = &mSourceSurface;
224 /* set src surface width/height to video crop size */
225 if (payload->crop_width && payload->crop_height) {
226 width = payload->crop_width;
227 height = (payload->crop_height >> mBobDeinterlace);
228 } else {
229 VLOGTRACE("Invalid cropping width or height");
230 payload->crop_width = width;
231 payload->crop_height = height;
232 }
233 }
234
235 vaStatus = vaCreateSurfacesWithAttribute(mVaDpy,
236 width,
237 height,
238 VA_RT_FORMAT_YUV420,
239 1,
240 surface,
241 vaSurfaceAttrib);
242 if (vaStatus != VA_STATUS_SUCCESS) {
243 ELOGTRACE("vaCreateSurfacesWithAttribute returns %d", vaStatus);
244 ELOGTRACE("Attributes: target: %d, width: %d, height %d, bufferHeight %d, tiling %d",
245 isTarget, width, height, bufferHeight, payload->tiling);
246 *surface = 0;
247 return false;
248 }
249
250 return true;
251 }
252
startVA(VideoPayloadBuffer * payload,int transform)253 bool RotationBufferProvider::startVA(VideoPayloadBuffer *payload, int transform)
254 {
255 bool ret = true;
256 VAStatus vaStatus;
257 VAEntrypoint *entryPoint;
258 VAConfigAttrib attribDummy;
259 int numEntryPoints;
260 bool supportVideoProcessing = false;
261 int majorVer = 0, minorVer = 0;
262
263 // VA will hold a copy of the param pointer, so local varialbe doesn't work
264 mVaDpy = vaGetDisplay(&mDisplay);
265 if (NULL == mVaDpy) {
266 ELOGTRACE("failed to get VADisplay");
267 return false;
268 }
269
270 vaStatus = vaInitialize(mVaDpy, &majorVer, &minorVer);
271 CHECK_VA_STATUS_RETURN("vaInitialize");
272
273 numEntryPoints = vaMaxNumEntrypoints(mVaDpy);
274
275 if (numEntryPoints <= 0) {
276 ELOGTRACE("numEntryPoints value is invalid");
277 return false;
278 }
279
280 entryPoint = (VAEntrypoint*)malloc(sizeof(VAEntrypoint) * numEntryPoints);
281 if (NULL == entryPoint) {
282 ELOGTRACE("failed to malloc memory for entryPoint");
283 return false;
284 }
285
286 vaStatus = vaQueryConfigEntrypoints(mVaDpy,
287 VAProfileNone,
288 entryPoint,
289 &numEntryPoints);
290 CHECK_VA_STATUS_RETURN("vaQueryConfigEntrypoints");
291
292 for (int i = 0; i < numEntryPoints; i++)
293 if (entryPoint[i] == VAEntrypointVideoProc)
294 supportVideoProcessing = true;
295
296 free(entryPoint);
297 entryPoint = NULL;
298
299 if (!supportVideoProcessing) {
300 ELOGTRACE("VAEntrypointVideoProc is not supported");
301 return false;
302 }
303
304 vaStatus = vaCreateConfig(mVaDpy,
305 VAProfileNone,
306 VAEntrypointVideoProc,
307 &attribDummy,
308 0,
309 &mVaCfg);
310 CHECK_VA_STATUS_RETURN("vaCreateConfig");
311
312 // create first target surface
313 ret = createVaSurface(payload, transform, true);
314 if (ret == false) {
315 ELOGTRACE("failed to create target surface with attribute");
316 return false;
317 }
318
319 vaStatus = vaCreateContext(mVaDpy,
320 mVaCfg,
321 payload->width,
322 payload->height,
323 0,
324 &mRotatedSurfaces[0],
325 1,
326 &mVaCtx);
327 CHECK_VA_STATUS_RETURN("vaCreateContext");
328
329 VAProcFilterType filters[VAProcFilterCount];
330 unsigned int numFilters = VAProcFilterCount;
331 vaStatus = vaQueryVideoProcFilters(mVaDpy, mVaCtx, filters, &numFilters);
332 CHECK_VA_STATUS_RETURN("vaQueryVideoProcFilters");
333
334 bool supportVideoProcFilter = false;
335 for (unsigned int j = 0; j < numFilters; j++)
336 if (filters[j] == VAProcFilterNone)
337 supportVideoProcFilter = true;
338
339 if (!supportVideoProcFilter) {
340 ELOGTRACE("VAProcFilterNone is not supported");
341 return false;
342 }
343
344 VAProcFilterParameterBuffer filter;
345 filter.type = VAProcFilterNone;
346 filter.value = 0;
347
348 vaStatus = vaCreateBuffer(mVaDpy,
349 mVaCtx,
350 VAProcFilterParameterBufferType,
351 sizeof(filter),
352 1,
353 &filter,
354 &mVaBufFilter);
355 CHECK_VA_STATUS_RETURN("vaCreateBuffer");
356
357 VAProcPipelineCaps pipelineCaps;
358 unsigned int numCaps = 1;
359 vaStatus = vaQueryVideoProcPipelineCaps(mVaDpy,
360 mVaCtx,
361 &mVaBufFilter,
362 numCaps,
363 &pipelineCaps);
364 CHECK_VA_STATUS_RETURN("vaQueryVideoProcPipelineCaps");
365
366 if (!(pipelineCaps.rotation_flags & (1 << transFromHalToVa(transform)))) {
367 ELOGTRACE("VA_ROTATION_xxx: 0x%08x is not supported by the filter",
368 transFromHalToVa(transform));
369 return false;
370 }
371
372 mBobDeinterlace = payload->bob_deinterlace;
373 mVaInitialized = true;
374
375 return true;
376 }
377
setupRotationBuffer(VideoPayloadBuffer * payload,int transform)378 bool RotationBufferProvider::setupRotationBuffer(VideoPayloadBuffer *payload, int transform)
379 {
380 #ifdef DEBUG_ROTATION_PERFROMANCE
381 uint32_t setup_Begin = getMilliseconds();
382 #endif
383 VAStatus vaStatus;
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
prepareBufferInfo(int w,int h,int stride,VideoPayloadBuffer * payload,void * user_pt)514 bool RotationBufferProvider::prepareBufferInfo(int w, int h, int stride, VideoPayloadBuffer *payload, void *user_pt)
515 {
516 int 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 size = stride * h + stride * h / 2;
529
530 ssize_t index;
531 index = mTTMWrappers.indexOfKey((uint64_t)user_pt);
532 if (index < 0) {
533 VLOGTRACE("wrapped userPt as wsbm buffer");
534 bool ret = mWsbm->allocateTTMBufferUB(size, 0, &buf, user_pt);
535 if (ret == false) {
536 ELOGTRACE("failed to allocate TTM buffer");
537 return ret;
538 }
539
540 if (mTTMWrappers.size() >= TTM_WRAPPER_COUNT) {
541 WLOGTRACE("mTTMWrappers is unexpectedly full. Invalidate caches");
542 invalidateCaches();
543 }
544
545 index = mTTMWrappers.add((uint64_t)user_pt, buf);
546 } else {
547 VLOGTRACE("got wsbmBuffer in saved caches");
548 buf = mTTMWrappers.valueAt(index);
549 }
550
551 payload->khandle = mWsbm->getKBufHandle(buf);
552 return true;
553 }
554
freeVaSurfaces()555 void RotationBufferProvider::freeVaSurfaces()
556 {
557 bool ret;
558 VAStatus vaStatus;
559
560 for (int i = 0; i < MAX_SURFACE_NUM; i++) {
561 if (NULL != mDrmBuf[i]) {
562 ret = mWsbm->destroyTTMBuffer(mDrmBuf[i]);
563 if (!ret)
564 WLOGTRACE("failed to free TTMBuffer");
565 mDrmBuf[i] = NULL;
566 }
567 }
568
569 // remove wsbm buffer ref from VA
570 for (int j = 0; j < MAX_SURFACE_NUM; j++) {
571 if (0 != mRotatedSurfaces[j]) {
572 vaStatus = vaDestroySurfaces(mVaDpy, &mRotatedSurfaces[j], 1);
573 if (vaStatus != VA_STATUS_SUCCESS)
574 WLOGTRACE("vaDestroySurfaces failed, vaStatus = %d", vaStatus);
575 }
576 mRotatedSurfaces[j] = 0;
577 }
578 }
579
stopVA()580 void RotationBufferProvider::stopVA()
581 {
582 freeVaSurfaces();
583
584 if (0 != mVaBufFilter)
585 vaDestroyBuffer(mVaDpy, mVaBufFilter);
586 if (0 != mVaCfg)
587 vaDestroyConfig(mVaDpy,mVaCfg);
588 if (0 != mVaCtx)
589 vaDestroyContext(mVaDpy, mVaCtx);
590 if (0 != mVaDpy)
591 vaTerminate(mVaDpy);
592
593 mVaInitialized = false;
594
595 // reset VA variable
596 mVaDpy = 0;
597 mVaCfg = 0;
598 mVaCtx = 0;
599 mVaBufFilter = 0;
600 mSourceSurface = 0;
601
602 mWidth = 0;
603 mHeight = 0;
604 mRotatedWidth = 0;
605 mRotatedHeight = 0;
606 mRotatedStride = 0;
607 mTargetIndex = 0;
608 }
609
isContextChanged(int width,int height,int transform)610 bool RotationBufferProvider::isContextChanged(int width, int height, int transform)
611 {
612 // check rotation config
613 if (height == mHeight &&
614 width == mWidth &&
615 transform == mTransform) {
616 return false;
617 }
618
619 return true;
620 }
621
622 } // name space intel
623 } // name space android
624