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    mtx_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    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 
    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    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  */
    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    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->transfer_map(pipe, sv->texture, 0,
    264                                        PIPE_TRANSFER_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_transfer_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  */
    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    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_TRANSFER_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->transfer_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_transfer_unmap(pipe, transfer);
    415          } else {
    416             pipe->texture_subdata(pipe, tex, 0,
    417                                   PIPE_TRANSFER_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_TRANSFER_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  */
    437 void
    438 vlVdpVideoSurfaceClear(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 to mesa state tracker
    465  */
    466 struct 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 
    484 VdpStatus 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 = DRM_API_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_READ_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