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
30#include "vl/vl_defines.h"
31#include "vl/vl_video_buffer.h"
32#include "vl/vl_deint_filter.h"
33
34#include "va_private.h"
35
36static const VARectangle *
37vlVaRegionDefault(const VARectangle *region, struct pipe_video_buffer *buf,
38		  VARectangle *def)
39{
40   if (region)
41      return region;
42
43   def->x = 0;
44   def->y = 0;
45   def->width = buf->width;
46   def->height = buf->height;
47
48   return def;
49}
50
51static VAStatus
52vlVaPostProcCompositor(vlVaDriver *drv, vlVaContext *context,
53                       const VARectangle *src_region,
54                       const VARectangle *dst_region,
55                       struct pipe_video_buffer *src,
56                       struct pipe_video_buffer *dst,
57                       enum vl_compositor_deinterlace deinterlace)
58{
59   struct pipe_surface **surfaces;
60   struct u_rect src_rect;
61   struct u_rect dst_rect;
62
63   surfaces = dst->get_surfaces(dst);
64   if (!surfaces || !surfaces[0])
65      return VA_STATUS_ERROR_INVALID_SURFACE;
66
67   src_rect.x0 = src_region->x;
68   src_rect.y0 = src_region->y;
69   src_rect.x1 = src_region->x + src_region->width;
70   src_rect.y1 = src_region->y + src_region->height;
71
72   dst_rect.x0 = dst_region->x;
73   dst_rect.y0 = dst_region->y;
74   dst_rect.x1 = dst_region->x + dst_region->width;
75   dst_rect.y1 = dst_region->y + dst_region->height;
76
77   vl_compositor_clear_layers(&drv->cstate);
78   vl_compositor_set_buffer_layer(&drv->cstate, &drv->compositor, 0, src,
79				  &src_rect, NULL, deinterlace);
80   vl_compositor_set_layer_dst_area(&drv->cstate, 0, &dst_rect);
81   vl_compositor_render(&drv->cstate, &drv->compositor, surfaces[0], NULL, false);
82
83   drv->pipe->flush(drv->pipe, NULL, 0);
84   return VA_STATUS_SUCCESS;
85}
86
87static void vlVaGetBox(struct pipe_video_buffer *buf, unsigned idx,
88                       struct pipe_box *box, const VARectangle *region)
89{
90   unsigned plane = buf->interlaced ? idx / 2: idx;
91   unsigned x, y, width, height;
92
93   x = abs(region->x);
94   y = abs(region->y);
95   width = region->width;
96   height = region->height;
97
98   vl_video_buffer_adjust_size(&x, &y, plane, buf->chroma_format,
99                               buf->interlaced);
100   vl_video_buffer_adjust_size(&width, &height, plane, buf->chroma_format,
101                               buf->interlaced);
102
103   box->x = region->x < 0 ? -x : x;
104   box->y = region->y < 0 ? -y : y;
105   box->width = width;
106   box->height = height;
107}
108
109static VAStatus vlVaPostProcBlit(vlVaDriver *drv, vlVaContext *context,
110                                 const VARectangle *src_region,
111                                 const VARectangle *dst_region,
112                                 struct pipe_video_buffer *src,
113                                 struct pipe_video_buffer *dst,
114                                 enum vl_compositor_deinterlace deinterlace)
115{
116   struct pipe_surface **src_surfaces;
117   struct pipe_surface **dst_surfaces;
118   unsigned i;
119
120   if (src->interlaced != dst->interlaced)
121      return VA_STATUS_ERROR_INVALID_SURFACE;
122
123   src_surfaces = src->get_surfaces(src);
124   if (!src_surfaces || !src_surfaces[0])
125      return VA_STATUS_ERROR_INVALID_SURFACE;
126
127   dst_surfaces = dst->get_surfaces(dst);
128   if (!dst_surfaces || !dst_surfaces[0])
129      return VA_STATUS_ERROR_INVALID_SURFACE;
130
131   for (i = 0; i < VL_MAX_SURFACES; ++i) {
132      struct pipe_surface *from = src_surfaces[i];
133      struct pipe_blit_info blit;
134
135      if (src->interlaced) {
136         /* Not 100% accurate, but close enough */
137         switch (deinterlace) {
138         case VL_COMPOSITOR_BOB_TOP:
139            from = src_surfaces[i & ~1];
140            break;
141         case VL_COMPOSITOR_BOB_BOTTOM:
142            from = src_surfaces[(i & ~1) + 1];
143            break;
144         default:
145            break;
146         }
147      }
148
149      if (!from || !dst_surfaces[i])
150         continue;
151
152      memset(&blit, 0, sizeof(blit));
153      blit.src.resource = from->texture;
154      blit.src.format = from->format;
155      blit.src.level = 0;
156      blit.src.box.z = from->u.tex.first_layer;
157      blit.src.box.depth = 1;
158      vlVaGetBox(src, i, &blit.src.box, src_region);
159
160      blit.dst.resource = dst_surfaces[i]->texture;
161      blit.dst.format = dst_surfaces[i]->format;
162      blit.dst.level = 0;
163      blit.dst.box.z = dst_surfaces[i]->u.tex.first_layer;
164      blit.dst.box.depth = 1;
165      vlVaGetBox(dst, i, &blit.dst.box, dst_region);
166
167      blit.mask = PIPE_MASK_RGBA;
168      blit.filter = PIPE_TEX_MIPFILTER_LINEAR;
169
170      drv->pipe->blit(drv->pipe, &blit);
171   }
172
173   // TODO: figure out why this is necessary for DMA-buf sharing
174   drv->pipe->flush(drv->pipe, NULL, 0);
175
176   return VA_STATUS_SUCCESS;
177}
178
179static struct pipe_video_buffer *
180vlVaApplyDeint(vlVaDriver *drv, vlVaContext *context,
181               VAProcPipelineParameterBuffer *param,
182               struct pipe_video_buffer *current,
183               unsigned field)
184{
185   vlVaSurface *prevprev, *prev, *next;
186
187   if (param->num_forward_references < 1 ||
188       param->num_backward_references < 2)
189      return current;
190
191   prevprev = handle_table_get(drv->htab, param->backward_references[1]);
192   prev = handle_table_get(drv->htab, param->backward_references[0]);
193   next = handle_table_get(drv->htab, param->forward_references[0]);
194
195   if (!prevprev || !prev || !next)
196      return current;
197
198   if (context->deint && (context->deint->video_width != current->width ||
199       context->deint->video_height != current->height)) {
200      vl_deint_filter_cleanup(context->deint);
201      FREE(context->deint);
202      context->deint = NULL;
203   }
204
205   if (!context->deint) {
206      context->deint = MALLOC(sizeof(struct vl_deint_filter));
207      if (!vl_deint_filter_init(context->deint, drv->pipe, current->width,
208                                current->height, false, false)) {
209         FREE(context->deint);
210         context->deint = NULL;
211         return current;
212      }
213   }
214
215   if (!vl_deint_filter_check_buffers(context->deint, prevprev->buffer,
216                                      prev->buffer, current, next->buffer))
217      return current;
218
219   vl_deint_filter_render(context->deint, prevprev->buffer, prev->buffer,
220                          current, next->buffer, field);
221   return context->deint->video_buffer;
222}
223
224VAStatus
225vlVaHandleVAProcPipelineParameterBufferType(vlVaDriver *drv, vlVaContext *context, vlVaBuffer *buf)
226{
227   enum vl_compositor_deinterlace deinterlace = VL_COMPOSITOR_WEAVE;
228   VARectangle def_src_region, def_dst_region;
229   const VARectangle *src_region, *dst_region;
230   VAProcPipelineParameterBuffer *param;
231   struct pipe_video_buffer *src;
232   vlVaSurface *src_surface;
233   unsigned i;
234
235   if (!drv || !context)
236      return VA_STATUS_ERROR_INVALID_CONTEXT;
237
238   if (!buf || !buf->data)
239      return VA_STATUS_ERROR_INVALID_BUFFER;
240
241   if (!context->target)
242      return VA_STATUS_ERROR_INVALID_SURFACE;
243
244   param = buf->data;
245
246   src_surface = handle_table_get(drv->htab, param->surface);
247   if (!src_surface || !src_surface->buffer)
248      return VA_STATUS_ERROR_INVALID_SURFACE;
249
250   src = src_surface->buffer;
251
252   for (i = 0; i < param->num_filters; i++) {
253      vlVaBuffer *buf = handle_table_get(drv->htab, param->filters[i]);
254      VAProcFilterParameterBufferBase *filter;
255
256      if (!buf || buf->type != VAProcFilterParameterBufferType)
257         return VA_STATUS_ERROR_INVALID_BUFFER;
258
259      filter = buf->data;
260      switch (filter->type) {
261      case VAProcFilterDeinterlacing: {
262         VAProcFilterParameterBufferDeinterlacing *deint = buf->data;
263         switch (deint->algorithm) {
264         case VAProcDeinterlacingBob:
265            if (deint->flags & VA_DEINTERLACING_BOTTOM_FIELD)
266               deinterlace = VL_COMPOSITOR_BOB_BOTTOM;
267            else
268               deinterlace = VL_COMPOSITOR_BOB_TOP;
269            break;
270
271         case VAProcDeinterlacingWeave:
272            deinterlace = VL_COMPOSITOR_WEAVE;
273            break;
274
275         case VAProcDeinterlacingMotionAdaptive:
276            src = vlVaApplyDeint(drv, context, param, src,
277				 !!(deint->flags & VA_DEINTERLACING_BOTTOM_FIELD));
278            break;
279
280         default:
281            return VA_STATUS_ERROR_UNIMPLEMENTED;
282         }
283
284         break;
285      }
286
287      default:
288         return VA_STATUS_ERROR_UNIMPLEMENTED;
289      }
290   }
291
292   src_region = vlVaRegionDefault(param->surface_region, src_surface->buffer, &def_src_region);
293   dst_region = vlVaRegionDefault(param->output_region, context->target, &def_dst_region);
294
295   if (context->target->buffer_format != PIPE_FORMAT_NV12)
296      return vlVaPostProcCompositor(drv, context, src_region, dst_region,
297                                    src, context->target, deinterlace);
298   else
299      return vlVaPostProcBlit(drv, context, src_region, dst_region,
300                              src, context->target, deinterlace);
301}
302