mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-23 04:18:14 +02:00
VAEncCodedBufferType is used for reading back encoded data. Mapping it for read instead of write speeds up reading the data on CPU. On radeonsi this will result in VRAM copy to staging buffer in cached GTT, making the CPU read much faster. Signed-off-by: David Rosca <nowrep@gmail.com> Reviewed-by: Ruijing Dong <ruijing.dong@amd.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/20376>
513 lines
14 KiB
C
513 lines
14 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright 2010 Thomas Balling Sørensen & Orasanu Lucian.
|
|
* Copyright 2014 Advanced Micro Devices, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sub license, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial portions
|
|
* of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
|
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
**************************************************************************/
|
|
|
|
#include "pipe/p_screen.h"
|
|
#include "frontend/drm_driver.h"
|
|
#include "util/u_memory.h"
|
|
#include "util/u_handle_table.h"
|
|
#include "util/u_transfer.h"
|
|
#include "vl/vl_winsys.h"
|
|
|
|
#include "va_private.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <va/va_win32.h>
|
|
#endif
|
|
|
|
VAStatus
|
|
vlVaCreateBuffer(VADriverContextP ctx, VAContextID context, VABufferType type,
|
|
unsigned int size, unsigned int num_elements, void *data,
|
|
VABufferID *buf_id)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
buf = CALLOC(1, sizeof(vlVaBuffer));
|
|
if (!buf)
|
|
return VA_STATUS_ERROR_ALLOCATION_FAILED;
|
|
|
|
buf->type = type;
|
|
buf->size = size;
|
|
buf->num_elements = num_elements;
|
|
buf->data = MALLOC(size * num_elements);
|
|
|
|
if (!buf->data) {
|
|
FREE(buf);
|
|
return VA_STATUS_ERROR_ALLOCATION_FAILED;
|
|
}
|
|
|
|
if (data)
|
|
memcpy(buf->data, data, size * num_elements);
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
mtx_lock(&drv->mutex);
|
|
*buf_id = handle_table_add(drv->htab, buf);
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaBufferSetNumElements(VADriverContextP ctx, VABufferID buf_id,
|
|
unsigned int num_elements)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
mtx_unlock(&drv->mutex);
|
|
if (!buf)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
if (buf->derived_surface.resource)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
buf->data = REALLOC(buf->data, buf->size * buf->num_elements,
|
|
buf->size * num_elements);
|
|
buf->num_elements = num_elements;
|
|
|
|
if (!buf->data)
|
|
return VA_STATUS_ERROR_ALLOCATION_FAILED;
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaMapBuffer(VADriverContextP ctx, VABufferID buf_id, void **pbuff)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
if (!drv)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
if (!pbuff)
|
|
return VA_STATUS_ERROR_INVALID_PARAMETER;
|
|
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
if (!buf || buf->export_refcount > 0) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
if (buf->derived_surface.resource) {
|
|
struct pipe_resource *resource;
|
|
struct pipe_box box;
|
|
void *(*map_func)(struct pipe_context *,
|
|
struct pipe_resource *resource,
|
|
unsigned level,
|
|
unsigned usage, /* a combination of PIPE_MAP_x */
|
|
const struct pipe_box *,
|
|
struct pipe_transfer **out_transfer);
|
|
|
|
memset(&box, 0, sizeof(box));
|
|
resource = buf->derived_surface.resource;
|
|
box.width = resource->width0;
|
|
box.height = resource->height0;
|
|
box.depth = resource->depth0;
|
|
|
|
if (resource->target == PIPE_BUFFER)
|
|
map_func = drv->pipe->buffer_map;
|
|
else
|
|
map_func = drv->pipe->texture_map;
|
|
|
|
*pbuff = map_func(drv->pipe, resource, 0,
|
|
buf->type == VAEncCodedBufferType ?
|
|
PIPE_MAP_READ : PIPE_MAP_WRITE,
|
|
&box, &buf->derived_surface.transfer);
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
if (!buf->derived_surface.transfer || !*pbuff)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
if (buf->type == VAEncCodedBufferType) {
|
|
((VACodedBufferSegment*)buf->data)->buf = *pbuff;
|
|
((VACodedBufferSegment*)buf->data)->size = buf->coded_size;
|
|
((VACodedBufferSegment*)buf->data)->next = NULL;
|
|
*pbuff = buf->data;
|
|
}
|
|
} else {
|
|
mtx_unlock(&drv->mutex);
|
|
*pbuff = buf->data;
|
|
}
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaUnmapBuffer(VADriverContextP ctx, VABufferID buf_id)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
struct pipe_resource *resource;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
if (!drv)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
if (!buf || buf->export_refcount > 0) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
resource = buf->derived_surface.resource;
|
|
if (resource) {
|
|
void (*unmap_func)(struct pipe_context *pipe,
|
|
struct pipe_transfer *transfer);
|
|
|
|
if (!buf->derived_surface.transfer) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
if (resource->target == PIPE_BUFFER)
|
|
unmap_func = pipe_buffer_unmap;
|
|
else
|
|
unmap_func = pipe_texture_unmap;
|
|
|
|
unmap_func(drv->pipe, buf->derived_surface.transfer);
|
|
buf->derived_surface.transfer = NULL;
|
|
}
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaDestroyBuffer(VADriverContextP ctx, VABufferID buf_id)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
if (!buf) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
if (buf->derived_surface.resource) {
|
|
pipe_resource_reference(&buf->derived_surface.resource, NULL);
|
|
|
|
if (buf->derived_image_buffer)
|
|
buf->derived_image_buffer->destroy(buf->derived_image_buffer);
|
|
}
|
|
|
|
FREE(buf->data);
|
|
FREE(buf);
|
|
handle_table_remove(VL_VA_DRIVER(ctx)->htab, buf_id);
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaBufferInfo(VADriverContextP ctx, VABufferID buf_id, VABufferType *type,
|
|
unsigned int *size, unsigned int *num_elements)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
mtx_unlock(&drv->mutex);
|
|
if (!buf)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
*type = buf->type;
|
|
*size = buf->size;
|
|
*num_elements = buf->num_elements;
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaAcquireBufferHandle(VADriverContextP ctx, VABufferID buf_id,
|
|
VABufferInfo *out_buf_info)
|
|
{
|
|
vlVaDriver *drv;
|
|
uint32_t i;
|
|
uint32_t mem_type;
|
|
vlVaBuffer *buf ;
|
|
struct pipe_screen *screen;
|
|
|
|
/* List of supported memory types, in preferred order. */
|
|
static const uint32_t mem_types[] = {
|
|
#ifdef _WIN32
|
|
VA_SURFACE_ATTRIB_MEM_TYPE_NTHANDLE,
|
|
VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE,
|
|
#else
|
|
VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME,
|
|
#endif
|
|
0
|
|
};
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
screen = VL_VA_PSCREEN(ctx);
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(VL_VA_DRIVER(ctx)->htab, buf_id);
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
if (!buf)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
/* Only VA surface|image like buffers are supported for now .*/
|
|
if (buf->type != VAImageBufferType)
|
|
return VA_STATUS_ERROR_UNSUPPORTED_BUFFERTYPE;
|
|
|
|
if (!out_buf_info)
|
|
return VA_STATUS_ERROR_INVALID_PARAMETER;
|
|
|
|
if (!out_buf_info->mem_type)
|
|
mem_type = mem_types[0];
|
|
else {
|
|
mem_type = 0;
|
|
for (i = 0; mem_types[i] != 0; i++) {
|
|
if (out_buf_info->mem_type & mem_types[i]) {
|
|
mem_type = out_buf_info->mem_type;
|
|
break;
|
|
}
|
|
}
|
|
if (!mem_type)
|
|
return VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE;
|
|
}
|
|
|
|
if (!buf->derived_surface.resource)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
if (buf->export_refcount > 0) {
|
|
if (buf->export_state.mem_type != mem_type)
|
|
return VA_STATUS_ERROR_INVALID_PARAMETER;
|
|
} else {
|
|
VABufferInfo * const buf_info = &buf->export_state;
|
|
|
|
switch (mem_type) {
|
|
#ifdef _WIN32
|
|
case VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE:
|
|
case VA_SURFACE_ATTRIB_MEM_TYPE_NTHANDLE:
|
|
#else
|
|
case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME:
|
|
#endif
|
|
{
|
|
struct winsys_handle whandle;
|
|
|
|
mtx_lock(&drv->mutex);
|
|
drv->pipe->flush(drv->pipe, NULL, 0);
|
|
|
|
memset(&whandle, 0, sizeof(whandle));
|
|
whandle.type = WINSYS_HANDLE_TYPE_FD;
|
|
|
|
#ifdef _WIN32
|
|
if (mem_type == VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE)
|
|
whandle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
|
|
#endif
|
|
if (!screen->resource_get_handle(screen, drv->pipe,
|
|
buf->derived_surface.resource,
|
|
&whandle, PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE)) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
buf_info->handle = (intptr_t)whandle.handle;
|
|
|
|
#ifdef _WIN32
|
|
if (mem_type == VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE)
|
|
buf_info->handle = (intptr_t)whandle.com_obj;
|
|
#endif
|
|
break;
|
|
}
|
|
default:
|
|
return VA_STATUS_ERROR_UNSUPPORTED_MEMORY_TYPE;
|
|
}
|
|
|
|
buf_info->type = buf->type;
|
|
buf_info->mem_type = mem_type;
|
|
buf_info->mem_size = buf->num_elements * buf->size;
|
|
}
|
|
|
|
buf->export_refcount++;
|
|
|
|
*out_buf_info = buf->export_state;
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
VAStatus
|
|
vlVaReleaseBufferHandle(VADriverContextP ctx, VABufferID buf_id)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
mtx_unlock(&drv->mutex);
|
|
|
|
if (!buf)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
if (buf->export_refcount == 0)
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
|
|
if (--buf->export_refcount == 0) {
|
|
VABufferInfo * const buf_info = &buf->export_state;
|
|
|
|
switch (buf_info->mem_type) {
|
|
#ifdef _WIN32
|
|
case VA_SURFACE_ATTRIB_MEM_TYPE_D3D12_RESOURCE:
|
|
// Do nothing for this case.
|
|
break;
|
|
case VA_SURFACE_ATTRIB_MEM_TYPE_NTHANDLE:
|
|
CloseHandle((HANDLE) buf_info->handle);
|
|
break;
|
|
#else
|
|
case VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME:
|
|
close((intptr_t)buf_info->handle);
|
|
break;
|
|
#endif
|
|
default:
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
buf_info->mem_type = 0;
|
|
}
|
|
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
#if VA_CHECK_VERSION(1, 15, 0)
|
|
VAStatus
|
|
vlVaSyncBuffer(VADriverContextP ctx, VABufferID buf_id, uint64_t timeout_ns)
|
|
{
|
|
vlVaDriver *drv;
|
|
vlVaContext *context;
|
|
vlVaBuffer *buf;
|
|
|
|
if (!ctx)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
drv = VL_VA_DRIVER(ctx);
|
|
if (!drv)
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
|
|
/* Some apps like ffmpeg check for vaSyncBuffer to be present
|
|
to do async enqueuing of multiple vaEndPicture encode calls
|
|
before calling vaSyncBuffer with a pre-defined latency
|
|
If vaSyncBuffer is not implemented, they fallback to the
|
|
usual synchronous pairs of { vaEndPicture + vaSyncSurface }
|
|
|
|
As this might require the driver to support multiple
|
|
operations and/or store multiple feedback values before sync
|
|
fallback to backward compatible behaviour unless driver
|
|
explicitly supports PIPE_VIDEO_CAP_ENC_SUPPORTS_ASYNC_OPERATION
|
|
*/
|
|
if (!drv->pipe->screen->get_video_param(drv->pipe->screen,
|
|
PIPE_VIDEO_PROFILE_UNKNOWN,
|
|
PIPE_VIDEO_ENTRYPOINT_ENCODE,
|
|
PIPE_VIDEO_CAP_ENC_SUPPORTS_ASYNC_OPERATION))
|
|
return VA_STATUS_ERROR_UNIMPLEMENTED;
|
|
|
|
/* vaSyncBuffer spec states that "If timeout is zero, the function returns immediately." */
|
|
if (timeout_ns == 0)
|
|
return VA_STATUS_ERROR_TIMEDOUT;
|
|
|
|
if (timeout_ns != VA_TIMEOUT_INFINITE)
|
|
return VA_STATUS_ERROR_UNIMPLEMENTED;
|
|
|
|
mtx_lock(&drv->mutex);
|
|
buf = handle_table_get(drv->htab, buf_id);
|
|
|
|
if (!buf) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_BUFFER;
|
|
}
|
|
|
|
if (!buf->feedback) {
|
|
/* No outstanding operation: nothing to do. */
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
|
|
context = handle_table_get(drv->htab, buf->ctx);
|
|
if (!context) {
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_ERROR_INVALID_CONTEXT;
|
|
}
|
|
|
|
vlVaSurface* surf = handle_table_get(drv->htab, buf->associated_encode_input_surf);
|
|
|
|
if ((buf->feedback) && (context->decoder->entrypoint == PIPE_VIDEO_ENTRYPOINT_ENCODE)) {
|
|
context->decoder->get_feedback(context->decoder, buf->feedback, &(buf->coded_size));
|
|
buf->feedback = NULL;
|
|
/* Also mark the associated render target (encode source texture) surface as done
|
|
in case they call vaSyncSurface on it to avoid getting the feedback twice*/
|
|
if(surf)
|
|
{
|
|
surf->feedback = NULL;
|
|
buf->associated_encode_input_surf = VA_INVALID_ID;
|
|
}
|
|
}
|
|
|
|
mtx_unlock(&drv->mutex);
|
|
return VA_STATUS_SUCCESS;
|
|
}
|
|
#endif
|