• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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