1/**************************************************************************
2 *
3 * Copyright 2015 Advanced Micro Devices, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "util/u_handle_table.h"
29#include "util/u_memory.h"
30#include "util/u_compute.h"
31
32#include "vl/vl_defines.h"
33#include "vl/vl_video_buffer.h"
34#include "vl/vl_deint_filter.h"
35#include "vl/vl_winsys.h"
36
37#include "va_private.h"
38
39static const VARectangle *
40vlVaRegionDefault(const VARectangle *region, vlVaSurface *surf,
41		  VARectangle *def)
42{
43   if (region)
44      return region;
45
46   def->x = 0;
47   def->y = 0;
48   def->width = surf->templat.width;
49   def->height = surf->templat.height;
50
51   return def;
52}
53
54static VAStatus
55vlVaPostProcCompositor(vlVaDriver *drv, vlVaContext *context,
56                       const VARectangle *src_region,
57                       const VARectangle *dst_region,
58                       struct pipe_video_buffer *src,
59                       struct pipe_video_buffer *dst,
60                       enum vl_compositor_deinterlace deinterlace)
61{
62   struct pipe_surface **surfaces;
63   struct u_rect src_rect;
64   struct u_rect dst_rect;
65
66   surfaces = dst->get_surfaces(dst);
67   if (!surfaces || !surfaces[0])
68      return VA_STATUS_ERROR_INVALID_SURFACE;
69
70   src_rect.x0 = src_region->x;
71   src_rect.y0 = src_region->y;
72   src_rect.x1 = src_region->x + src_region->width;
73   src_rect.y1 = src_region->y + src_region->height;
74
75   dst_rect.x0 = dst_region->x;
76   dst_rect.y0 = dst_region->y;
77   dst_rect.x1 = dst_region->x + dst_region->width;
78   dst_rect.y1 = dst_region->y + dst_region->height;
79
80   vl_compositor_clear_layers(&drv->cstate);
81   vl_compositor_set_buffer_layer(&drv->cstate, &drv->compositor, 0, src,
82				  &src_rect, NULL, deinterlace);
83   vl_compositor_set_layer_dst_area(&drv->cstate, 0, &dst_rect);
84   vl_compositor_render(&drv->cstate, &drv->compositor, surfaces[0], NULL, false);
85
86   drv->pipe->flush(drv->pipe, NULL, 0);
87   return VA_STATUS_SUCCESS;
88}
89
90static void vlVaGetBox(struct pipe_video_buffer *buf, unsigned idx,
91                       struct pipe_box *box, const VARectangle *region)
92{
93   unsigned plane = buf->interlaced ? idx / 2: idx;
94   unsigned x, y, width, height;
95
96   x = abs(region->x);
97   y = abs(region->y);
98   width = region->width;
99   height = region->height;
100
101   vl_video_buffer_adjust_size(&x, &y, plane,
102                               pipe_format_to_chroma_format(buf->buffer_format),
103                               buf->interlaced);
104   vl_video_buffer_adjust_size(&width, &height, plane,
105                               pipe_format_to_chroma_format(buf->buffer_format),
106                               buf->interlaced);
107
108   box->x = region->x < 0 ? -x : x;
109   box->y = region->y < 0 ? -y : y;
110   box->width = width;
111   box->height = height;
112}
113
114static VAStatus vlVaVidEngineBlit(vlVaDriver *drv, vlVaContext *context,
115                                 const VARectangle *src_region,
116                                 const VARectangle *dst_region,
117                                 struct pipe_video_buffer *src,
118                                 struct pipe_video_buffer *dst,
119                                 enum vl_compositor_deinterlace deinterlace,
120                                 VAProcPipelineParameterBuffer* param)
121{
122   if (deinterlace != VL_COMPOSITOR_NONE)
123      return VA_STATUS_ERROR_UNIMPLEMENTED;
124
125   if (src->buffer_format != PIPE_FORMAT_NV12 ||
126       dst->buffer_format != PIPE_FORMAT_NV12)
127      return VA_STATUS_ERROR_UNIMPLEMENTED;
128
129   struct u_rect src_rect;
130   struct u_rect dst_rect;
131
132   src_rect.x0 = src_region->x;
133   src_rect.y0 = src_region->y;
134   src_rect.x1 = src_region->x + src_region->width;
135   src_rect.y1 = src_region->y + src_region->height;
136
137   dst_rect.x0 = dst_region->x;
138   dst_rect.y0 = dst_region->y;
139   dst_rect.x1 = dst_region->x + dst_region->width;
140   dst_rect.y1 = dst_region->y + dst_region->height;
141
142   context->desc.vidproc.base.input_format = src->buffer_format;
143   context->desc.vidproc.base.output_format = dst->buffer_format;
144
145   context->desc.vidproc.src_region = src_rect;
146   context->desc.vidproc.dst_region = dst_rect;
147
148   if (param->rotation_state == VA_ROTATION_NONE)
149      context->desc.vidproc.orientation = PIPE_VIDEO_VPP_ORIENTATION_DEFAULT;
150   else if (param->rotation_state == VA_ROTATION_90)
151      context->desc.vidproc.orientation = PIPE_VIDEO_VPP_ROTATION_90;
152   else if (param->rotation_state == VA_ROTATION_180)
153      context->desc.vidproc.orientation = PIPE_VIDEO_VPP_ROTATION_180;
154   else if (param->rotation_state == VA_ROTATION_270)
155      context->desc.vidproc.orientation = PIPE_VIDEO_VPP_ROTATION_270;
156
157   if (param->mirror_state == VA_MIRROR_HORIZONTAL)
158      context->desc.vidproc.orientation |= PIPE_VIDEO_VPP_FLIP_HORIZONTAL;
159   if (param->mirror_state == VA_MIRROR_VERTICAL)
160      context->desc.vidproc.orientation |= PIPE_VIDEO_VPP_FLIP_VERTICAL;
161
162   memset(&context->desc.vidproc.blend, 0, sizeof(context->desc.vidproc.blend));
163   context->desc.vidproc.blend.mode = PIPE_VIDEO_VPP_BLEND_MODE_NONE;
164   if (param->blend_state != NULL) {
165      if (param->blend_state->flags & VA_BLEND_GLOBAL_ALPHA) {
166         context->desc.vidproc.blend.mode = PIPE_VIDEO_VPP_BLEND_MODE_GLOBAL_ALPHA;
167         context->desc.vidproc.blend.global_alpha = param->blend_state->global_alpha;
168      }
169   }
170
171   if (context->needs_begin_frame) {
172      context->decoder->begin_frame(context->decoder, dst,
173                                    &context->desc.base);
174      context->needs_begin_frame = false;
175   }
176   context->decoder->process_frame(context->decoder, src, &context->desc.vidproc);
177   context->vpp_needs_flush_on_endpic = true;
178
179   return VA_STATUS_SUCCESS;
180}
181
182static VAStatus vlVaPostProcBlit(vlVaDriver *drv, vlVaContext *context,
183                                 const VARectangle *src_region,
184                                 const VARectangle *dst_region,
185                                 struct pipe_video_buffer *src,
186                                 struct pipe_video_buffer *dst,
187                                 enum vl_compositor_deinterlace deinterlace)
188{
189   struct pipe_surface **src_surfaces;
190   struct pipe_surface **dst_surfaces;
191   struct u_rect src_rect;
192   struct u_rect dst_rect;
193   bool scale = false;
194   bool grab = false;
195   unsigned i;
196
197   if ((src->buffer_format == PIPE_FORMAT_B8G8R8X8_UNORM ||
198        src->buffer_format == PIPE_FORMAT_B8G8R8A8_UNORM ||
199        src->buffer_format == PIPE_FORMAT_R8G8B8X8_UNORM ||
200        src->buffer_format == PIPE_FORMAT_R8G8B8A8_UNORM) &&
201       !src->interlaced)
202      grab = true;
203
204   if ((src->width != dst->width || src->height != dst->height) &&
205       (src->interlaced && dst->interlaced))
206      scale = true;
207
208   src_surfaces = src->get_surfaces(src);
209   if (!src_surfaces || !src_surfaces[0])
210      return VA_STATUS_ERROR_INVALID_SURFACE;
211
212   if (scale || (src->interlaced != dst->interlaced && dst->interlaced)) {
213      vlVaSurface *surf;
214
215      surf = handle_table_get(drv->htab, context->target_id);
216      surf->templat.interlaced = false;
217      dst->destroy(dst);
218
219      if (vlVaHandleSurfaceAllocate(drv, surf, &surf->templat, NULL, 0) != VA_STATUS_SUCCESS)
220         return VA_STATUS_ERROR_ALLOCATION_FAILED;
221
222      dst = context->target = surf->buffer;
223   }
224
225   dst_surfaces = dst->get_surfaces(dst);
226   if (!dst_surfaces || !dst_surfaces[0])
227      return VA_STATUS_ERROR_INVALID_SURFACE;
228
229   src_rect.x0 = src_region->x;
230   src_rect.y0 = src_region->y;
231   src_rect.x1 = src_region->x + src_region->width;
232   src_rect.y1 = src_region->y + src_region->height;
233
234   dst_rect.x0 = dst_region->x;
235   dst_rect.y0 = dst_region->y;
236   dst_rect.x1 = dst_region->x + dst_region->width;
237   dst_rect.y1 = dst_region->y + dst_region->height;
238
239   if (grab) {
240      vl_compositor_convert_rgb_to_yuv(&drv->cstate, &drv->compositor, 0,
241                                       ((struct vl_video_buffer *)src)->resources[0],
242                                       dst, &src_rect, &dst_rect);
243
244      return VA_STATUS_SUCCESS;
245   }
246
247   if (src->interlaced != dst->interlaced) {
248      vl_compositor_yuv_deint_full(&drv->cstate, &drv->compositor,
249                                   src, dst, &src_rect, &dst_rect,
250                                   deinterlace);
251
252      return VA_STATUS_SUCCESS;
253   }
254
255   for (i = 0; i < VL_MAX_SURFACES; ++i) {
256      struct pipe_surface *from = src_surfaces[i];
257      struct pipe_blit_info blit;
258
259      if (src->interlaced) {
260         /* Not 100% accurate, but close enough */
261         switch (deinterlace) {
262         case VL_COMPOSITOR_BOB_TOP:
263            from = src_surfaces[i & ~1];
264            break;
265         case VL_COMPOSITOR_BOB_BOTTOM:
266            from = src_surfaces[(i & ~1) + 1];
267            break;
268         default:
269            break;
270         }
271      }
272
273      if (!from || !dst_surfaces[i])
274         continue;
275
276      memset(&blit, 0, sizeof(blit));
277      blit.src.resource = from->texture;
278      blit.src.format = from->format;
279      blit.src.level = 0;
280      blit.src.box.z = from->u.tex.first_layer;
281      blit.src.box.depth = 1;
282      vlVaGetBox(src, i, &blit.src.box, src_region);
283
284      blit.dst.resource = dst_surfaces[i]->texture;
285      blit.dst.format = dst_surfaces[i]->format;
286      blit.dst.level = 0;
287      blit.dst.box.z = dst_surfaces[i]->u.tex.first_layer;
288      blit.dst.box.depth = 1;
289      vlVaGetBox(dst, i, &blit.dst.box, dst_region);
290
291      blit.mask = PIPE_MASK_RGBA;
292      blit.filter = PIPE_TEX_MIPFILTER_LINEAR;
293
294      if (drv->pipe->screen->get_param(drv->pipe->screen,
295                                       PIPE_CAP_PREFER_COMPUTE_FOR_MULTIMEDIA))
296         util_compute_blit(drv->pipe, &blit, &context->blit_cs, !drv->compositor.deinterlace);
297      else
298         drv->pipe->blit(drv->pipe, &blit);
299   }
300
301   // TODO: figure out why this is necessary for DMA-buf sharing
302   drv->pipe->flush(drv->pipe, NULL, 0);
303
304   return VA_STATUS_SUCCESS;
305}
306
307static struct pipe_video_buffer *
308vlVaApplyDeint(vlVaDriver *drv, vlVaContext *context,
309               VAProcPipelineParameterBuffer *param,
310               struct pipe_video_buffer *current,
311               unsigned field)
312{
313   vlVaSurface *prevprev, *prev, *next;
314
315   if (param->num_forward_references < 2 ||
316       param->num_backward_references < 1)
317      return current;
318
319   prevprev = handle_table_get(drv->htab, param->forward_references[1]);
320   prev = handle_table_get(drv->htab, param->forward_references[0]);
321   next = handle_table_get(drv->htab, param->backward_references[0]);
322
323   if (!prevprev || !prev || !next)
324      return current;
325
326   if (context->deint && (context->deint->video_width != current->width ||
327       context->deint->video_height != current->height)) {
328      vl_deint_filter_cleanup(context->deint);
329      FREE(context->deint);
330      context->deint = NULL;
331   }
332
333   if (!context->deint) {
334      context->deint = MALLOC(sizeof(struct vl_deint_filter));
335      if (!vl_deint_filter_init(context->deint, drv->pipe, current->width,
336                                current->height, false, false)) {
337         FREE(context->deint);
338         context->deint = NULL;
339         return current;
340      }
341   }
342
343   if (!vl_deint_filter_check_buffers(context->deint, prevprev->buffer,
344                                      prev->buffer, current, next->buffer))
345      return current;
346
347   vl_deint_filter_render(context->deint, prevprev->buffer, prev->buffer,
348                          current, next->buffer, field);
349   return context->deint->video_buffer;
350}
351
352VAStatus
353vlVaHandleVAProcPipelineParameterBufferType(vlVaDriver *drv, vlVaContext *context, vlVaBuffer *buf)
354{
355   enum vl_compositor_deinterlace deinterlace = VL_COMPOSITOR_NONE;
356   VARectangle def_src_region, def_dst_region;
357   const VARectangle *src_region, *dst_region;
358   VAProcPipelineParameterBuffer *param;
359   struct pipe_video_buffer *src, *dst;
360   vlVaSurface *src_surface, *dst_surface;
361   unsigned i;
362   struct pipe_screen *pscreen;
363
364   if (!drv || !context)
365      return VA_STATUS_ERROR_INVALID_CONTEXT;
366
367   if (!buf || !buf->data)
368      return VA_STATUS_ERROR_INVALID_BUFFER;
369
370   if (!context->target)
371      return VA_STATUS_ERROR_INVALID_SURFACE;
372
373   param = buf->data;
374
375   src_surface = handle_table_get(drv->htab, param->surface);
376   dst_surface = handle_table_get(drv->htab, context->target_id);
377
378   pscreen = drv->vscreen->pscreen;
379
380   if (src_surface->buffer->buffer_format != dst_surface->buffer->buffer_format &&
381       !src_surface->buffer->interlaced &&
382       (dst_surface->buffer->buffer_format == PIPE_FORMAT_NV12 ||
383        dst_surface->buffer->buffer_format == PIPE_FORMAT_P010 ||
384        dst_surface->buffer->buffer_format == PIPE_FORMAT_P016) &&
385       pscreen->get_video_param(pscreen,
386                                PIPE_VIDEO_PROFILE_UNKNOWN,
387                                PIPE_VIDEO_ENTRYPOINT_ENCODE,
388                                PIPE_VIDEO_CAP_EFC_SUPPORTED)) {
389
390      // EFC will convert the buffer to a format the encoder accepts
391      dst_surface->encoder_format = dst_surface->buffer->buffer_format;
392
393      vlVaSurface *surf;
394
395      surf = handle_table_get(drv->htab, context->target_id);
396      surf->templat.interlaced = src_surface->templat.interlaced;
397      surf->templat.buffer_format = src_surface->templat.buffer_format;
398      surf->buffer->destroy(surf->buffer);
399
400      if (vlVaHandleSurfaceAllocate(drv, surf, &surf->templat, NULL, 0) != VA_STATUS_SUCCESS)
401         return VA_STATUS_ERROR_ALLOCATION_FAILED;
402
403      pipe_resource_reference(&(((struct vl_video_buffer *)(surf->buffer))->resources[0]), ((struct vl_video_buffer *)(src_surface->buffer))->resources[0]);
404      context->target = surf->buffer;
405
406      return VA_STATUS_SUCCESS;
407   }
408
409   if (!src_surface || !src_surface->buffer)
410      return VA_STATUS_ERROR_INVALID_SURFACE;
411
412   src = src_surface->buffer;
413   dst = dst_surface->buffer;
414
415   /* convert the destination buffer to progressive if we're deinterlacing
416      otherwise we might end up deinterlacing twice */
417   if (param->num_filters && dst->interlaced) {
418      vlVaSurface *surf;
419      surf = dst_surface;
420      surf->templat.interlaced = false;
421      dst->destroy(dst);
422
423      if (vlVaHandleSurfaceAllocate(drv, surf, &surf->templat, NULL, 0) != VA_STATUS_SUCCESS)
424         return VA_STATUS_ERROR_ALLOCATION_FAILED;
425
426      dst = context->target = surf->buffer;
427   }
428
429   for (i = 0; i < param->num_filters; i++) {
430      vlVaBuffer *buf = handle_table_get(drv->htab, param->filters[i]);
431      VAProcFilterParameterBufferBase *filter;
432
433      if (!buf || buf->type != VAProcFilterParameterBufferType)
434         return VA_STATUS_ERROR_INVALID_BUFFER;
435
436      filter = buf->data;
437      switch (filter->type) {
438      case VAProcFilterDeinterlacing: {
439         VAProcFilterParameterBufferDeinterlacing *deint = buf->data;
440         switch (deint->algorithm) {
441         case VAProcDeinterlacingBob:
442            if (deint->flags & VA_DEINTERLACING_BOTTOM_FIELD)
443               deinterlace = VL_COMPOSITOR_BOB_BOTTOM;
444            else
445               deinterlace = VL_COMPOSITOR_BOB_TOP;
446            break;
447
448         case VAProcDeinterlacingWeave:
449            deinterlace = VL_COMPOSITOR_WEAVE;
450            break;
451
452         case VAProcDeinterlacingMotionAdaptive:
453            src = vlVaApplyDeint(drv, context, param, src,
454				 !!(deint->flags & VA_DEINTERLACING_BOTTOM_FIELD));
455             deinterlace = VL_COMPOSITOR_MOTION_ADAPTIVE;
456            break;
457
458         default:
459            return VA_STATUS_ERROR_UNIMPLEMENTED;
460         }
461         drv->compositor.deinterlace = deinterlace;
462         break;
463      }
464
465      default:
466         return VA_STATUS_ERROR_UNIMPLEMENTED;
467      }
468   }
469
470   src_region = vlVaRegionDefault(param->surface_region, src_surface, &def_src_region);
471   dst_region = vlVaRegionDefault(param->output_region, dst_surface, &def_dst_region);
472
473   /* If the driver supports video engine post proc, attempt to do that
474    * if it fails, fallback to the other existing implementations below
475    */
476   if (pscreen->get_video_param(pscreen,
477                                PIPE_VIDEO_PROFILE_UNKNOWN,
478                                PIPE_VIDEO_ENTRYPOINT_PROCESSING,
479                                PIPE_VIDEO_CAP_SUPPORTED)) {
480      if (!context->decoder) {
481         context->decoder = drv->pipe->create_video_codec(drv->pipe, &context->templat);
482         if (!context->decoder)
483            return VA_STATUS_ERROR_ALLOCATION_FAILED;
484      }
485
486      /* Perform VPBlit, if fail, fallback to other implementations below */
487      if (VA_STATUS_SUCCESS == vlVaVidEngineBlit(drv, context, src_region, dst_region,
488                                                 src, context->target, deinterlace, param))
489         return VA_STATUS_SUCCESS;
490   }
491
492   /* Try other post proc implementations */
493   if (context->target->buffer_format != PIPE_FORMAT_NV12 &&
494       context->target->buffer_format != PIPE_FORMAT_P010 &&
495       context->target->buffer_format != PIPE_FORMAT_P016)
496      return vlVaPostProcCompositor(drv, context, src_region, dst_region,
497                                    src, context->target, deinterlace);
498   else
499      return vlVaPostProcBlit(drv, context, src_region, dst_region,
500                              src, context->target, deinterlace);
501}
502