1/**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen & Orasanu Lucian.
4 * Copyright 2014 Advanced Micro Devices, Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 **************************************************************************/
28
29#include "pipe/p_screen.h"
30
31#include "util/u_memory.h"
32#include "util/u_handle_table.h"
33#include "util/u_surface.h"
34#include "util/u_video.h"
35
36#include "vl/vl_winsys.h"
37#include "vl/vl_video_buffer.h"
38
39#include "va_private.h"
40
41static const VAImageFormat formats[] =
42{
43   {VA_FOURCC('N','V','1','2')},
44   {VA_FOURCC('P','0','1','0')},
45   {VA_FOURCC('P','0','1','6')},
46   {VA_FOURCC('I','4','2','0')},
47   {VA_FOURCC('Y','V','1','2')},
48   {VA_FOURCC('Y','U','Y','V')},
49   {VA_FOURCC('U','Y','V','Y')},
50   {.fourcc = VA_FOURCC('B','G','R','A'), .byte_order = VA_LSB_FIRST, 32, 32,
51    0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
52   {.fourcc = VA_FOURCC('R','G','B','A'), .byte_order = VA_LSB_FIRST, 32, 32,
53    0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
54   {.fourcc = VA_FOURCC('B','G','R','X'), .byte_order = VA_LSB_FIRST, 32, 24,
55    0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
56   {.fourcc = VA_FOURCC('R','G','B','X'), .byte_order = VA_LSB_FIRST, 32, 24,
57    0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000}
58};
59
60static void
61vlVaVideoSurfaceSize(vlVaSurface *p_surf, int component,
62                     unsigned *width, unsigned *height)
63{
64   *width = p_surf->templat.width;
65   *height = p_surf->templat.height;
66
67   vl_video_buffer_adjust_size(width, height, component,
68                               p_surf->templat.chroma_format,
69                               p_surf->templat.interlaced);
70}
71
72VAStatus
73vlVaQueryImageFormats(VADriverContextP ctx, VAImageFormat *format_list, int *num_formats)
74{
75   struct pipe_screen *pscreen;
76   enum pipe_format format;
77   int i;
78
79   STATIC_ASSERT(ARRAY_SIZE(formats) == VL_VA_MAX_IMAGE_FORMATS);
80
81   if (!ctx)
82      return VA_STATUS_ERROR_INVALID_CONTEXT;
83
84   if (!(format_list && num_formats))
85      return VA_STATUS_ERROR_INVALID_PARAMETER;
86
87   *num_formats = 0;
88   pscreen = VL_VA_PSCREEN(ctx);
89   for (i = 0; i < ARRAY_SIZE(formats); ++i) {
90      format = VaFourccToPipeFormat(formats[i].fourcc);
91      if (pscreen->is_video_format_supported(pscreen, format,
92          PIPE_VIDEO_PROFILE_UNKNOWN,
93          PIPE_VIDEO_ENTRYPOINT_BITSTREAM))
94         format_list[(*num_formats)++] = formats[i];
95   }
96
97   return VA_STATUS_SUCCESS;
98}
99
100VAStatus
101vlVaCreateImage(VADriverContextP ctx, VAImageFormat *format, int width, int height, VAImage *image)
102{
103   VAStatus status;
104   vlVaDriver *drv;
105   VAImage *img;
106   int w, h;
107
108   if (!ctx)
109      return VA_STATUS_ERROR_INVALID_CONTEXT;
110
111   if (!(format && image && width && height))
112      return VA_STATUS_ERROR_INVALID_PARAMETER;
113
114   drv = VL_VA_DRIVER(ctx);
115
116   img = CALLOC(1, sizeof(VAImage));
117   if (!img)
118      return VA_STATUS_ERROR_ALLOCATION_FAILED;
119   mtx_lock(&drv->mutex);
120   img->image_id = handle_table_add(drv->htab, img);
121   mtx_unlock(&drv->mutex);
122
123   img->format = *format;
124   img->width = width;
125   img->height = height;
126   w = align(width, 2);
127   h = align(height, 2);
128
129   switch (format->fourcc) {
130   case VA_FOURCC('N','V','1','2'):
131      img->num_planes = 2;
132      img->pitches[0] = w;
133      img->offsets[0] = 0;
134      img->pitches[1] = w;
135      img->offsets[1] = w * h;
136      img->data_size  = w * h * 3 / 2;
137      break;
138
139   case VA_FOURCC('P','0','1','0'):
140   case VA_FOURCC('P','0','1','6'):
141      img->num_planes = 2;
142      img->pitches[0] = w * 2;
143      img->offsets[0] = 0;
144      img->pitches[1] = w * 2;
145      img->offsets[1] = w * h * 2;
146      img->data_size  = w * h * 3;
147      break;
148
149   case VA_FOURCC('I','4','2','0'):
150   case VA_FOURCC('Y','V','1','2'):
151      img->num_planes = 3;
152      img->pitches[0] = w;
153      img->offsets[0] = 0;
154      img->pitches[1] = w / 2;
155      img->offsets[1] = w * h;
156      img->pitches[2] = w / 2;
157      img->offsets[2] = w * h * 5 / 4;
158      img->data_size  = w * h * 3 / 2;
159      break;
160
161   case VA_FOURCC('U','Y','V','Y'):
162   case VA_FOURCC('Y','U','Y','V'):
163      img->num_planes = 1;
164      img->pitches[0] = w * 2;
165      img->offsets[0] = 0;
166      img->data_size  = w * h * 2;
167      break;
168
169   case VA_FOURCC('B','G','R','A'):
170   case VA_FOURCC('R','G','B','A'):
171   case VA_FOURCC('B','G','R','X'):
172   case VA_FOURCC('R','G','B','X'):
173      img->num_planes = 1;
174      img->pitches[0] = w * 4;
175      img->offsets[0] = 0;
176      img->data_size  = w * h * 4;
177      break;
178
179   default:
180      return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
181   }
182
183   status =  vlVaCreateBuffer(ctx, 0, VAImageBufferType,
184                           align(img->data_size, 16),
185                           1, NULL, &img->buf);
186   if (status != VA_STATUS_SUCCESS)
187      return status;
188   *image = *img;
189
190   return status;
191}
192
193VAStatus
194vlVaDeriveImage(VADriverContextP ctx, VASurfaceID surface, VAImage *image)
195{
196   vlVaDriver *drv;
197   vlVaSurface *surf;
198   vlVaBuffer *img_buf;
199   VAImage *img;
200   struct pipe_screen *screen;
201   struct pipe_surface **surfaces;
202   int w;
203   int h;
204   int i;
205   unsigned stride = 0;
206   unsigned offset = 0;
207
208   if (!ctx)
209      return VA_STATUS_ERROR_INVALID_CONTEXT;
210
211   drv = VL_VA_DRIVER(ctx);
212
213   if (!drv)
214      return VA_STATUS_ERROR_INVALID_CONTEXT;
215
216   screen = VL_VA_PSCREEN(ctx);
217
218   if (!screen)
219      return VA_STATUS_ERROR_INVALID_CONTEXT;
220
221   surf = handle_table_get(drv->htab, surface);
222
223   if (!surf || !surf->buffer)
224      return VA_STATUS_ERROR_INVALID_SURFACE;
225
226   if (surf->buffer->interlaced)
227     return VA_STATUS_ERROR_OPERATION_FAILED;
228
229   surfaces = surf->buffer->get_surfaces(surf->buffer);
230   if (!surfaces || !surfaces[0]->texture)
231      return VA_STATUS_ERROR_ALLOCATION_FAILED;
232
233   img = CALLOC(1, sizeof(VAImage));
234   if (!img)
235      return VA_STATUS_ERROR_ALLOCATION_FAILED;
236
237   img->format.fourcc = PipeFormatToVaFourcc(surf->buffer->buffer_format);
238   img->buf = VA_INVALID_ID;
239   img->width = surf->buffer->width;
240   img->height = surf->buffer->height;
241   img->num_palette_entries = 0;
242   img->entry_bytes = 0;
243   w = align(surf->buffer->width, 2);
244   h = align(surf->buffer->height, 2);
245
246   for (i = 0; i < ARRAY_SIZE(formats); ++i) {
247      if (img->format.fourcc == formats[i].fourcc) {
248         img->format = formats[i];
249         break;
250      }
251   }
252
253   mtx_lock(&drv->mutex);
254   if (screen->resource_get_info) {
255      screen->resource_get_info(screen, surfaces[0]->texture, &stride,
256                                &offset);
257      if (!stride)
258         offset = 0;
259   }
260
261   switch (img->format.fourcc) {
262   case VA_FOURCC('U','Y','V','Y'):
263   case VA_FOURCC('Y','U','Y','V'):
264      img->pitches[0] = stride > 0 ? stride : w * 2;
265      assert(img->pitches[0] >= (w * 2));
266      break;
267
268   case VA_FOURCC('B','G','R','A'):
269   case VA_FOURCC('R','G','B','A'):
270   case VA_FOURCC('B','G','R','X'):
271   case VA_FOURCC('R','G','B','X'):
272      img->pitches[0] = stride > 0 ? stride : w * 4;
273      assert(img->pitches[0] >= (w * 4));
274      break;
275
276   default:
277      /* VaDeriveImage only supports contiguous planes. But there is now a
278         more generic api vlVaExportSurfaceHandle. */
279      FREE(img);
280      mtx_unlock(&drv->mutex);
281      return VA_STATUS_ERROR_OPERATION_FAILED;
282   }
283
284   img->num_planes = 1;
285   img->offsets[0] = offset;
286   img->data_size  = img->pitches[0] * h;
287
288   img_buf = CALLOC(1, sizeof(vlVaBuffer));
289   if (!img_buf) {
290      FREE(img);
291      mtx_unlock(&drv->mutex);
292      return VA_STATUS_ERROR_ALLOCATION_FAILED;
293   }
294
295   img->image_id = handle_table_add(drv->htab, img);
296
297   img_buf->type = VAImageBufferType;
298   img_buf->size = img->data_size;
299   img_buf->num_elements = 1;
300
301   pipe_resource_reference(&img_buf->derived_surface.resource, surfaces[0]->texture);
302
303   img->buf = handle_table_add(VL_VA_DRIVER(ctx)->htab, img_buf);
304   mtx_unlock(&drv->mutex);
305
306   *image = *img;
307
308   return VA_STATUS_SUCCESS;
309}
310
311VAStatus
312vlVaDestroyImage(VADriverContextP ctx, VAImageID image)
313{
314   vlVaDriver *drv;
315   VAImage  *vaimage;
316   VAStatus status;
317
318   if (!ctx)
319      return VA_STATUS_ERROR_INVALID_CONTEXT;
320
321   drv = VL_VA_DRIVER(ctx);
322   mtx_lock(&drv->mutex);
323   vaimage = handle_table_get(drv->htab, image);
324   if (!vaimage) {
325      mtx_unlock(&drv->mutex);
326      return VA_STATUS_ERROR_INVALID_IMAGE;
327   }
328
329   handle_table_remove(VL_VA_DRIVER(ctx)->htab, image);
330   mtx_unlock(&drv->mutex);
331   status = vlVaDestroyBuffer(ctx, vaimage->buf);
332   FREE(vaimage);
333   return status;
334}
335
336VAStatus
337vlVaSetImagePalette(VADriverContextP ctx, VAImageID image, unsigned char *palette)
338{
339   if (!ctx)
340      return VA_STATUS_ERROR_INVALID_CONTEXT;
341
342   return VA_STATUS_ERROR_UNIMPLEMENTED;
343}
344
345VAStatus
346vlVaGetImage(VADriverContextP ctx, VASurfaceID surface, int x, int y,
347             unsigned int width, unsigned int height, VAImageID image)
348{
349   vlVaDriver *drv;
350   vlVaSurface *surf;
351   vlVaBuffer *img_buf;
352   VAImage *vaimage;
353   struct pipe_sampler_view **views;
354   enum pipe_format format;
355   bool convert = false;
356   void *data[3];
357   unsigned pitches[3], i, j;
358
359   if (!ctx)
360      return VA_STATUS_ERROR_INVALID_CONTEXT;
361
362   drv = VL_VA_DRIVER(ctx);
363
364   mtx_lock(&drv->mutex);
365   surf = handle_table_get(drv->htab, surface);
366   if (!surf || !surf->buffer) {
367      mtx_unlock(&drv->mutex);
368      return VA_STATUS_ERROR_INVALID_SURFACE;
369   }
370
371   vaimage = handle_table_get(drv->htab, image);
372   if (!vaimage) {
373      mtx_unlock(&drv->mutex);
374      return VA_STATUS_ERROR_INVALID_IMAGE;
375   }
376
377   if (x < 0 || y < 0) {
378      mtx_unlock(&drv->mutex);
379      return VA_STATUS_ERROR_INVALID_PARAMETER;
380   }
381
382   if (x + width > surf->templat.width ||
383       y + height > surf->templat.height) {
384      mtx_unlock(&drv->mutex);
385      return VA_STATUS_ERROR_INVALID_PARAMETER;
386   }
387
388   if (width > vaimage->width ||
389       height > vaimage->height) {
390      mtx_unlock(&drv->mutex);
391      return VA_STATUS_ERROR_INVALID_PARAMETER;
392   }
393
394   img_buf = handle_table_get(drv->htab, vaimage->buf);
395   if (!img_buf) {
396      mtx_unlock(&drv->mutex);
397      return VA_STATUS_ERROR_INVALID_BUFFER;
398   }
399
400   format = VaFourccToPipeFormat(vaimage->format.fourcc);
401   if (format == PIPE_FORMAT_NONE) {
402      mtx_unlock(&drv->mutex);
403      return VA_STATUS_ERROR_OPERATION_FAILED;
404   }
405
406   if (format != surf->buffer->buffer_format) {
407      /* support NV12 to YV12 and IYUV conversion now only */
408      if ((format == PIPE_FORMAT_YV12 &&
409          surf->buffer->buffer_format == PIPE_FORMAT_NV12) ||
410          (format == PIPE_FORMAT_IYUV &&
411          surf->buffer->buffer_format == PIPE_FORMAT_NV12))
412         convert = true;
413      else {
414         mtx_unlock(&drv->mutex);
415         return VA_STATUS_ERROR_OPERATION_FAILED;
416      }
417   }
418
419   views = surf->buffer->get_sampler_view_planes(surf->buffer);
420   if (!views) {
421      mtx_unlock(&drv->mutex);
422      return VA_STATUS_ERROR_OPERATION_FAILED;
423   }
424
425   for (i = 0; i < vaimage->num_planes; i++) {
426      data[i] = img_buf->data + vaimage->offsets[i];
427      pitches[i] = vaimage->pitches[i];
428   }
429   if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
430      void *tmp_d;
431      unsigned tmp_p;
432      tmp_d  = data[1];
433      data[1] = data[2];
434      data[2] = tmp_d;
435      tmp_p = pitches[1];
436      pitches[1] = pitches[2];
437      pitches[2] = tmp_p;
438   }
439
440   for (i = 0; i < vaimage->num_planes; i++) {
441      unsigned box_w = align(width, 2);
442      unsigned box_h = align(height, 2);
443      unsigned box_x = x & ~1;
444      unsigned box_y = y & ~1;
445      if (!views[i]) continue;
446      vl_video_buffer_adjust_size(&box_w, &box_h, i,
447                                  surf->templat.chroma_format,
448                                  surf->templat.interlaced);
449      vl_video_buffer_adjust_size(&box_x, &box_y, i,
450                                  surf->templat.chroma_format,
451                                  surf->templat.interlaced);
452      for (j = 0; j < views[i]->texture->array_size; ++j) {
453         struct pipe_box box = {box_x, box_y, j, box_w, box_h, 1};
454         struct pipe_transfer *transfer;
455         uint8_t *map;
456         map = drv->pipe->transfer_map(drv->pipe, views[i]->texture, 0,
457                  PIPE_TRANSFER_READ, &box, &transfer);
458         if (!map) {
459            mtx_unlock(&drv->mutex);
460            return VA_STATUS_ERROR_OPERATION_FAILED;
461         }
462
463         if (i == 1 && convert) {
464            u_copy_nv12_to_yv12(data, pitches, i, j,
465               transfer->stride, views[i]->texture->array_size,
466               map, box.width, box.height);
467         } else {
468            util_copy_rect(data[i] + pitches[i] * j,
469               views[i]->texture->format,
470               pitches[i] * views[i]->texture->array_size, 0, 0,
471               box.width, box.height, map, transfer->stride, 0, 0);
472         }
473         pipe_transfer_unmap(drv->pipe, transfer);
474      }
475   }
476   mtx_unlock(&drv->mutex);
477
478   return VA_STATUS_SUCCESS;
479}
480
481VAStatus
482vlVaPutImage(VADriverContextP ctx, VASurfaceID surface, VAImageID image,
483             int src_x, int src_y, unsigned int src_width, unsigned int src_height,
484             int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height)
485{
486   vlVaDriver *drv;
487   vlVaSurface *surf;
488   vlVaBuffer *img_buf;
489   VAImage *vaimage;
490   struct pipe_sampler_view **views;
491   enum pipe_format format;
492   void *data[3];
493   unsigned pitches[3], i, j;
494
495   if (!ctx)
496      return VA_STATUS_ERROR_INVALID_CONTEXT;
497
498   drv = VL_VA_DRIVER(ctx);
499   mtx_lock(&drv->mutex);
500
501   surf = handle_table_get(drv->htab, surface);
502   if (!surf || !surf->buffer) {
503      mtx_unlock(&drv->mutex);
504      return VA_STATUS_ERROR_INVALID_SURFACE;
505   }
506
507   vaimage = handle_table_get(drv->htab, image);
508   if (!vaimage) {
509      mtx_unlock(&drv->mutex);
510      return VA_STATUS_ERROR_INVALID_IMAGE;
511   }
512
513   img_buf = handle_table_get(drv->htab, vaimage->buf);
514   if (!img_buf) {
515      mtx_unlock(&drv->mutex);
516      return VA_STATUS_ERROR_INVALID_BUFFER;
517   }
518
519   if (img_buf->derived_surface.resource) {
520      /* Attempting to transfer derived image to surface */
521      mtx_unlock(&drv->mutex);
522      return VA_STATUS_ERROR_UNIMPLEMENTED;
523   }
524
525   format = VaFourccToPipeFormat(vaimage->format.fourcc);
526
527   if (format == PIPE_FORMAT_NONE) {
528      mtx_unlock(&drv->mutex);
529      return VA_STATUS_ERROR_OPERATION_FAILED;
530   }
531
532   if ((format != surf->buffer->buffer_format) &&
533         ((format != PIPE_FORMAT_YV12) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12)) &&
534         ((format != PIPE_FORMAT_IYUV) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12))) {
535      struct pipe_video_buffer *tmp_buf;
536
537      surf->templat.buffer_format = format;
538      if (format == PIPE_FORMAT_YUYV || format == PIPE_FORMAT_UYVY ||
539          format == PIPE_FORMAT_B8G8R8A8_UNORM || format == PIPE_FORMAT_B8G8R8X8_UNORM ||
540          format == PIPE_FORMAT_R8G8B8A8_UNORM || format == PIPE_FORMAT_R8G8B8X8_UNORM)
541         surf->templat.interlaced = false;
542      tmp_buf = drv->pipe->create_video_buffer(drv->pipe, &surf->templat);
543
544      if (!tmp_buf) {
545         mtx_unlock(&drv->mutex);
546         return VA_STATUS_ERROR_ALLOCATION_FAILED;
547      }
548
549      surf->buffer->destroy(surf->buffer);
550      surf->buffer = tmp_buf;
551   }
552
553   views = surf->buffer->get_sampler_view_planes(surf->buffer);
554   if (!views) {
555      mtx_unlock(&drv->mutex);
556      return VA_STATUS_ERROR_OPERATION_FAILED;
557   }
558
559   for (i = 0; i < vaimage->num_planes; i++) {
560      data[i] = img_buf->data + vaimage->offsets[i];
561      pitches[i] = vaimage->pitches[i];
562   }
563   if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
564      void *tmp_d;
565      unsigned tmp_p;
566      tmp_d  = data[1];
567      data[1] = data[2];
568      data[2] = tmp_d;
569      tmp_p = pitches[1];
570      pitches[1] = pitches[2];
571      pitches[2] = tmp_p;
572   }
573
574   for (i = 0; i < vaimage->num_planes; ++i) {
575      unsigned width, height;
576      struct pipe_resource *tex;
577
578      if (!views[i]) continue;
579      tex = views[i]->texture;
580
581      vlVaVideoSurfaceSize(surf, i, &width, &height);
582      for (j = 0; j < tex->array_size; ++j) {
583         struct pipe_box dst_box = {0, 0, j, width, height, 1};
584
585         if (((format == PIPE_FORMAT_YV12) || (format == PIPE_FORMAT_IYUV))
586             && (surf->buffer->buffer_format == PIPE_FORMAT_NV12)
587             && i == 1) {
588            struct pipe_transfer *transfer = NULL;
589            uint8_t *map = NULL;
590
591            map = drv->pipe->transfer_map(drv->pipe,
592                                          tex,
593                                          0,
594                                          PIPE_TRANSFER_WRITE |
595                                          PIPE_TRANSFER_DISCARD_RANGE,
596                                          &dst_box, &transfer);
597            if (map == NULL) {
598               mtx_unlock(&drv->mutex);
599               return VA_STATUS_ERROR_OPERATION_FAILED;
600            }
601
602            u_copy_nv12_from_yv12((const void * const*) data, pitches, i, j,
603                                  transfer->stride, tex->array_size,
604                                  map, dst_box.width, dst_box.height);
605            pipe_transfer_unmap(drv->pipe, transfer);
606         } else {
607            drv->pipe->texture_subdata(drv->pipe, tex, 0,
608                                       PIPE_TRANSFER_WRITE, &dst_box,
609                                       data[i] + pitches[i] * j,
610                                       pitches[i] * views[i]->texture->array_size, 0);
611         }
612      }
613   }
614   mtx_unlock(&drv->mutex);
615
616   return VA_STATUS_SUCCESS;
617}
618