1/**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen.
4 * Copyright 2011 Christian König.
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 VMWARE AND/OR ITS SUPPLIERS 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 <assert.h>
30
31#include "pipe/p_state.h"
32
33#include "util/u_memory.h"
34#include "util/u_debug.h"
35#include "util/u_rect.h"
36#include "util/u_surface.h"
37#include "util/u_video.h"
38#include "vl/vl_defines.h"
39
40#include "frontend/drm_driver.h"
41
42#include "vdpau_private.h"
43
44enum getbits_conversion {
45   CONVERSION_NONE,
46   CONVERSION_NV12_TO_YV12,
47   CONVERSION_YV12_TO_NV12,
48   CONVERSION_SWAP_YUYV_UYVY,
49};
50
51/**
52 * Create a VdpVideoSurface.
53 */
54VdpStatus
55vlVdpVideoSurfaceCreate(VdpDevice device, VdpChromaType chroma_type,
56                        uint32_t width, uint32_t height,
57                        VdpVideoSurface *surface)
58{
59   struct pipe_context *pipe;
60   vlVdpSurface *p_surf;
61   VdpStatus ret;
62
63   if (!(width && height)) {
64      ret = VDP_STATUS_INVALID_SIZE;
65      goto inv_size;
66   }
67
68   p_surf = CALLOC(1, sizeof(vlVdpSurface));
69   if (!p_surf) {
70      ret = VDP_STATUS_RESOURCES;
71      goto no_res;
72   }
73
74   vlVdpDevice *dev = vlGetDataHTAB(device);
75   if (!dev) {
76      ret = VDP_STATUS_INVALID_HANDLE;
77      goto inv_device;
78   }
79
80   DeviceReference(&p_surf->device, dev);
81   pipe = dev->context;
82
83   mtx_lock(&dev->mutex);
84   memset(&p_surf->templat, 0, sizeof(p_surf->templat));
85   /* TODO: buffer_format should be selected to match chroma_type */
86   p_surf->templat.buffer_format = pipe->screen->get_video_param
87   (
88      pipe->screen,
89      PIPE_VIDEO_PROFILE_UNKNOWN,
90      PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
91      PIPE_VIDEO_CAP_PREFERED_FORMAT
92   );
93   p_surf->templat.width = width;
94   p_surf->templat.height = height;
95   p_surf->templat.interlaced = pipe->screen->get_video_param
96   (
97      pipe->screen,
98      PIPE_VIDEO_PROFILE_UNKNOWN,
99      PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
100      PIPE_VIDEO_CAP_PREFERS_INTERLACED
101   );
102   if (p_surf->templat.buffer_format != PIPE_FORMAT_NONE)
103      p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
104
105   /* do not mandate early allocation of a video buffer */
106   vlVdpVideoSurfaceClear(p_surf);
107   mtx_unlock(&dev->mutex);
108
109   *surface = vlAddDataHTAB(p_surf);
110   if (*surface == 0) {
111      ret = VDP_STATUS_ERROR;
112      goto no_handle;
113   }
114
115   return VDP_STATUS_OK;
116
117no_handle:
118   p_surf->video_buffer->destroy(p_surf->video_buffer);
119
120inv_device:
121   DeviceReference(&p_surf->device, NULL);
122   FREE(p_surf);
123
124no_res:
125inv_size:
126   return ret;
127}
128
129/**
130 * Destroy a VdpVideoSurface.
131 */
132VdpStatus
133vlVdpVideoSurfaceDestroy(VdpVideoSurface surface)
134{
135   vlVdpSurface *p_surf;
136
137   p_surf = (vlVdpSurface *)vlGetDataHTAB((vlHandle)surface);
138   if (!p_surf)
139      return VDP_STATUS_INVALID_HANDLE;
140
141   mtx_lock(&p_surf->device->mutex);
142   if (p_surf->video_buffer)
143      p_surf->video_buffer->destroy(p_surf->video_buffer);
144   mtx_unlock(&p_surf->device->mutex);
145
146   vlRemoveDataHTAB(surface);
147   DeviceReference(&p_surf->device, NULL);
148   FREE(p_surf);
149
150   return VDP_STATUS_OK;
151}
152
153/**
154 * Retrieve the parameters used to create a VdpVideoSurface.
155 */
156VdpStatus
157vlVdpVideoSurfaceGetParameters(VdpVideoSurface surface,
158                               VdpChromaType *chroma_type,
159                               uint32_t *width, uint32_t *height)
160{
161   if (!(width && height && chroma_type))
162      return VDP_STATUS_INVALID_POINTER;
163
164   vlVdpSurface *p_surf = vlGetDataHTAB(surface);
165   if (!p_surf)
166      return VDP_STATUS_INVALID_HANDLE;
167
168   if (p_surf->video_buffer) {
169      *width = p_surf->video_buffer->width;
170      *height = p_surf->video_buffer->height;
171      *chroma_type = PipeToChroma(pipe_format_to_chroma_format(p_surf->video_buffer->buffer_format));
172   } else {
173      *width = p_surf->templat.width;
174      *height = p_surf->templat.height;
175      *chroma_type = PipeToChroma(pipe_format_to_chroma_format(p_surf->templat.buffer_format));
176   }
177
178   return VDP_STATUS_OK;
179}
180
181static void
182vlVdpVideoSurfaceSize(vlVdpSurface *p_surf, int component,
183                      unsigned *width, unsigned *height)
184{
185   *width = p_surf->templat.width;
186   *height = p_surf->templat.height;
187
188   vl_video_buffer_adjust_size(width, height, component,
189                               pipe_format_to_chroma_format(p_surf->templat.buffer_format),
190                               p_surf->templat.interlaced);
191}
192
193/**
194 * Copy image data from a VdpVideoSurface to application memory in a specified
195 * YCbCr format.
196 */
197VdpStatus
198vlVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface,
199                              VdpYCbCrFormat destination_ycbcr_format,
200                              void *const *destination_data,
201                              uint32_t const *destination_pitches)
202{
203   vlVdpSurface *vlsurface;
204   struct pipe_context *pipe;
205   enum pipe_format format, buffer_format;
206   struct pipe_sampler_view **sampler_views;
207   enum getbits_conversion conversion = CONVERSION_NONE;
208   unsigned i, j;
209
210   vlsurface = vlGetDataHTAB(surface);
211   if (!vlsurface)
212      return VDP_STATUS_INVALID_HANDLE;
213
214   pipe = vlsurface->device->context;
215   if (!pipe)
216      return VDP_STATUS_INVALID_HANDLE;
217
218   if (!destination_data || !destination_pitches)
219       return VDP_STATUS_INVALID_POINTER;
220
221   format = FormatYCBCRToPipe(destination_ycbcr_format);
222   if (format == PIPE_FORMAT_NONE)
223      return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;
224
225   if (vlsurface->video_buffer == NULL)
226      return VDP_STATUS_INVALID_VALUE;
227
228   buffer_format = vlsurface->video_buffer->buffer_format;
229   if (format != buffer_format) {
230      if (format == PIPE_FORMAT_YV12 && buffer_format == PIPE_FORMAT_NV12)
231         conversion = CONVERSION_NV12_TO_YV12;
232      else if (format == PIPE_FORMAT_NV12 && buffer_format == PIPE_FORMAT_YV12)
233         conversion = CONVERSION_YV12_TO_NV12;
234      else if ((format == PIPE_FORMAT_YUYV && buffer_format == PIPE_FORMAT_UYVY) ||
235               (format == PIPE_FORMAT_UYVY && buffer_format == PIPE_FORMAT_YUYV))
236         conversion = CONVERSION_SWAP_YUYV_UYVY;
237      else
238         return VDP_STATUS_NO_IMPLEMENTATION;
239   }
240
241   mtx_lock(&vlsurface->device->mutex);
242   sampler_views = vlsurface->video_buffer->get_sampler_view_planes(vlsurface->video_buffer);
243   if (!sampler_views) {
244      mtx_unlock(&vlsurface->device->mutex);
245      return VDP_STATUS_RESOURCES;
246   }
247
248   for (i = 0; i < 3; ++i) {
249      unsigned width, height;
250      struct pipe_sampler_view *sv = sampler_views[i];
251      if (!sv) continue;
252
253      vlVdpVideoSurfaceSize(vlsurface, i, &width, &height);
254
255      for (j = 0; j < sv->texture->array_size; ++j) {
256         struct pipe_box box = {
257            0, 0, j,
258            width, height, 1
259         };
260         struct pipe_transfer *transfer;
261         uint8_t *map;
262
263         map = pipe->texture_map(pipe, sv->texture, 0,
264                                       PIPE_MAP_READ, &box, &transfer);
265         if (!map) {
266            mtx_unlock(&vlsurface->device->mutex);
267            return VDP_STATUS_RESOURCES;
268         }
269
270         if (conversion == CONVERSION_NV12_TO_YV12 && i == 1) {
271            u_copy_nv12_to_yv12(destination_data, destination_pitches,
272                                i, j, transfer->stride, sv->texture->array_size,
273                                map, box.width, box.height);
274         } else if (conversion == CONVERSION_YV12_TO_NV12 && i > 0) {
275            u_copy_yv12_to_nv12(destination_data, destination_pitches,
276                                i, j, transfer->stride, sv->texture->array_size,
277                                map, box.width, box.height);
278         } else if (conversion == CONVERSION_SWAP_YUYV_UYVY) {
279            u_copy_swap422_packed(destination_data, destination_pitches,
280                                   i, j, transfer->stride, sv->texture->array_size,
281                                   map, box.width, box.height);
282         } else {
283            util_copy_rect(destination_data[i] + destination_pitches[i] * j, sv->texture->format,
284                           destination_pitches[i] * sv->texture->array_size, 0, 0,
285                           box.width, box.height, map, transfer->stride, 0, 0);
286         }
287
288         pipe_texture_unmap(pipe, transfer);
289      }
290   }
291   mtx_unlock(&vlsurface->device->mutex);
292
293   return VDP_STATUS_OK;
294}
295
296/**
297 * Copy image data from application memory in a specific YCbCr format to
298 * a VdpVideoSurface.
299 */
300VdpStatus
301vlVdpVideoSurfacePutBitsYCbCr(VdpVideoSurface surface,
302                              VdpYCbCrFormat source_ycbcr_format,
303                              void const *const *source_data,
304                              uint32_t const *source_pitches)
305{
306   enum pipe_format pformat = FormatYCBCRToPipe(source_ycbcr_format);
307   enum getbits_conversion conversion = CONVERSION_NONE;
308   struct pipe_context *pipe;
309   struct pipe_sampler_view **sampler_views;
310   unsigned i, j;
311   unsigned usage = PIPE_MAP_WRITE;
312
313   vlVdpSurface *p_surf = vlGetDataHTAB(surface);
314   if (!p_surf)
315      return VDP_STATUS_INVALID_HANDLE;
316
317   pipe = p_surf->device->context;
318   if (!pipe)
319      return VDP_STATUS_INVALID_HANDLE;
320
321   if (!source_data || !source_pitches)
322       return VDP_STATUS_INVALID_POINTER;
323
324   mtx_lock(&p_surf->device->mutex);
325
326   if (p_surf->video_buffer == NULL ||
327       ((pformat != p_surf->video_buffer->buffer_format))) {
328      enum pipe_format nformat = pformat;
329      struct pipe_screen *screen = pipe->screen;
330
331      /* Determine the most suitable format for the new surface */
332      if (!screen->is_video_format_supported(screen, nformat,
333                                             PIPE_VIDEO_PROFILE_UNKNOWN,
334                                             PIPE_VIDEO_ENTRYPOINT_BITSTREAM)) {
335         nformat = screen->get_video_param(screen,
336                                           PIPE_VIDEO_PROFILE_UNKNOWN,
337                                           PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
338                                           PIPE_VIDEO_CAP_PREFERED_FORMAT);
339         if (nformat == PIPE_FORMAT_NONE) {
340            mtx_unlock(&p_surf->device->mutex);
341            return VDP_STATUS_NO_IMPLEMENTATION;
342         }
343      }
344
345      if (p_surf->video_buffer == NULL  ||
346          nformat != p_surf->video_buffer->buffer_format) {
347         /* destroy the old one */
348         if (p_surf->video_buffer)
349            p_surf->video_buffer->destroy(p_surf->video_buffer);
350
351         /* adjust the template parameters */
352         p_surf->templat.buffer_format = nformat;
353         if (nformat == PIPE_FORMAT_YUYV || nformat == PIPE_FORMAT_UYVY)
354            p_surf->templat.interlaced = false;
355
356         /* and try to create the video buffer with the new format */
357         p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
358
359         /* stil no luck? ok forget it we don't support it */
360         if (!p_surf->video_buffer) {
361            mtx_unlock(&p_surf->device->mutex);
362            return VDP_STATUS_NO_IMPLEMENTATION;
363         }
364         vlVdpVideoSurfaceClear(p_surf);
365      }
366   }
367
368   if (pformat != p_surf->video_buffer->buffer_format) {
369      if (pformat == PIPE_FORMAT_YV12 &&
370          p_surf->video_buffer->buffer_format == PIPE_FORMAT_NV12)
371         conversion = CONVERSION_YV12_TO_NV12;
372      else {
373         mtx_unlock(&p_surf->device->mutex);
374         return VDP_STATUS_NO_IMPLEMENTATION;
375      }
376   }
377
378   sampler_views = p_surf->video_buffer->get_sampler_view_planes(p_surf->video_buffer);
379   if (!sampler_views) {
380      mtx_unlock(&p_surf->device->mutex);
381      return VDP_STATUS_RESOURCES;
382   }
383
384   for (i = 0; i < 3; ++i) {
385      unsigned width, height;
386      struct pipe_sampler_view *sv = sampler_views[i];
387      struct pipe_resource *tex;
388      if (!sv || !source_pitches[i]) continue;
389
390      tex = sv->texture;
391      vlVdpVideoSurfaceSize(p_surf, i, &width, &height);
392
393      for (j = 0; j < tex->array_size; ++j) {
394         struct pipe_box dst_box = {
395            0, 0, j,
396            width, height, 1
397         };
398
399         if (conversion == CONVERSION_YV12_TO_NV12 && i == 1) {
400            struct pipe_transfer *transfer;
401            uint8_t *map;
402
403            map = pipe->texture_map(pipe, tex, 0, usage,
404                                     &dst_box, &transfer);
405            if (!map) {
406               mtx_unlock(&p_surf->device->mutex);
407               return VDP_STATUS_RESOURCES;
408            }
409
410            u_copy_nv12_from_yv12(source_data, source_pitches,
411                                  i, j, transfer->stride, tex->array_size,
412                                  map, dst_box.width, dst_box.height);
413
414            pipe_texture_unmap(pipe, transfer);
415         } else {
416            pipe->texture_subdata(pipe, tex, 0,
417                                  PIPE_MAP_WRITE, &dst_box,
418                                  source_data[i] + source_pitches[i] * j,
419                                  source_pitches[i] * tex->array_size,
420                                  0);
421         }
422         /*
423          * This surface has already been synced
424          * by the first map.
425          */
426         usage |= PIPE_MAP_UNSYNCHRONIZED;
427      }
428   }
429   mtx_unlock(&p_surf->device->mutex);
430
431   return VDP_STATUS_OK;
432}
433
434/**
435 * Helper function to initially clear the VideoSurface after (re-)creation
436 */
437void
438vlVdpVideoSurfaceClear(vlVdpSurface *vlsurf)
439{
440   struct pipe_context *pipe = vlsurf->device->context;
441   struct pipe_surface **surfaces;
442   unsigned i;
443
444   if (!vlsurf->video_buffer)
445      return;
446
447   surfaces = vlsurf->video_buffer->get_surfaces(vlsurf->video_buffer);
448   for (i = 0; i < VL_MAX_SURFACES; ++i) {
449      union pipe_color_union c = {};
450
451      if (!surfaces[i])
452         continue;
453
454      if (i > !!vlsurf->templat.interlaced)
455         c.f[0] = c.f[1] = c.f[2] = c.f[3] = 0.5f;
456
457      pipe->clear_render_target(pipe, surfaces[i], &c, 0, 0,
458                                surfaces[i]->width, surfaces[i]->height, false);
459   }
460   pipe->flush(pipe, NULL, 0);
461}
462
463/**
464 * Interop for the GL gallium frontend
465 */
466struct pipe_video_buffer *vlVdpVideoSurfaceGallium(VdpVideoSurface surface)
467{
468   vlVdpSurface *p_surf = vlGetDataHTAB(surface);
469   if (!p_surf)
470      return NULL;
471
472   mtx_lock(&p_surf->device->mutex);
473   if (p_surf->video_buffer == NULL) {
474      struct pipe_context *pipe = p_surf->device->context;
475
476      /* try to create a video buffer if we don't already have one */
477      p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
478   }
479   mtx_unlock(&p_surf->device->mutex);
480
481   return p_surf->video_buffer;
482}
483
484VdpStatus vlVdpVideoSurfaceDMABuf(VdpVideoSurface surface,
485                                  VdpVideoSurfacePlane plane,
486                                  struct VdpSurfaceDMABufDesc *result)
487{
488   vlVdpSurface *p_surf = vlGetDataHTAB(surface);
489
490   struct pipe_screen *pscreen;
491   struct winsys_handle whandle;
492
493   struct pipe_surface *surf;
494
495   if (!p_surf)
496      return VDP_STATUS_INVALID_HANDLE;
497
498   if (plane > 3)
499      return VDP_STATUS_INVALID_VALUE;
500
501   if (!result)
502      return VDP_STATUS_INVALID_POINTER;
503
504   memset(result, 0, sizeof(*result));
505   result->handle = -1;
506
507   mtx_lock(&p_surf->device->mutex);
508   if (p_surf->video_buffer == NULL) {
509      struct pipe_context *pipe = p_surf->device->context;
510
511      /* try to create a video buffer if we don't already have one */
512      p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
513   }
514
515   /* Check if surface match interop requirements */
516   if (p_surf->video_buffer == NULL || !p_surf->video_buffer->interlaced ||
517       p_surf->video_buffer->buffer_format != PIPE_FORMAT_NV12) {
518      mtx_unlock(&p_surf->device->mutex);
519      return VDP_STATUS_NO_IMPLEMENTATION;
520   }
521
522   surf = p_surf->video_buffer->get_surfaces(p_surf->video_buffer)[plane];
523   if (!surf) {
524      mtx_unlock(&p_surf->device->mutex);
525      return VDP_STATUS_RESOURCES;
526   }
527
528   memset(&whandle, 0, sizeof(struct winsys_handle));
529   whandle.type = WINSYS_HANDLE_TYPE_FD;
530   whandle.layer = surf->u.tex.first_layer;
531
532   pscreen = surf->texture->screen;
533   if (!pscreen->resource_get_handle(pscreen, p_surf->device->context,
534                                     surf->texture, &whandle,
535                                     PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE)) {
536      mtx_unlock(&p_surf->device->mutex);
537      return VDP_STATUS_NO_IMPLEMENTATION;
538   }
539
540   mtx_unlock(&p_surf->device->mutex);
541
542   result->handle = whandle.handle;
543   result->width = surf->width;
544   result->height = surf->height;
545   result->offset = whandle.offset;
546   result->stride = whandle.stride;
547
548   if (surf->format == PIPE_FORMAT_R8_UNORM)
549      result->format = VDP_RGBA_FORMAT_R8;
550   else
551      result->format = VDP_RGBA_FORMAT_R8G8;
552
553   return VDP_STATUS_OK;
554}
555