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