Home | History | Annotate | Download | only in vdpau
      1 /**************************************************************************
      2  *
      3  * Copyright 2010 Thomas Balling Srensen.
      4  * Copyright 2011 Christian Knig.
      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 "state_tracker/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
     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    pipe_mutex_lock(dev->mutex);
     84    memset(&p_surf->templat, 0, sizeof(p_surf->templat));
     85    p_surf->templat.buffer_format = pipe->screen->get_video_param
     86    (
     87       pipe->screen,
     88       PIPE_VIDEO_PROFILE_UNKNOWN,
     89       PIPE_VIDEO_ENTRYPOINT_BITSTREAM,
     90       PIPE_VIDEO_CAP_PREFERED_FORMAT
     91    );
     92    p_surf->templat.chroma_format = ChromaToPipe(chroma_type);
     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    pipe_mutex_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 
    117 no_handle:
    118    p_surf->video_buffer->destroy(p_surf->video_buffer);
    119 
    120 inv_device:
    121    DeviceReference(&p_surf->device, NULL);
    122    FREE(p_surf);
    123 
    124 no_res:
    125 inv_size:
    126    return ret;
    127 }
    128 
    129 /**
    130  * Destroy a VdpVideoSurface.
    131  */
    132 VdpStatus
    133 vlVdpVideoSurfaceDestroy(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    pipe_mutex_lock(p_surf->device->mutex);
    142    if (p_surf->video_buffer)
    143       p_surf->video_buffer->destroy(p_surf->video_buffer);
    144    pipe_mutex_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  */
    156 VdpStatus
    157 vlVdpVideoSurfaceGetParameters(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(p_surf->video_buffer->chroma_format);
    172    } else {
    173       *width = p_surf->templat.width;
    174       *height = p_surf->templat.height;
    175       *chroma_type = PipeToChroma(p_surf->templat.chroma_format);
    176    }
    177 
    178    return VDP_STATUS_OK;
    179 }
    180 
    181 static void
    182 vlVdpVideoSurfaceSize(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                                p_surf->templat.chroma_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  */
    197 VdpStatus
    198 vlVdpVideoSurfaceGetBitsYCbCr(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    pipe_mutex_lock(vlsurface->device->mutex);
    242    sampler_views = vlsurface->video_buffer->get_sampler_view_planes(vlsurface->video_buffer);
    243    if (!sampler_views) {
    244       pipe_mutex_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->transfer_map(pipe, sv->texture, 0,
    264                                        PIPE_TRANSFER_READ, &box, &transfer);
    265          if (!map) {
    266             pipe_mutex_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_transfer_unmap(pipe, transfer);
    289       }
    290    }
    291    pipe_mutex_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  */
    300 VdpStatus
    301 vlVdpVideoSurfacePutBitsYCbCr(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    struct pipe_context *pipe;
    308    struct pipe_sampler_view **sampler_views;
    309    unsigned i, j;
    310 
    311    vlVdpSurface *p_surf = vlGetDataHTAB(surface);
    312    if (!p_surf)
    313       return VDP_STATUS_INVALID_HANDLE;
    314 
    315    pipe = p_surf->device->context;
    316    if (!pipe)
    317       return VDP_STATUS_INVALID_HANDLE;
    318 
    319    if (!source_data || !source_pitches)
    320        return VDP_STATUS_INVALID_POINTER;
    321 
    322    pipe_mutex_lock(p_surf->device->mutex);
    323    if (p_surf->video_buffer == NULL || pformat != p_surf->video_buffer->buffer_format) {
    324 
    325       /* destroy the old one */
    326       if (p_surf->video_buffer)
    327          p_surf->video_buffer->destroy(p_surf->video_buffer);
    328 
    329       /* adjust the template parameters */
    330       p_surf->templat.buffer_format = pformat;
    331 
    332       /* and try to create the video buffer with the new format */
    333       p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
    334 
    335       /* stil no luck? ok forget it we don't support it */
    336       if (!p_surf->video_buffer) {
    337          pipe_mutex_unlock(p_surf->device->mutex);
    338          return VDP_STATUS_NO_IMPLEMENTATION;
    339       }
    340       vlVdpVideoSurfaceClear(p_surf);
    341    }
    342 
    343    sampler_views = p_surf->video_buffer->get_sampler_view_planes(p_surf->video_buffer);
    344    if (!sampler_views) {
    345       pipe_mutex_unlock(p_surf->device->mutex);
    346       return VDP_STATUS_RESOURCES;
    347    }
    348 
    349    for (i = 0; i < 3; ++i) {
    350       unsigned width, height;
    351       struct pipe_sampler_view *sv = sampler_views[i];
    352       if (!sv || !source_pitches[i]) continue;
    353 
    354       vlVdpVideoSurfaceSize(p_surf, i, &width, &height);
    355 
    356       for (j = 0; j < sv->texture->array_size; ++j) {
    357          struct pipe_box dst_box = {
    358             0, 0, j,
    359             width, height, 1
    360          };
    361 
    362          pipe->texture_subdata(pipe, sv->texture, 0,
    363                                PIPE_TRANSFER_WRITE, &dst_box,
    364                                source_data[i] + source_pitches[i] * j,
    365                                source_pitches[i] * sv->texture->array_size,
    366                                0);
    367       }
    368    }
    369    pipe_mutex_unlock(p_surf->device->mutex);
    370 
    371    return VDP_STATUS_OK;
    372 }
    373 
    374 /**
    375  * Helper function to initially clear the VideoSurface after (re-)creation
    376  */
    377 void
    378 vlVdpVideoSurfaceClear(vlVdpSurface *vlsurf)
    379 {
    380    struct pipe_context *pipe = vlsurf->device->context;
    381    struct pipe_surface **surfaces;
    382    unsigned i;
    383 
    384    if (!vlsurf->video_buffer)
    385       return;
    386 
    387    surfaces = vlsurf->video_buffer->get_surfaces(vlsurf->video_buffer);
    388    for (i = 0; i < VL_MAX_SURFACES; ++i) {
    389       union pipe_color_union c = {};
    390 
    391       if (!surfaces[i])
    392          continue;
    393 
    394       if (i > !!vlsurf->templat.interlaced)
    395          c.f[0] = c.f[1] = c.f[2] = c.f[3] = 0.5f;
    396 
    397       pipe->clear_render_target(pipe, surfaces[i], &c, 0, 0,
    398                                 surfaces[i]->width, surfaces[i]->height, false);
    399    }
    400    pipe->flush(pipe, NULL, 0);
    401 }
    402 
    403 /**
    404  * Interop to mesa state tracker
    405  */
    406 struct pipe_video_buffer *vlVdpVideoSurfaceGallium(VdpVideoSurface surface)
    407 {
    408    vlVdpSurface *p_surf = vlGetDataHTAB(surface);
    409    if (!p_surf)
    410       return NULL;
    411 
    412    pipe_mutex_lock(p_surf->device->mutex);
    413    if (p_surf->video_buffer == NULL) {
    414       struct pipe_context *pipe = p_surf->device->context;
    415 
    416       /* try to create a video buffer if we don't already have one */
    417       p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
    418    }
    419    pipe_mutex_unlock(p_surf->device->mutex);
    420 
    421    return p_surf->video_buffer;
    422 }
    423 
    424 VdpStatus vlVdpVideoSurfaceDMABuf(VdpVideoSurface surface,
    425                                   VdpVideoSurfacePlane plane,
    426                                   struct VdpSurfaceDMABufDesc *result)
    427 {
    428    vlVdpSurface *p_surf = vlGetDataHTAB(surface);
    429 
    430    struct pipe_screen *pscreen;
    431    struct winsys_handle whandle;
    432 
    433    struct pipe_surface *surf;
    434 
    435    if (!p_surf)
    436       return VDP_STATUS_INVALID_HANDLE;
    437 
    438    if (plane > 3)
    439       return VDP_STATUS_INVALID_VALUE;
    440 
    441    if (!result)
    442       return VDP_STATUS_INVALID_POINTER;
    443 
    444    memset(result, 0, sizeof(*result));
    445    result->handle = -1;
    446 
    447    pipe_mutex_lock(p_surf->device->mutex);
    448    if (p_surf->video_buffer == NULL) {
    449       struct pipe_context *pipe = p_surf->device->context;
    450 
    451       /* try to create a video buffer if we don't already have one */
    452       p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);
    453    }
    454 
    455    /* Check if surface match interop requirements */
    456    if (p_surf->video_buffer == NULL || !p_surf->video_buffer->interlaced ||
    457        p_surf->video_buffer->buffer_format != PIPE_FORMAT_NV12) {
    458       pipe_mutex_unlock(p_surf->device->mutex);
    459       return VDP_STATUS_NO_IMPLEMENTATION;
    460    }
    461 
    462    surf = p_surf->video_buffer->get_surfaces(p_surf->video_buffer)[plane];
    463    if (!surf) {
    464       pipe_mutex_unlock(p_surf->device->mutex);
    465       return VDP_STATUS_RESOURCES;
    466    }
    467 
    468    memset(&whandle, 0, sizeof(struct winsys_handle));
    469    whandle.type = DRM_API_HANDLE_TYPE_FD;
    470    whandle.layer = surf->u.tex.first_layer;
    471 
    472    pscreen = surf->texture->screen;
    473    if (!pscreen->resource_get_handle(pscreen, p_surf->device->context,
    474                                      surf->texture, &whandle,
    475                                      PIPE_HANDLE_USAGE_READ_WRITE)) {
    476       pipe_mutex_unlock(p_surf->device->mutex);
    477       return VDP_STATUS_NO_IMPLEMENTATION;
    478    }
    479 
    480    pipe_mutex_unlock(p_surf->device->mutex);
    481 
    482    result->handle = whandle.handle;
    483    result->width = surf->width;
    484    result->height = surf->height;
    485    result->offset = whandle.offset;
    486    result->stride = whandle.stride;
    487 
    488    if (surf->format == PIPE_FORMAT_R8_UNORM)
    489       result->format = VDP_RGBA_FORMAT_R8;
    490    else
    491       result->format = VDP_RGBA_FORMAT_R8G8;
    492 
    493    return VDP_STATUS_OK;
    494 }
    495