Path: blob/21.2-virgl/src/gallium/frontends/vdpau/surface.c
4565 views
/**************************************************************************1*2* Copyright 2010 Thomas Balling Sørensen.3* Copyright 2011 Christian König.4* All Rights Reserved.5*6* Permission is hereby granted, free of charge, to any person obtaining a7* copy of this software and associated documentation files (the8* "Software"), to deal in the Software without restriction, including9* without limitation the rights to use, copy, modify, merge, publish,10* distribute, sub license, and/or sell copies of the Software, and to11* permit persons to whom the Software is furnished to do so, subject to12* the following conditions:13*14* The above copyright notice and this permission notice (including the15* next paragraph) shall be included in all copies or substantial portions16* of the Software.17*18* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21* IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR22* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.25*26**************************************************************************/2728#include <assert.h>2930#include "pipe/p_state.h"3132#include "util/u_memory.h"33#include "util/u_debug.h"34#include "util/u_rect.h"35#include "util/u_surface.h"36#include "util/u_video.h"37#include "vl/vl_defines.h"3839#include "frontend/drm_driver.h"4041#include "vdpau_private.h"4243enum getbits_conversion {44CONVERSION_NONE,45CONVERSION_NV12_TO_YV12,46CONVERSION_YV12_TO_NV12,47CONVERSION_SWAP_YUYV_UYVY,48};4950/**51* Create a VdpVideoSurface.52*/53VdpStatus54vlVdpVideoSurfaceCreate(VdpDevice device, VdpChromaType chroma_type,55uint32_t width, uint32_t height,56VdpVideoSurface *surface)57{58struct pipe_context *pipe;59vlVdpSurface *p_surf;60VdpStatus ret;6162if (!(width && height)) {63ret = VDP_STATUS_INVALID_SIZE;64goto inv_size;65}6667p_surf = CALLOC(1, sizeof(vlVdpSurface));68if (!p_surf) {69ret = VDP_STATUS_RESOURCES;70goto no_res;71}7273vlVdpDevice *dev = vlGetDataHTAB(device);74if (!dev) {75ret = VDP_STATUS_INVALID_HANDLE;76goto inv_device;77}7879DeviceReference(&p_surf->device, dev);80pipe = dev->context;8182mtx_lock(&dev->mutex);83memset(&p_surf->templat, 0, sizeof(p_surf->templat));84/* TODO: buffer_format should be selected to match chroma_type */85p_surf->templat.buffer_format = pipe->screen->get_video_param86(87pipe->screen,88PIPE_VIDEO_PROFILE_UNKNOWN,89PIPE_VIDEO_ENTRYPOINT_BITSTREAM,90PIPE_VIDEO_CAP_PREFERED_FORMAT91);92p_surf->templat.width = width;93p_surf->templat.height = height;94p_surf->templat.interlaced = pipe->screen->get_video_param95(96pipe->screen,97PIPE_VIDEO_PROFILE_UNKNOWN,98PIPE_VIDEO_ENTRYPOINT_BITSTREAM,99PIPE_VIDEO_CAP_PREFERS_INTERLACED100);101if (p_surf->templat.buffer_format != PIPE_FORMAT_NONE)102p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);103104/* do not mandate early allocation of a video buffer */105vlVdpVideoSurfaceClear(p_surf);106mtx_unlock(&dev->mutex);107108*surface = vlAddDataHTAB(p_surf);109if (*surface == 0) {110ret = VDP_STATUS_ERROR;111goto no_handle;112}113114return VDP_STATUS_OK;115116no_handle:117p_surf->video_buffer->destroy(p_surf->video_buffer);118119inv_device:120DeviceReference(&p_surf->device, NULL);121FREE(p_surf);122123no_res:124inv_size:125return ret;126}127128/**129* Destroy a VdpVideoSurface.130*/131VdpStatus132vlVdpVideoSurfaceDestroy(VdpVideoSurface surface)133{134vlVdpSurface *p_surf;135136p_surf = (vlVdpSurface *)vlGetDataHTAB((vlHandle)surface);137if (!p_surf)138return VDP_STATUS_INVALID_HANDLE;139140mtx_lock(&p_surf->device->mutex);141if (p_surf->video_buffer)142p_surf->video_buffer->destroy(p_surf->video_buffer);143mtx_unlock(&p_surf->device->mutex);144145vlRemoveDataHTAB(surface);146DeviceReference(&p_surf->device, NULL);147FREE(p_surf);148149return VDP_STATUS_OK;150}151152/**153* Retrieve the parameters used to create a VdpVideoSurface.154*/155VdpStatus156vlVdpVideoSurfaceGetParameters(VdpVideoSurface surface,157VdpChromaType *chroma_type,158uint32_t *width, uint32_t *height)159{160if (!(width && height && chroma_type))161return VDP_STATUS_INVALID_POINTER;162163vlVdpSurface *p_surf = vlGetDataHTAB(surface);164if (!p_surf)165return VDP_STATUS_INVALID_HANDLE;166167if (p_surf->video_buffer) {168*width = p_surf->video_buffer->width;169*height = p_surf->video_buffer->height;170*chroma_type = PipeToChroma(pipe_format_to_chroma_format(p_surf->video_buffer->buffer_format));171} else {172*width = p_surf->templat.width;173*height = p_surf->templat.height;174*chroma_type = PipeToChroma(pipe_format_to_chroma_format(p_surf->templat.buffer_format));175}176177return VDP_STATUS_OK;178}179180static void181vlVdpVideoSurfaceSize(vlVdpSurface *p_surf, int component,182unsigned *width, unsigned *height)183{184*width = p_surf->templat.width;185*height = p_surf->templat.height;186187vl_video_buffer_adjust_size(width, height, component,188pipe_format_to_chroma_format(p_surf->templat.buffer_format),189p_surf->templat.interlaced);190}191192/**193* Copy image data from a VdpVideoSurface to application memory in a specified194* YCbCr format.195*/196VdpStatus197vlVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface,198VdpYCbCrFormat destination_ycbcr_format,199void *const *destination_data,200uint32_t const *destination_pitches)201{202vlVdpSurface *vlsurface;203struct pipe_context *pipe;204enum pipe_format format, buffer_format;205struct pipe_sampler_view **sampler_views;206enum getbits_conversion conversion = CONVERSION_NONE;207unsigned i, j;208209vlsurface = vlGetDataHTAB(surface);210if (!vlsurface)211return VDP_STATUS_INVALID_HANDLE;212213pipe = vlsurface->device->context;214if (!pipe)215return VDP_STATUS_INVALID_HANDLE;216217if (!destination_data || !destination_pitches)218return VDP_STATUS_INVALID_POINTER;219220format = FormatYCBCRToPipe(destination_ycbcr_format);221if (format == PIPE_FORMAT_NONE)222return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;223224if (vlsurface->video_buffer == NULL)225return VDP_STATUS_INVALID_VALUE;226227buffer_format = vlsurface->video_buffer->buffer_format;228if (format != buffer_format) {229if (format == PIPE_FORMAT_YV12 && buffer_format == PIPE_FORMAT_NV12)230conversion = CONVERSION_NV12_TO_YV12;231else if (format == PIPE_FORMAT_NV12 && buffer_format == PIPE_FORMAT_YV12)232conversion = CONVERSION_YV12_TO_NV12;233else if ((format == PIPE_FORMAT_YUYV && buffer_format == PIPE_FORMAT_UYVY) ||234(format == PIPE_FORMAT_UYVY && buffer_format == PIPE_FORMAT_YUYV))235conversion = CONVERSION_SWAP_YUYV_UYVY;236else237return VDP_STATUS_NO_IMPLEMENTATION;238}239240mtx_lock(&vlsurface->device->mutex);241sampler_views = vlsurface->video_buffer->get_sampler_view_planes(vlsurface->video_buffer);242if (!sampler_views) {243mtx_unlock(&vlsurface->device->mutex);244return VDP_STATUS_RESOURCES;245}246247for (i = 0; i < 3; ++i) {248unsigned width, height;249struct pipe_sampler_view *sv = sampler_views[i];250if (!sv) continue;251252vlVdpVideoSurfaceSize(vlsurface, i, &width, &height);253254for (j = 0; j < sv->texture->array_size; ++j) {255struct pipe_box box = {2560, 0, j,257width, height, 1258};259struct pipe_transfer *transfer;260uint8_t *map;261262map = pipe->texture_map(pipe, sv->texture, 0,263PIPE_MAP_READ, &box, &transfer);264if (!map) {265mtx_unlock(&vlsurface->device->mutex);266return VDP_STATUS_RESOURCES;267}268269if (conversion == CONVERSION_NV12_TO_YV12 && i == 1) {270u_copy_nv12_to_yv12(destination_data, destination_pitches,271i, j, transfer->stride, sv->texture->array_size,272map, box.width, box.height);273} else if (conversion == CONVERSION_YV12_TO_NV12 && i > 0) {274u_copy_yv12_to_nv12(destination_data, destination_pitches,275i, j, transfer->stride, sv->texture->array_size,276map, box.width, box.height);277} else if (conversion == CONVERSION_SWAP_YUYV_UYVY) {278u_copy_swap422_packed(destination_data, destination_pitches,279i, j, transfer->stride, sv->texture->array_size,280map, box.width, box.height);281} else {282util_copy_rect(destination_data[i] + destination_pitches[i] * j, sv->texture->format,283destination_pitches[i] * sv->texture->array_size, 0, 0,284box.width, box.height, map, transfer->stride, 0, 0);285}286287pipe_texture_unmap(pipe, transfer);288}289}290mtx_unlock(&vlsurface->device->mutex);291292return VDP_STATUS_OK;293}294295/**296* Copy image data from application memory in a specific YCbCr format to297* a VdpVideoSurface.298*/299VdpStatus300vlVdpVideoSurfacePutBitsYCbCr(VdpVideoSurface surface,301VdpYCbCrFormat source_ycbcr_format,302void const *const *source_data,303uint32_t const *source_pitches)304{305enum pipe_format pformat = FormatYCBCRToPipe(source_ycbcr_format);306enum getbits_conversion conversion = CONVERSION_NONE;307struct pipe_context *pipe;308struct pipe_sampler_view **sampler_views;309unsigned i, j;310unsigned usage = PIPE_MAP_WRITE;311312vlVdpSurface *p_surf = vlGetDataHTAB(surface);313if (!p_surf)314return VDP_STATUS_INVALID_HANDLE;315316pipe = p_surf->device->context;317if (!pipe)318return VDP_STATUS_INVALID_HANDLE;319320if (!source_data || !source_pitches)321return VDP_STATUS_INVALID_POINTER;322323mtx_lock(&p_surf->device->mutex);324325if (p_surf->video_buffer == NULL ||326((pformat != p_surf->video_buffer->buffer_format))) {327enum pipe_format nformat = pformat;328struct pipe_screen *screen = pipe->screen;329330/* Determine the most suitable format for the new surface */331if (!screen->is_video_format_supported(screen, nformat,332PIPE_VIDEO_PROFILE_UNKNOWN,333PIPE_VIDEO_ENTRYPOINT_BITSTREAM)) {334nformat = screen->get_video_param(screen,335PIPE_VIDEO_PROFILE_UNKNOWN,336PIPE_VIDEO_ENTRYPOINT_BITSTREAM,337PIPE_VIDEO_CAP_PREFERED_FORMAT);338if (nformat == PIPE_FORMAT_NONE) {339mtx_unlock(&p_surf->device->mutex);340return VDP_STATUS_NO_IMPLEMENTATION;341}342}343344if (p_surf->video_buffer == NULL ||345nformat != p_surf->video_buffer->buffer_format) {346/* destroy the old one */347if (p_surf->video_buffer)348p_surf->video_buffer->destroy(p_surf->video_buffer);349350/* adjust the template parameters */351p_surf->templat.buffer_format = nformat;352if (nformat == PIPE_FORMAT_YUYV || nformat == PIPE_FORMAT_UYVY)353p_surf->templat.interlaced = false;354355/* and try to create the video buffer with the new format */356p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);357358/* stil no luck? ok forget it we don't support it */359if (!p_surf->video_buffer) {360mtx_unlock(&p_surf->device->mutex);361return VDP_STATUS_NO_IMPLEMENTATION;362}363vlVdpVideoSurfaceClear(p_surf);364}365}366367if (pformat != p_surf->video_buffer->buffer_format) {368if (pformat == PIPE_FORMAT_YV12 &&369p_surf->video_buffer->buffer_format == PIPE_FORMAT_NV12)370conversion = CONVERSION_YV12_TO_NV12;371else {372mtx_unlock(&p_surf->device->mutex);373return VDP_STATUS_NO_IMPLEMENTATION;374}375}376377sampler_views = p_surf->video_buffer->get_sampler_view_planes(p_surf->video_buffer);378if (!sampler_views) {379mtx_unlock(&p_surf->device->mutex);380return VDP_STATUS_RESOURCES;381}382383for (i = 0; i < 3; ++i) {384unsigned width, height;385struct pipe_sampler_view *sv = sampler_views[i];386struct pipe_resource *tex;387if (!sv || !source_pitches[i]) continue;388389tex = sv->texture;390vlVdpVideoSurfaceSize(p_surf, i, &width, &height);391392for (j = 0; j < tex->array_size; ++j) {393struct pipe_box dst_box = {3940, 0, j,395width, height, 1396};397398if (conversion == CONVERSION_YV12_TO_NV12 && i == 1) {399struct pipe_transfer *transfer;400uint8_t *map;401402map = pipe->texture_map(pipe, tex, 0, usage,403&dst_box, &transfer);404if (!map) {405mtx_unlock(&p_surf->device->mutex);406return VDP_STATUS_RESOURCES;407}408409u_copy_nv12_from_yv12(source_data, source_pitches,410i, j, transfer->stride, tex->array_size,411map, dst_box.width, dst_box.height);412413pipe_texture_unmap(pipe, transfer);414} else {415pipe->texture_subdata(pipe, tex, 0,416PIPE_MAP_WRITE, &dst_box,417source_data[i] + source_pitches[i] * j,418source_pitches[i] * tex->array_size,4190);420}421/*422* This surface has already been synced423* by the first map.424*/425usage |= PIPE_MAP_UNSYNCHRONIZED;426}427}428mtx_unlock(&p_surf->device->mutex);429430return VDP_STATUS_OK;431}432433/**434* Helper function to initially clear the VideoSurface after (re-)creation435*/436void437vlVdpVideoSurfaceClear(vlVdpSurface *vlsurf)438{439struct pipe_context *pipe = vlsurf->device->context;440struct pipe_surface **surfaces;441unsigned i;442443if (!vlsurf->video_buffer)444return;445446surfaces = vlsurf->video_buffer->get_surfaces(vlsurf->video_buffer);447for (i = 0; i < VL_MAX_SURFACES; ++i) {448union pipe_color_union c = {};449450if (!surfaces[i])451continue;452453if (i > !!vlsurf->templat.interlaced)454c.f[0] = c.f[1] = c.f[2] = c.f[3] = 0.5f;455456pipe->clear_render_target(pipe, surfaces[i], &c, 0, 0,457surfaces[i]->width, surfaces[i]->height, false);458}459pipe->flush(pipe, NULL, 0);460}461462/**463* Interop for the GL gallium frontend464*/465struct pipe_video_buffer *vlVdpVideoSurfaceGallium(VdpVideoSurface surface)466{467vlVdpSurface *p_surf = vlGetDataHTAB(surface);468if (!p_surf)469return NULL;470471mtx_lock(&p_surf->device->mutex);472if (p_surf->video_buffer == NULL) {473struct pipe_context *pipe = p_surf->device->context;474475/* try to create a video buffer if we don't already have one */476p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);477}478mtx_unlock(&p_surf->device->mutex);479480return p_surf->video_buffer;481}482483VdpStatus vlVdpVideoSurfaceDMABuf(VdpVideoSurface surface,484VdpVideoSurfacePlane plane,485struct VdpSurfaceDMABufDesc *result)486{487vlVdpSurface *p_surf = vlGetDataHTAB(surface);488489struct pipe_screen *pscreen;490struct winsys_handle whandle;491492struct pipe_surface *surf;493494if (!p_surf)495return VDP_STATUS_INVALID_HANDLE;496497if (plane > 3)498return VDP_STATUS_INVALID_VALUE;499500if (!result)501return VDP_STATUS_INVALID_POINTER;502503memset(result, 0, sizeof(*result));504result->handle = -1;505506mtx_lock(&p_surf->device->mutex);507if (p_surf->video_buffer == NULL) {508struct pipe_context *pipe = p_surf->device->context;509510/* try to create a video buffer if we don't already have one */511p_surf->video_buffer = pipe->create_video_buffer(pipe, &p_surf->templat);512}513514/* Check if surface match interop requirements */515if (p_surf->video_buffer == NULL || !p_surf->video_buffer->interlaced ||516p_surf->video_buffer->buffer_format != PIPE_FORMAT_NV12) {517mtx_unlock(&p_surf->device->mutex);518return VDP_STATUS_NO_IMPLEMENTATION;519}520521surf = p_surf->video_buffer->get_surfaces(p_surf->video_buffer)[plane];522if (!surf) {523mtx_unlock(&p_surf->device->mutex);524return VDP_STATUS_RESOURCES;525}526527memset(&whandle, 0, sizeof(struct winsys_handle));528whandle.type = WINSYS_HANDLE_TYPE_FD;529whandle.layer = surf->u.tex.first_layer;530531pscreen = surf->texture->screen;532if (!pscreen->resource_get_handle(pscreen, p_surf->device->context,533surf->texture, &whandle,534PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE)) {535mtx_unlock(&p_surf->device->mutex);536return VDP_STATUS_NO_IMPLEMENTATION;537}538539mtx_unlock(&p_surf->device->mutex);540541result->handle = whandle.handle;542result->width = surf->width;543result->height = surf->height;544result->offset = whandle.offset;545result->stride = whandle.stride;546547if (surf->format == PIPE_FORMAT_R8_UNORM)548result->format = VDP_RGBA_FORMAT_R8;549else550result->format = VDP_RGBA_FORMAT_R8G8;551552return VDP_STATUS_OK;553}554555556