vdpau: move RGB surface management out of the VO

Integrate it with the existing surface allocator in vdpau.c. The changes
are a bit violent, because the vdpau API is so non-orthogonal: compared
to video surfaces, output surfaces use a different ID type, different
format types, and different API functions.

Also, introduce IMGFMT_VDPAU_OUTPUT for VdpOutputSurfaces wrapped in
mp_image, rather than hacking it. This is a bit cleaner.
This commit is contained in:
wm4 2014-05-22 20:55:17 +02:00
parent dbed21cde4
commit 7b7e15a460
5 changed files with 123 additions and 113 deletions

View File

@ -42,6 +42,8 @@ struct mp_imgfmt_entry {
};
static const struct mp_imgfmt_entry mp_imgfmt_list[] = {
// not in ffmpeg
FMT("vdpau_output", IMGFMT_VDPAU_OUTPUT)
// these formats are pretty common, and the "le"/"be" suffixes enforced
// by FFmpeg are annoying
FMT("yuv420p10", IMGFMT_420P10)
@ -128,12 +130,26 @@ const char *mp_imgfmt_to_name(int fmt)
return "unknown";
}
static struct mp_imgfmt_desc mp_only_imgfmt_desc(int mpfmt)
{
switch (mpfmt) {
case IMGFMT_VDPAU_OUTPUT:
return (struct mp_imgfmt_desc) {
.id = mpfmt,
.avformat = AV_PIX_FMT_NONE,
.name = mp_imgfmt_to_name(mpfmt),
.flags = MP_IMGFLAG_BE | MP_IMGFLAG_LE | MP_IMGFLAG_RGB,
};
}
return (struct mp_imgfmt_desc) {0};
}
struct mp_imgfmt_desc mp_imgfmt_get_desc(int mpfmt)
{
enum AVPixelFormat fmt = imgfmt2pixfmt(mpfmt);
const AVPixFmtDescriptor *pd = av_pix_fmt_desc_get(fmt);
if (!pd || fmt == AV_PIX_FMT_NONE)
return (struct mp_imgfmt_desc) {0};
return mp_only_imgfmt_desc(mpfmt);
struct mp_imgfmt_desc desc = {
.id = mpfmt,

View File

@ -252,7 +252,8 @@ enum mp_imgfmt {
// Hardware accelerated formats. Plane data points to special data
// structures, instead of pixel data.
IMGFMT_VDPAU,
IMGFMT_VDPAU, // VdpVideoSurface
IMGFMT_VDPAU_OUTPUT, // VdpOutputSurface
IMGFMT_VDA,
IMGFMT_VAAPI,
@ -330,7 +331,8 @@ static inline bool IMGFMT_IS_RGB(int fmt)
#define IMGFMT_RGB_DEPTH(fmt) (mp_imgfmt_get_desc(fmt).plane_bits)
#define IMGFMT_IS_HWACCEL(fmt) \
((fmt) == IMGFMT_VDPAU || (fmt) == IMGFMT_VAAPI || (fmt) == IMGFMT_VDA)
((fmt) == IMGFMT_VDPAU || (fmt) == IMGFMT_VDPAU_OUTPUT || \
(fmt) == IMGFMT_VAAPI || (fmt) == IMGFMT_VDA)
int mp_imgfmt_from_name(bstr name, bool allow_hwaccel);
const char *mp_imgfmt_to_name(int fmt);

View File

@ -60,7 +60,6 @@
/* number of video and output surfaces */
#define MAX_OUTPUT_SURFACES 15
#define NUM_BUFFERED_VIDEO 5
/* Pixelformat used for output surfaces */
#define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
@ -83,8 +82,6 @@ struct vdpctx {
VdpOutputSurface output_surfaces[MAX_OUTPUT_SURFACES];
VdpOutputSurface screenshot_surface;
int num_output_surfaces;
VdpOutputSurface rgb_surfaces[NUM_BUFFERED_VIDEO];
bool rgb_surfaces_used[NUM_BUFFERED_VIDEO];
VdpOutputSurface black_pixel;
struct mp_image *current_image;
@ -177,7 +174,7 @@ static int render_video_to_output_surface(struct vo *vo,
"vdp_presentation_queue_block_until_surface_idle");
if (vc->rgb_mode) {
VdpOutputSurface surface = (uintptr_t)mpi->planes[0];
VdpOutputSurface surface = (uintptr_t)mpi->planes[3];
int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
vdp_st = vdp->output_surface_render_output_surface(output_surface,
NULL, vc->black_pixel,
@ -373,15 +370,6 @@ static void free_video_specific(struct vo *vo)
}
vc->screenshot_surface = VDP_INVALID_HANDLE;
for (int n = 0; n < NUM_BUFFERED_VIDEO; n++) {
assert(!vc->rgb_surfaces_used[n]);
if (vc->rgb_surfaces[n] != VDP_INVALID_HANDLE) {
vdp_st = vdp->output_surface_destroy(vc->rgb_surfaces[n]);
CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
}
vc->rgb_surfaces[n] = VDP_INVALID_HANDLE;
}
if (vc->black_pixel != VDP_INVALID_HANDLE) {
vdp_st = vdp->output_surface_destroy(vc->black_pixel);
CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
@ -389,14 +377,6 @@ static void free_video_specific(struct vo *vo)
vc->black_pixel = VDP_INVALID_HANDLE;
}
static int get_rgb_format(int imgfmt)
{
switch (imgfmt) {
case IMGFMT_BGR32: return VDP_RGBA_FORMAT_B8G8R8A8;
default: return -1;
}
}
static int initialize_vdpau_objects(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
@ -412,17 +392,6 @@ static int initialize_vdpau_objects(struct vo *vo)
if (win_x11_init_vdpau_flip_queue(vo) < 0)
return -1;
if (vc->rgb_mode) {
int format = get_rgb_format(vc->image_format);
for (int n = 0; n < NUM_BUFFERED_VIDEO; n++) {
vdp_st = vdp->output_surface_create(vc->vdp_device,
format,
vc->vid_width, vc->vid_height,
&vc->rgb_surfaces[n]);
CHECK_VDP_ERROR(vo, "Allocating RGB surface");
}
}
if (vc->black_pixel == VDP_INVALID_HANDLE) {
vdp_st = vdp->output_surface_create(vc->vdp_device, OUTPUT_RGBA_FORMAT,
1, 1, &vc->black_pixel);
@ -443,8 +412,6 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
for (int i = 0; i < NUM_BUFFERED_VIDEO; i++)
vc->rgb_surfaces[i] = VDP_INVALID_HANDLE;
forget_frames(vo, false);
vc->black_pixel = VDP_INVALID_HANDLE;
vc->video_mixer->video_mixer = VDP_INVALID_HANDLE;
@ -502,7 +469,7 @@ static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
vc->vid_width = params->w;
vc->vid_height = params->h;
vc->rgb_mode = get_rgb_format(params->imgfmt) >= 0;
vc->rgb_mode = mp_vdpau_get_rgb_format(params->imgfmt, NULL);
free_video_specific(vo);
@ -872,33 +839,6 @@ static void flip_page_timed(struct vo *vo, int64_t pts_us, int duration)
vc->surface_num = WRAP_ADD(vc->surface_num, 1, vc->num_output_surfaces);
}
static void free_rgb_surface(void *ptr)
{
bool *entry = ptr;
*entry = false;
}
static struct mp_image *get_rgb_surface(struct vo *vo)
{
struct vdpctx *vc = vo->priv;
assert(vc->rgb_mode);
for (int n = 0; n < NUM_BUFFERED_VIDEO; n++) {
bool *used = &vc->rgb_surfaces_used[n];
if (!*used) {
*used = true;
struct mp_image mpi = {0};
mp_image_setfmt(&mpi, IMGFMT_VDPAU); // not really, but keep csp flags
mpi.planes[0] = (void *)(uintptr_t)vc->rgb_surfaces[n];
return mp_image_new_custom_ref(&mpi, used, free_rgb_surface);
}
}
MP_ERR(vo, "no surfaces available in get_rgb_surface\n");
return NULL;
}
static void draw_image(struct vo *vo, struct mp_image *mpi)
{
struct vdpctx *vc = vo->priv;
@ -912,29 +852,10 @@ static void draw_image(struct vo *vo, struct mp_image *mpi)
static struct mp_image *filter_image(struct vo *vo, struct mp_image *mpi)
{
struct vdpctx *vc = vo->priv;
struct vdp_functions *vdp = vc->vdp;
struct mp_image *reserved_mpi = NULL;
VdpStatus vdp_st;
check_preemption(vo);
if (vc->rgb_mode) {
reserved_mpi = get_rgb_surface(vo);
if (!reserved_mpi)
goto end;
VdpOutputSurface rgb_surface = (uintptr_t)reserved_mpi->planes[0];
if (rgb_surface != VDP_INVALID_HANDLE) {
vdp_st = vdp->output_surface_put_bits_native(rgb_surface,
&(const void *){mpi->planes[0]},
&(uint32_t){mpi->stride[0]},
NULL);
CHECK_VDP_WARNING(vo, "Error when calling "
"output_surface_put_bits_native");
}
} else {
reserved_mpi = mp_vdpau_upload_video_surface(vc->mpvdp, mpi);
}
struct mp_image *reserved_mpi = mp_vdpau_upload_video_surface(vc->mpvdp, mpi);
if (!reserved_mpi)
goto end;
@ -1014,8 +935,7 @@ static int query_format(struct vo *vo, uint32_t format)
int flags = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
if (mp_vdpau_get_format(format, NULL, NULL))
return flags;
int rgb_format = get_rgb_format(format);
if (!vc->force_yuv && rgb_format >= 0)
if (!vc->force_yuv && mp_vdpau_get_rgb_format(format, NULL))
return flags;
return 0;
}

View File

@ -28,8 +28,11 @@
static void mark_vdpau_objects_uninitialized(struct mp_vdpau_ctx *ctx)
{
for (int i = 0; i < MAX_VIDEO_SURFACES; i++)
for (int i = 0; i < MAX_VIDEO_SURFACES; i++) {
ctx->video_surfaces[i].surface = VDP_INVALID_HANDLE;
ctx->video_surfaces[i].osurface = VDP_INVALID_HANDLE;
ctx->video_surfaces[i].allocated = false;
}
ctx->vdp_device = VDP_INVALID_HANDLE;
}
@ -181,30 +184,45 @@ static struct mp_image *create_ref(struct mp_vdpau_ctx *ctx, int index)
struct mp_image *res =
mp_image_new_custom_ref(&(struct mp_image){0}, ref,
release_decoder_surface);
mp_image_setfmt(res, IMGFMT_VDPAU);
mp_image_setfmt(res, e->rgb ? IMGFMT_VDPAU_OUTPUT : IMGFMT_VDPAU);
mp_image_set_size(res, e->w, e->h);
res->planes[0] = (void *)"dummy"; // must be non-NULL, otherwise arbitrary
res->planes[3] = (void *)(intptr_t)e->surface;
res->planes[3] = (void *)(intptr_t)(e->rgb ? e->osurface : e->surface);
return res;
}
struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx,
VdpChromaType chroma, int w, int h)
static struct mp_image *mp_vdpau_get_surface(struct mp_vdpau_ctx *ctx,
VdpChromaType chroma,
VdpRGBAFormat rgb_format,
bool rgb, int w, int h)
{
struct vdp_functions *vdp = &ctx->vdp;
int surface_index = -1;
VdpStatus vdp_st;
if (rgb) {
chroma = (VdpChromaType)-1;
} else {
rgb_format = (VdpChromaType)-1;
}
pthread_mutex_lock(&ctx->pool_lock);
// Destroy all unused surfaces that don't have matching parameters
for (int n = 0; n < MAX_VIDEO_SURFACES; n++) {
struct surface_entry *e = &ctx->video_surfaces[n];
if (!e->in_use && e->surface != VDP_INVALID_HANDLE) {
if (e->chroma != chroma || e->w != w || e->h != h) {
vdp_st = vdp->video_surface_destroy(e->surface);
CHECK_VDP_WARNING(ctx, "Error when calling vdp_video_surface_destroy");
e->surface = VDP_INVALID_HANDLE;
if (!e->in_use && e->allocated) {
if (e->w != w || e->h != h || e->rgb != rgb ||
e->chroma != chroma || e->rgb_format != rgb_format)
{
if (e->rgb) {
vdp_st = vdp->output_surface_destroy(e->osurface);
} else {
vdp_st = vdp->video_surface_destroy(e->surface);
}
CHECK_VDP_WARNING(ctx, "Error when destroying surface");
e->surface = e->osurface = VDP_INVALID_HANDLE;
e->allocated = false;
}
}
}
@ -212,9 +230,11 @@ struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx,
// Try to find an existing unused surface
for (int n = 0; n < MAX_VIDEO_SURFACES; n++) {
struct surface_entry *e = &ctx->video_surfaces[n];
if (!e->in_use && e->surface != VDP_INVALID_HANDLE) {
if (!e->in_use && e->allocated) {
assert(e->w == w && e->h == h);
assert(e->chroma == chroma);
assert(e->rgb_format == rgb_format);
assert(e->rgb == rgb);
surface_index = n;
goto done;
}
@ -225,12 +245,23 @@ struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx,
struct surface_entry *e = &ctx->video_surfaces[n];
if (!e->in_use) {
assert(e->surface == VDP_INVALID_HANDLE);
assert(e->osurface == VDP_INVALID_HANDLE);
assert(!e->allocated);
e->chroma = chroma;
e->rgb_format = rgb_format;
e->rgb = rgb;
e->w = w;
e->h = h;
vdp_st = vdp->video_surface_create(ctx->vdp_device, chroma,
w, h, &e->surface);
CHECK_VDP_WARNING(ctx, "Error when calling vdp_video_surface_create");
if (rgb) {
vdp_st = vdp->output_surface_create(ctx->vdp_device, rgb_format,
w, h, &e->osurface);
e->allocated = e->osurface != VDP_INVALID_HANDLE;
} else {
vdp_st = vdp->video_surface_create(ctx->vdp_device, chroma,
w, h, &e->surface);
e->allocated = e->surface != VDP_INVALID_HANDLE;
}
CHECK_VDP_WARNING(ctx, "Error when allocating surface");
surface_index = n;
goto done;
}
@ -248,6 +279,12 @@ done: ;
return mpi;
}
struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx,
VdpChromaType chroma, int w, int h)
{
return mp_vdpau_get_surface(ctx, chroma, 0, false, w, h);
}
struct mp_vdpau_ctx *mp_vdpau_create_device_x11(struct mp_log *log,
struct vo_x11_state *x11)
{
@ -281,6 +318,10 @@ void mp_vdpau_destroy(struct mp_vdpau_ctx *ctx)
vdp_st = vdp->video_surface_destroy(ctx->video_surfaces[i].surface);
CHECK_VDP_WARNING(ctx, "Error when calling vdp_video_surface_destroy");
}
if (ctx->video_surfaces[i].osurface != VDP_INVALID_HANDLE) {
vdp_st = vdp->output_surface_destroy(ctx->video_surfaces[i].osurface);
CHECK_VDP_WARNING(ctx, "Error when calling vdp_output_surface_destroy");
}
}
if (vdp->device_destroy && ctx->vdp_device != VDP_INVALID_HANDLE) {
@ -327,6 +368,22 @@ bool mp_vdpau_get_format(int imgfmt, VdpChromaType *out_chroma_type,
return true;
}
bool mp_vdpau_get_rgb_format(int imgfmt, VdpRGBAFormat *out_rgba_format)
{
VdpRGBAFormat format = (VdpRGBAFormat)-1;
switch (imgfmt) {
case IMGFMT_BGR32:
format = VDP_RGBA_FORMAT_B8G8R8A8; break;
default:
return false;
}
if (out_rgba_format)
*out_rgba_format = format;
return true;
}
// Use mp_vdpau_get_video_surface, and upload mpi to it. Return NULL on failure.
// If the image is already a vdpau video surface, just return a reference.
struct mp_image *mp_vdpau_upload_video_surface(struct mp_vdpau_ctx *ctx,
@ -335,26 +392,36 @@ struct mp_image *mp_vdpau_upload_video_surface(struct mp_vdpau_ctx *ctx,
struct vdp_functions *vdp = &ctx->vdp;
VdpStatus vdp_st;
if (mpi->imgfmt == IMGFMT_VDPAU)
if (mpi->imgfmt == IMGFMT_VDPAU || mpi->imgfmt == IMGFMT_VDPAU_OUTPUT)
return mp_image_new_ref(mpi);
VdpChromaType chroma_type;
VdpYCbCrFormat pixel_format;
if (!mp_vdpau_get_format(mpi->imgfmt, &chroma_type, &pixel_format))
VdpChromaType chroma = (VdpChromaType)-1;
VdpYCbCrFormat ycbcr = (VdpYCbCrFormat)-1;
VdpRGBAFormat rgbafmt = (VdpRGBAFormat)-1;
bool rgb = !mp_vdpau_get_format(mpi->imgfmt, &chroma, &ycbcr);
if (rgb && !mp_vdpau_get_rgb_format(mpi->imgfmt, &rgbafmt))
return NULL;
struct mp_image *hwmpi =
mp_vdpau_get_video_surface(ctx, chroma_type, mpi->w, mpi->h);
mp_vdpau_get_surface(ctx, chroma, rgbafmt, rgb, mpi->w, mpi->h);
if (!hwmpi)
return NULL;
VdpVideoSurface surface = (intptr_t)hwmpi->planes[3];
const void *destdata[3] = {mpi->planes[0], mpi->planes[2], mpi->planes[1]};
if (mpi->imgfmt == IMGFMT_NV12)
destdata[1] = destdata[2];
vdp_st = vdp->video_surface_put_bits_y_cb_cr(surface,
pixel_format, destdata, mpi->stride);
CHECK_VDP_WARNING(ctx, "Error when calling vdp_video_surface_put_bits_y_cb_cr");
if (hwmpi->imgfmt == IMGFMT_VDPAU) {
VdpVideoSurface surface = (intptr_t)hwmpi->planes[3];
const void *destdata[3] = {mpi->planes[0], mpi->planes[2], mpi->planes[1]};
if (mpi->imgfmt == IMGFMT_NV12)
destdata[1] = destdata[2];
vdp_st = vdp->video_surface_put_bits_y_cb_cr(surface,
ycbcr, destdata, mpi->stride);
} else {
VdpOutputSurface rgb_surface = (intptr_t)hwmpi->planes[3];
vdp_st = vdp->output_surface_put_bits_native(rgb_surface,
&(const void *){mpi->planes[0]},
&(uint32_t){mpi->stride[0]},
NULL);
}
CHECK_VDP_WARNING(ctx, "Error when uploading surface");
mp_image_copy_attributes(hwmpi, mpi);
return hwmpi;

View File

@ -58,8 +58,12 @@ struct mp_vdpau_ctx {
pthread_mutex_t pool_lock;
struct surface_entry {
VdpVideoSurface surface;
VdpOutputSurface osurface;
bool allocated;
int w, h;
VdpRGBAFormat rgb_format;
VdpChromaType chroma;
bool rgb;
bool in_use;
} video_surfaces[MAX_VIDEO_SURFACES];
};
@ -75,6 +79,7 @@ struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx,
bool mp_vdpau_get_format(int imgfmt, VdpChromaType *out_chroma_type,
VdpYCbCrFormat *out_pixel_format);
bool mp_vdpau_get_rgb_format(int imgfmt, VdpRGBAFormat *out_rgba_format);
struct mp_image *mp_vdpau_upload_video_surface(struct mp_vdpau_ctx *ctx,
struct mp_image *mpi);