mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-23 12:58:09 +02:00
Acked-by: Eric Anholt <eric@anholt.net> Acked-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com> Acked-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer@amd.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/4902>
344 lines
11 KiB
C
344 lines
11 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright 2009 Younes Manton.
|
|
* 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 VMWARE AND/OR ITS SUPPLIERS 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 <assert.h>
|
|
|
|
#include <X11/Xlibint.h>
|
|
#include <X11/extensions/XvMClib.h>
|
|
|
|
#include "pipe/p_screen.h"
|
|
#include "pipe/p_video_codec.h"
|
|
#include "pipe/p_video_state.h"
|
|
#include "pipe/p_state.h"
|
|
|
|
#include "util/u_memory.h"
|
|
|
|
#include "vl/vl_csc.h"
|
|
#include "vl/vl_winsys.h"
|
|
|
|
#include "xvmc_private.h"
|
|
|
|
static Status Validate(Display *dpy, XvPortID port, int surface_type_id,
|
|
unsigned int width, unsigned int height, int flags,
|
|
bool *found_port, int *screen, int *chroma_format,
|
|
int *mc_type, int *surface_flags,
|
|
unsigned short *subpic_max_w,
|
|
unsigned short *subpic_max_h)
|
|
{
|
|
bool found_surface = false;
|
|
XvAdaptorInfo *adaptor_info;
|
|
unsigned int num_adaptors;
|
|
int num_types;
|
|
unsigned int max_width = 0, max_height = 0;
|
|
Status ret;
|
|
|
|
assert(dpy);
|
|
assert(found_port);
|
|
assert(screen);
|
|
assert(chroma_format);
|
|
assert(mc_type);
|
|
assert(surface_flags);
|
|
assert(subpic_max_w);
|
|
assert(subpic_max_h);
|
|
|
|
*found_port = false;
|
|
|
|
for (int i = 0; i < XScreenCount(dpy); ++i) {
|
|
ret = XvQueryAdaptors(dpy, XRootWindow(dpy, i), &num_adaptors, &adaptor_info);
|
|
if (ret != Success)
|
|
return ret;
|
|
|
|
for (unsigned int j = 0; j < num_adaptors && !*found_port; ++j) {
|
|
for (unsigned int k = 0; k < adaptor_info[j].num_ports && !*found_port; ++k) {
|
|
XvMCSurfaceInfo *surface_info;
|
|
|
|
if (adaptor_info[j].base_id + k != port)
|
|
continue;
|
|
|
|
*found_port = true;
|
|
|
|
surface_info = XvMCListSurfaceTypes(dpy, adaptor_info[j].base_id, &num_types);
|
|
if (!surface_info) {
|
|
XvFreeAdaptorInfo(adaptor_info);
|
|
return BadAlloc;
|
|
}
|
|
|
|
for (int l = 0; l < num_types && !found_surface; ++l) {
|
|
if (surface_info[l].surface_type_id != surface_type_id)
|
|
continue;
|
|
|
|
found_surface = true;
|
|
max_width = surface_info[l].max_width;
|
|
max_height = surface_info[l].max_height;
|
|
*chroma_format = surface_info[l].chroma_format;
|
|
*mc_type = surface_info[l].mc_type;
|
|
*surface_flags = surface_info[l].flags;
|
|
*subpic_max_w = surface_info[l].subpicture_max_width;
|
|
*subpic_max_h = surface_info[l].subpicture_max_height;
|
|
*screen = i;
|
|
|
|
XVMC_MSG(XVMC_TRACE, "[XvMC] Found requested context surface format.\n" \
|
|
"[XvMC] screen=%u, port=%u\n" \
|
|
"[XvMC] id=0x%08X\n" \
|
|
"[XvMC] max width=%u, max height=%u\n" \
|
|
"[XvMC] chroma format=0x%08X\n" \
|
|
"[XvMC] acceleration level=0x%08X\n" \
|
|
"[XvMC] flags=0x%08X\n" \
|
|
"[XvMC] subpicture max width=%u, max height=%u\n",
|
|
i, port, surface_type_id, max_width, max_height, *chroma_format,
|
|
*mc_type, *surface_flags, *subpic_max_w, *subpic_max_h);
|
|
}
|
|
|
|
free(surface_info);
|
|
}
|
|
}
|
|
|
|
XvFreeAdaptorInfo(adaptor_info);
|
|
}
|
|
|
|
if (!*found_port) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not find a suitable port.\n");
|
|
return XvBadPort;
|
|
}
|
|
if (!found_surface) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not find a suitable surface.\n");
|
|
return BadMatch;
|
|
}
|
|
if (width > max_width || height > max_height) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Requested context dimensions (w=%u,h=%u) too large (max w=%u,h=%u).\n",
|
|
width, height, max_width, max_height);
|
|
return BadValue;
|
|
}
|
|
if (flags != XVMC_DIRECT && flags != 0) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Invalid context flags 0x%08X.\n", flags);
|
|
return BadValue;
|
|
}
|
|
|
|
return Success;
|
|
}
|
|
|
|
static enum pipe_video_profile ProfileToPipe(int xvmc_profile)
|
|
{
|
|
if (xvmc_profile & XVMC_MPEG_1)
|
|
assert(0);
|
|
if (xvmc_profile & XVMC_MPEG_2)
|
|
return PIPE_VIDEO_PROFILE_MPEG2_MAIN;
|
|
if (xvmc_profile & XVMC_H263)
|
|
assert(0);
|
|
if (xvmc_profile & XVMC_MPEG_4)
|
|
assert(0);
|
|
|
|
assert(0);
|
|
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized profile 0x%08X.\n", xvmc_profile);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static enum pipe_video_chroma_format FormatToPipe(int xvmc_format)
|
|
{
|
|
switch (xvmc_format) {
|
|
case XVMC_CHROMA_FORMAT_420:
|
|
return PIPE_VIDEO_CHROMA_FORMAT_420;
|
|
case XVMC_CHROMA_FORMAT_422:
|
|
return PIPE_VIDEO_CHROMA_FORMAT_422;
|
|
case XVMC_CHROMA_FORMAT_444:
|
|
return PIPE_VIDEO_CHROMA_FORMAT_444;
|
|
default:
|
|
assert(0);
|
|
}
|
|
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Unrecognized format 0x%08X.\n", xvmc_format);
|
|
|
|
return -1;
|
|
}
|
|
|
|
PUBLIC
|
|
Status XvMCCreateContext(Display *dpy, XvPortID port, int surface_type_id,
|
|
int width, int height, int flags, XvMCContext *context)
|
|
{
|
|
bool found_port;
|
|
int scrn = 0;
|
|
int chroma_format = 0;
|
|
int mc_type = 0;
|
|
int surface_flags = 0;
|
|
unsigned short subpic_max_w = 0;
|
|
unsigned short subpic_max_h = 0;
|
|
Status ret;
|
|
struct vl_screen *vscreen;
|
|
struct pipe_context *pipe;
|
|
struct pipe_video_codec templat = {0};
|
|
XvMCContextPrivate *context_priv;
|
|
vl_csc_matrix csc;
|
|
|
|
XVMC_MSG(XVMC_TRACE, "[XvMC] Creating context %p.\n", context);
|
|
|
|
assert(dpy);
|
|
|
|
if (!context)
|
|
return XvMCBadContext;
|
|
|
|
ret = Validate(dpy, port, surface_type_id, width, height, flags,
|
|
&found_port, &scrn, &chroma_format, &mc_type, &surface_flags,
|
|
&subpic_max_w, &subpic_max_h);
|
|
|
|
/* Success and XvBadPort have the same value */
|
|
if (ret != Success || !found_port)
|
|
return ret;
|
|
|
|
/* XXX: Current limits */
|
|
if (chroma_format != XVMC_CHROMA_FORMAT_420) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Unsupported chroma format.\n");
|
|
return BadImplementation;
|
|
}
|
|
if ((mc_type & ~XVMC_IDCT) != (XVMC_MOCOMP | XVMC_MPEG_2)) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Non-MPEG2/Mocomp/iDCT acceleration unsupported.\n");
|
|
return BadImplementation;
|
|
}
|
|
if (surface_flags & XVMC_INTRA_UNSIGNED) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Cannot decode requested surface type. Unsigned intra unsupported.\n");
|
|
return BadImplementation;
|
|
}
|
|
|
|
context_priv = CALLOC(1, sizeof(XvMCContextPrivate));
|
|
if (!context_priv)
|
|
return BadAlloc;
|
|
|
|
/* TODO: Reuse screen if process creates another context */
|
|
vscreen = vl_dri3_screen_create(dpy, scrn);
|
|
if (!vscreen)
|
|
vscreen = vl_dri2_screen_create(dpy, scrn);
|
|
|
|
if (!vscreen) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL screen.\n");
|
|
FREE(context_priv);
|
|
return BadAlloc;
|
|
}
|
|
|
|
pipe = vscreen->pscreen->context_create(vscreen->pscreen, NULL, 0);
|
|
if (!pipe) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL context.\n");
|
|
vscreen->destroy(vscreen);
|
|
FREE(context_priv);
|
|
return BadAlloc;
|
|
}
|
|
|
|
templat.profile = ProfileToPipe(mc_type);
|
|
templat.entrypoint = (mc_type & XVMC_IDCT) ? PIPE_VIDEO_ENTRYPOINT_IDCT : PIPE_VIDEO_ENTRYPOINT_MC;
|
|
templat.chroma_format = FormatToPipe(chroma_format);
|
|
templat.width = width;
|
|
templat.height = height;
|
|
templat.max_references = 2;
|
|
templat.expect_chunked_decode = true;
|
|
|
|
context_priv->decoder = pipe->create_video_codec(pipe, &templat);
|
|
|
|
if (!context_priv->decoder) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL decoder.\n");
|
|
pipe->destroy(pipe);
|
|
vscreen->destroy(vscreen);
|
|
FREE(context_priv);
|
|
return BadAlloc;
|
|
}
|
|
|
|
if (!vl_compositor_init(&context_priv->compositor, pipe)) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL compositor.\n");
|
|
context_priv->decoder->destroy(context_priv->decoder);
|
|
pipe->destroy(pipe);
|
|
vscreen->destroy(vscreen);
|
|
FREE(context_priv);
|
|
return BadAlloc;
|
|
}
|
|
|
|
if (!vl_compositor_init_state(&context_priv->cstate, pipe)) {
|
|
XVMC_MSG(XVMC_ERR, "[XvMC] Could not create VL compositor state.\n");
|
|
vl_compositor_cleanup(&context_priv->compositor);
|
|
context_priv->decoder->destroy(context_priv->decoder);
|
|
pipe->destroy(pipe);
|
|
vscreen->destroy(vscreen);
|
|
FREE(context_priv);
|
|
return BadAlloc;
|
|
}
|
|
|
|
|
|
context_priv->color_standard =
|
|
debug_get_bool_option("G3DVL_NO_CSC", FALSE) ?
|
|
VL_CSC_COLOR_STANDARD_IDENTITY : VL_CSC_COLOR_STANDARD_BT_601;
|
|
context_priv->procamp = vl_default_procamp;
|
|
|
|
vl_csc_get_matrix
|
|
(
|
|
context_priv->color_standard,
|
|
&context_priv->procamp, true, &csc
|
|
);
|
|
vl_compositor_set_csc_matrix(&context_priv->cstate, (const vl_csc_matrix *)&csc, 1.0f, 0.0f);
|
|
|
|
context_priv->vscreen = vscreen;
|
|
context_priv->pipe = pipe;
|
|
context_priv->subpicture_max_width = subpic_max_w;
|
|
context_priv->subpicture_max_height = subpic_max_h;
|
|
|
|
context->context_id = XAllocID(dpy);
|
|
context->surface_type_id = surface_type_id;
|
|
context->width = width;
|
|
context->height = height;
|
|
context->flags = flags;
|
|
context->port = port;
|
|
context->privData = context_priv;
|
|
|
|
SyncHandle();
|
|
|
|
XVMC_MSG(XVMC_TRACE, "[XvMC] Context %p created.\n", context);
|
|
|
|
return Success;
|
|
}
|
|
|
|
PUBLIC
|
|
Status XvMCDestroyContext(Display *dpy, XvMCContext *context)
|
|
{
|
|
XvMCContextPrivate *context_priv;
|
|
|
|
XVMC_MSG(XVMC_TRACE, "[XvMC] Destroying context %p.\n", context);
|
|
|
|
assert(dpy);
|
|
|
|
if (!context || !context->privData)
|
|
return XvMCBadContext;
|
|
|
|
context_priv = context->privData;
|
|
context_priv->decoder->destroy(context_priv->decoder);
|
|
vl_compositor_cleanup_state(&context_priv->cstate);
|
|
vl_compositor_cleanup(&context_priv->compositor);
|
|
context_priv->pipe->destroy(context_priv->pipe);
|
|
context_priv->vscreen->destroy(context_priv->vscreen);
|
|
FREE(context_priv);
|
|
context->privData = NULL;
|
|
|
|
XVMC_MSG(XVMC_TRACE, "[XvMC] Context %p destroyed.\n", context);
|
|
|
|
return Success;
|
|
}
|