• 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 
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 
RotationBufferProvider(Wsbm * wsbm)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 
~RotationBufferProvider()64 RotationBufferProvider::~RotationBufferProvider()
65 {
66 }
67 
getMilliseconds()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 
initialize()75 bool RotationBufferProvider::initialize()
76 {
77     if (NULL == mWsbm)
78         return false;
79     mTTMWrappers.setCapacity(TTM_WRAPPER_COUNT);
80     return true;
81 }
82 
deinitialize()83 void RotationBufferProvider::deinitialize()
84 {
85     stopVA();
86     reset();
87 }
88 
reset()89 void RotationBufferProvider::reset()
90 {
91     if (mTTMWrappers.size()) {
92         invalidateCaches();
93     }
94 }
95 
invalidateCaches()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 
transFromHalToVa(int transform)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 
getStride(bool isTarget,int width)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 
createWsbmBuffer(int width,int height,void ** buf)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 
createVaSurface(VideoPayloadBuffer * payload,int transform,bool isTarget)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 
startVA(VideoPayloadBuffer * payload,int transform)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 
setupRotationBuffer(VideoPayloadBuffer * payload,int transform)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 
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 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 
freeVaSurfaces()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 
stopVA()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 
isContextChanged(int width,int height,int transform)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