From 5cca9143abdbf413641dc8a657ca869a9425b5a9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 5 Nov 2013 22:06:32 +0100 Subject: vdpau: move device and video surface management from vo_vdpau.c to vdpau.c The goal is being able to use vdpau decoding independently from vo_vdpau.c. --- video/out/vo_vdpau.c | 248 +++++++-------------------------------------------- video/vdpau.c | 216 +++++++++++++++++++++++++++++++++++++++++++- video/vdpau.h | 27 +++++- 3 files changed, 271 insertions(+), 220 deletions(-) (limited to 'video') diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c index 033f06cb19..8181a645f2 100644 --- a/video/out/vo_vdpau.c +++ b/video/out/vo_vdpau.c @@ -57,7 +57,6 @@ /* number of video and output surfaces */ #define MAX_OUTPUT_SURFACES 15 -#define MAX_VIDEO_SURFACES 50 #define NUM_BUFFERED_VIDEO 5 /* Pixelformat used for output surfaces */ @@ -68,17 +67,13 @@ */ struct vdpctx { - struct mp_vdpau_ctx mpvdp; + struct mp_vdpau_ctx *mpvdp; struct vdp_functions *vdp; VdpDevice vdp_device; + uint64_t preemption_counter; struct m_color colorkey; - bool is_preempted; - bool preemption_acked; - bool preemption_user_notified; - double last_preemption_retry_fail; - VdpPresentationQueueTarget flip_target; VdpPresentationQueue flip_queue; uint64_t last_vdp_time; @@ -123,14 +118,6 @@ struct vdpctx { VdpRect out_rect_vid; struct mp_osd_res osd_rect; - // Surface pool - struct surface_entry { - VdpVideoSurface surface; - int fmt, w, h; - VdpChromaType chroma; - bool in_use; - } video_surfaces[MAX_VIDEO_SURFACES]; - int surface_num; // indexes output_surfaces int query_surface_num; VdpTime recent_vsync_time; @@ -450,66 +437,6 @@ static void resize(struct vo *vo) vo->want_redraw = true; } -static void preemption_callback(VdpDevice device, void *context) -{ - struct vdpctx *vc = context; - vc->is_preempted = true; - vc->mpvdp.is_preempted = true; - vc->preemption_acked = false; -} - -static int win_x11_init_vdpau_procs(struct vo *vo) -{ - struct vo_x11_state *x11 = vo->x11; - struct vdpctx *vc = vo->priv; - struct vdp_functions *vdp = vc->vdp; - VdpStatus vdp_st; - - *vdp = (struct vdp_functions){0}; - - struct vdp_function { - const int id; - int offset; - }; - - const struct vdp_function *dsc; - - static const struct vdp_function vdp_func[] = { -#define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)}, -#include "video/vdpau_functions.inc" -#undef VDP_FUNCTION - {0, -1} - }; - - VdpGetProcAddress *get_proc_address; - vdp_st = vdp_device_create_x11(x11->display, x11->screen, &vc->vdp_device, - &get_proc_address); - vc->mpvdp.vdp_device = vc->vdp_device; - if (vdp_st != VDP_STATUS_OK) { - if (vc->is_preempted) - MP_DBG(vo, "Error calling vdp_device_create_x11 while preempted: %d\n", - vdp_st); - else - MP_ERR(vo, "Error when calling vdp_device_create_x11: %d\n", vdp_st); - return -1; - } - - vdp->get_error_string = NULL; - for (dsc = vdp_func; dsc->offset >= 0; dsc++) { - vdp_st = get_proc_address(vc->vdp_device, dsc->id, - (void **)((char *)vdp + dsc->offset)); - if (vdp_st != VDP_STATUS_OK) { - MP_ERR(vo, "Error when calling vdp_get_proc_address(function " - "id %d): %s\n", dsc->id, - vdp->get_error_string ? vdp->get_error_string(vdp_st) : "?"); - return -1; - } - } - vdp_st = vdp->preemption_callback_register(vc->vdp_device, - preemption_callback, vc); - return 0; -} - static int win_x11_init_vdpau_flip_queue(struct vo *vo) { struct vdpctx *vc = vo->priv; @@ -535,7 +462,7 @@ static int win_x11_init_vdpau_flip_queue(struct vo *vo) if (vc->flip_queue == VDP_INVALID_HANDLE) { vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target, &vc->flip_queue); - if (vc->is_preempted && vdp_st != VDP_STATUS_OK) { + if (vc->mpvdp->is_preempted && vdp_st != VDP_STATUS_OK) { MP_DBG(vo, "Failed to create flip queue while preempted: %s\n", vdp->get_error_string(vdp_st)); return -1; @@ -800,8 +727,6 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo) { struct vdpctx *vc = vo->priv; - for (int i = 0; i < MAX_VIDEO_SURFACES; i++) - vc->video_surfaces[i].surface = VDP_INVALID_HANDLE; for (int i = 0; i < NUM_BUFFERED_VIDEO; i++) vc->rgb_surfaces[i] = VDP_INVALID_HANDLE; forget_frames(vo, false); @@ -813,7 +738,6 @@ static void mark_vdpau_objects_uninitialized(struct vo *vo) vc->output_surfaces[i] = VDP_INVALID_HANDLE; vc->screenshot_surface = VDP_INVALID_HANDLE; vc->vdp_device = VDP_INVALID_HANDLE; - vc->mpvdp.vdp_device = vc->vdp_device; for (int i = 0; i < MAX_OSD_PARTS; i++) { struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i]; talloc_free(sfc->packer); @@ -829,30 +753,22 @@ static int handle_preemption(struct vo *vo) { struct vdpctx *vc = vo->priv; - if (!vc->is_preempted) - return 0; - if (!vc->preemption_acked) + if (!mp_vdpau_status_ok(vc->mpvdp)) { mark_vdpau_objects_uninitialized(vo); - vc->preemption_acked = true; - if (!vc->preemption_user_notified) { - MP_ERR(vo, "Got display preemption notice! Will attempt to recover.\n"); - vc->preemption_user_notified = true; - } - /* Trying to initialize seems to be quite slow, so only try once a - * second to avoid using 100% CPU. */ - if (vc->last_preemption_retry_fail && - mp_time_sec() - vc->last_preemption_retry_fail < 1.0) - return -1; - if (win_x11_init_vdpau_procs(vo) < 0 || initialize_vdpau_objects(vo) < 0) { - vc->last_preemption_retry_fail = mp_time_sec(); return -1; } - vc->last_preemption_retry_fail = 0; - vc->is_preempted = false; - vc->mpvdp.is_preempted = false; - vc->mpvdp.preemption_counter++; - vc->preemption_user_notified = false; - MP_INFO(vo, "Recovered from display preemption.\n"); + + if (vc->preemption_counter == vc->mpvdp->preemption_counter) + return 0; + + mark_vdpau_objects_uninitialized(vo); + + vc->preemption_counter = vc->mpvdp->preemption_counter; + vc->vdp_device = vc->mpvdp->vdp_device; + + if (initialize_vdpau_objects(vo) < 0) + return -1; + return 1; } @@ -861,12 +777,6 @@ static bool status_ok(struct vo *vo) return vo->config_ok && handle_preemption(vo) >= 0; } -static bool ctx_status_ok(struct mp_vdpau_ctx *ctx) -{ - struct vo *vo = ctx->priv; - return handle_preemption(vo) >= 0; -} - /* * connect to X server, create and map window, initialize all * VDPAU objects, create different surfaces etc. @@ -1223,83 +1133,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 release_decoder_surface(void *ptr) -{ - bool *in_use_ptr = ptr; - *in_use_ptr = false; -} - -static struct mp_image *create_ref(struct surface_entry *e) -{ - assert(!e->in_use); - e->in_use = true; - struct mp_image *res = - mp_image_new_custom_ref(&(struct mp_image){0}, &e->in_use, - release_decoder_surface); - mp_image_setfmt(res, e->fmt); - 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; - return res; -} - -static struct mp_image *get_video_surface(struct mp_vdpau_ctx *ctx, int fmt, - VdpChromaType chroma, int w, int h) -{ - struct vo *vo = ctx->priv; - struct vdpctx *vc = vo->priv; - struct vdp_functions *vdp = vc->vdp; - VdpStatus vdp_st; - - assert(IMGFMT_IS_VDPAU(fmt)); - - // Destroy all unused surfaces that don't have matching parameters - for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { - struct surface_entry *e = &vc->video_surfaces[n]; - if (!e->in_use && e->surface != VDP_INVALID_HANDLE) { - if (e->fmt != fmt || e->chroma != chroma || e->w != w || e->h != h) { - vdp_st = vdp->video_surface_destroy(e->surface); - CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy"); - e->surface = VDP_INVALID_HANDLE; - } - } - } - - // Try to find an existing unused surface - for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { - struct surface_entry *e = &vc->video_surfaces[n]; - if (!e->in_use && e->surface != VDP_INVALID_HANDLE) { - assert(e->w == w && e->h == h); - assert(e->fmt == fmt && e->chroma == chroma); - return create_ref(e); - } - } - - // Allocate new surface - for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { - struct surface_entry *e = &vc->video_surfaces[n]; - if (!e->in_use) { - assert(e->surface == VDP_INVALID_HANDLE); - e->fmt = fmt; - e->chroma = chroma; - e->w = w; - e->h = h; - if (vc->is_preempted) { - MP_WARN(vo, "Preempted, no surface.\n"); - } else { - vdp_st = vdp->video_surface_create(vc->vdp_device, chroma, - w, h, &e->surface); - CHECK_ST_WARNING("Error when calling vdp_video_surface_create"); - } - return create_ref(e); - } - } - - MP_ERR(vo, "no surfaces available in get_video_surface\n"); - // TODO: this probably breaks things forever, provide a dummy buffer? - return NULL; -} - static VdpOutputSurface get_rgb_surface(struct vo *vo) { struct vdpctx *vc = vo->priv; @@ -1350,8 +1183,9 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) "output_surface_put_bits_native"); } } else { - reserved_mpi = get_video_surface(&vc->mpvdp, IMGFMT_VDPAU, - vc->vdp_chroma_type, mpi->w, mpi->h); + reserved_mpi = mp_vdpau_get_video_surface(vc->mpvdp, IMGFMT_VDPAU, + vc->vdp_chroma_type, + mpi->w, mpi->h); if (!reserved_mpi) return; surface = (VdpVideoSurface)(intptr_t)reserved_mpi->planes[3]; @@ -1456,15 +1290,6 @@ static void destroy_vdpau_objects(struct vo *vo) free_video_specific(vo); - for (int i = 0; i < MAX_VIDEO_SURFACES; i++) { - // can't hold references past VO lifetime - assert(!vc->video_surfaces[i].in_use); - if (vc->video_surfaces[i].surface != VDP_INVALID_HANDLE) { - vdp_st = vdp->video_surface_destroy(vc->video_surfaces[i].surface); - CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy"); - } - } - if (vc->flip_queue != VDP_INVALID_HANDLE) { vdp_st = vdp->presentation_queue_destroy(vc->flip_queue); CHECK_ST_WARNING("Error when calling vdp_presentation_queue_destroy"); @@ -1491,8 +1316,8 @@ static void destroy_vdpau_objects(struct vo *vo) } } - vdp_st = vdp->device_destroy(vc->vdp_device); - CHECK_ST_WARNING("Error when calling vdp_device_destroy"); + mp_vdpau_destroy(vc->mpvdp); + vc->mpvdp = NULL; } static void uninit(struct vo *vo) @@ -1507,16 +1332,23 @@ static int preinit(struct vo *vo) { struct vdpctx *vc = vo->priv; - vc->vdp = talloc_zero(vc, struct vdp_functions); - vc->mpvdp.priv = vo; - vc->mpvdp.vdp = vc->vdp; - vc->mpvdp.status_ok = ctx_status_ok; - vc->mpvdp.get_video_surface = get_video_surface; + if (!vo_x11_init(vo)) + return -1; + + vc->mpvdp = mp_vdpau_create_device_x11(vo->log, vo->x11); + if (!vc->mpvdp) { + vo_x11_uninit(vo); + return -1; + } // Mark everything as invalid first so uninit() can tell what has been // allocated mark_vdpau_objects_uninitialized(vo); + vc->preemption_counter = vc->mpvdp->preemption_counter; + vc->vdp_device = vc->mpvdp->vdp_device; + vc->vdp = vc->mpvdp->vdp; + vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS; vc->video_eq.capabilities = MP_CSP_EQ_CAPS_COLORMATRIX; @@ -1524,18 +1356,6 @@ static int preinit(struct vo *vo) if (vc->deint < 0) vc->deint = 0; - if (!vo_x11_init(vo)) - return -1; - - // After this calling uninit() should work to free resources - - if (win_x11_init_vdpau_procs(vo) < 0) { - if (vc->vdp->device_destroy) - vc->vdp->device_destroy(vc->vdp_device); - vo_x11_uninit(vo); - return -1; - } - return 0; } @@ -1611,7 +1431,7 @@ static int control(struct vo *vo, uint32_t request, void *data) return true; case VOCTRL_GET_HWDEC_INFO: { struct mp_hwdec_info *arg = data; - arg->vdpau_ctx = &vc->mpvdp; + arg->vdpau_ctx = vc->mpvdp; return true; } case VOCTRL_GET_PANSCAN: diff --git a/video/vdpau.c b/video/vdpau.c index 697bda8447..a9991e2b12 100644 --- a/video/vdpau.c +++ b/video/vdpau.c @@ -15,21 +15,233 @@ * with mpv. If not, see . */ +#include + #include "vdpau.h" +#include "osdep/timer.h" + +#include "video/out/x11_common.h" #include "video/img_format.h" +#include "video/mp_image.h" + +static void mark_vdpau_objects_uninitialized(struct mp_vdpau_ctx *ctx) +{ + for (int i = 0; i < MAX_VIDEO_SURFACES; i++) + ctx->video_surfaces[i].surface = VDP_INVALID_HANDLE; + ctx->vdp_device = VDP_INVALID_HANDLE; +} + +static void preemption_callback(VdpDevice device, void *context) +{ + struct mp_vdpau_ctx *ctx = context; + ctx->is_preempted = true; +} + +static int win_x11_init_vdpau_procs(struct mp_vdpau_ctx *ctx) +{ + struct vo_x11_state *x11 = ctx->x11; + VdpStatus vdp_st; + + // Don't operate on ctx->vdp directly, so that even if init fails, ctx->vdp + // will have the function pointers from the previous successful init, and + // won't randomly make other code crash on calling NULL pointers. + struct vdp_functions vdp = {0}; + + if (!x11) + return -1; + + struct vdp_function { + const int id; + int offset; + }; + + static const struct vdp_function vdp_func[] = { +#define VDP_FUNCTION(_, macro_name, mp_name) {macro_name, offsetof(struct vdp_functions, mp_name)}, +#include "video/vdpau_functions.inc" +#undef VDP_FUNCTION + {0, -1} + }; + + VdpGetProcAddress *get_proc_address; + vdp_st = vdp_device_create_x11(x11->display, x11->screen, &ctx->vdp_device, + &get_proc_address); + if (vdp_st != VDP_STATUS_OK) { + if (ctx->is_preempted) + MP_DBG(ctx, "Error calling vdp_device_create_x11 while preempted: %d\n", + vdp_st); + else + MP_ERR(ctx, "Error when calling vdp_device_create_x11: %d\n", vdp_st); + return -1; + } + + for (const struct vdp_function *dsc = vdp_func; dsc->offset >= 0; dsc++) { + vdp_st = get_proc_address(ctx->vdp_device, dsc->id, + (void **)((char *)&vdp + dsc->offset)); + if (vdp_st != VDP_STATUS_OK) { + MP_ERR(ctx, "Error when calling vdp_get_proc_address(function " + "id %d): %s\n", dsc->id, + vdp.get_error_string ? vdp.get_error_string(vdp_st) : "?"); + return -1; + } + } + + *ctx->vdp = vdp; + ctx->get_proc_address = get_proc_address; + + vdp_st = vdp.preemption_callback_register(ctx->vdp_device, + preemption_callback, ctx); + return 0; +} + +static int handle_preemption(struct mp_vdpau_ctx *ctx) +{ + if (!ctx->is_preempted) + return 0; + mark_vdpau_objects_uninitialized(ctx); + if (!ctx->preemption_user_notified) { + MP_ERR(ctx, "Got display preemption notice! Will attempt to recover.\n"); + ctx->preemption_user_notified = true; + } + /* Trying to initialize seems to be quite slow, so only try once a + * second to avoid using 100% CPU. */ + if (ctx->last_preemption_retry_fail && + mp_time_sec() - ctx->last_preemption_retry_fail < 1.0) + return -1; + if (win_x11_init_vdpau_procs(ctx) < 0) { + ctx->last_preemption_retry_fail = mp_time_sec(); + return -1; + } + ctx->preemption_user_notified = false; + ctx->last_preemption_retry_fail = 0; + ctx->is_preempted = false; + ctx->preemption_counter++; + MP_INFO(ctx, "Recovered from display preemption.\n"); + return 1; +} // Check whether vdpau initialization and preemption status is ok and we can // proceed normally. bool mp_vdpau_status_ok(struct mp_vdpau_ctx *ctx) { - return ctx->status_ok(ctx); + return handle_preemption(ctx) >= 0; +} + +static void release_decoder_surface(void *ptr) +{ + bool *in_use_ptr = ptr; + *in_use_ptr = false; +} + +static struct mp_image *create_ref(struct surface_entry *e) +{ + assert(!e->in_use); + e->in_use = true; + struct mp_image *res = + mp_image_new_custom_ref(&(struct mp_image){0}, &e->in_use, + release_decoder_surface); + mp_image_setfmt(res, e->fmt); + 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; + return res; } struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx, int fmt, VdpChromaType chroma, int w, int h) { - return ctx->get_video_surface(ctx, fmt, chroma, w, h); + struct vdp_functions *vdp = ctx->vdp; + VdpStatus vdp_st; + + assert(IMGFMT_IS_VDPAU(fmt)); + + // 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->fmt != fmt || e->chroma != chroma || e->w != w || e->h != h) { + vdp_st = vdp->video_surface_destroy(e->surface); + CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy"); + e->surface = VDP_INVALID_HANDLE; + } + } + } + + // 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) { + assert(e->w == w && e->h == h); + assert(e->fmt == fmt && e->chroma == chroma); + return create_ref(e); + } + } + + // Allocate new surface + for (int n = 0; n < MAX_VIDEO_SURFACES; n++) { + struct surface_entry *e = &ctx->video_surfaces[n]; + if (!e->in_use) { + assert(e->surface == VDP_INVALID_HANDLE); + e->fmt = fmt; + e->chroma = chroma; + e->w = w; + e->h = h; + if (ctx->is_preempted) { + MP_WARN(ctx, "Preempted, no surface.\n"); + } else { + vdp_st = vdp->video_surface_create(ctx->vdp_device, chroma, + w, h, &e->surface); + CHECK_ST_WARNING("Error when calling vdp_video_surface_create"); + } + return create_ref(e); + } + } + + MP_ERR(ctx, "no surfaces available in mp_vdpau_get_video_surface\n"); + return NULL; +} + +struct mp_vdpau_ctx *mp_vdpau_create_device_x11(struct mp_log *log, + struct vo_x11_state *x11) +{ + struct mp_vdpau_ctx *ctx = talloc_ptrtype(NULL, ctx); + *ctx = (struct mp_vdpau_ctx) { + .log = log, + .x11 = x11, + .vdp = talloc_zero(ctx, struct vdp_functions), + }; + + mark_vdpau_objects_uninitialized(ctx); + + if (win_x11_init_vdpau_procs(ctx) < 0) { + if (ctx->vdp->device_destroy) + ctx->vdp->device_destroy(ctx->vdp_device); + talloc_free(ctx); + return NULL; + } + return ctx; +} + +void mp_vdpau_destroy(struct mp_vdpau_ctx *ctx) +{ + struct vdp_functions *vdp = ctx->vdp; + VdpStatus vdp_st; + + for (int i = 0; i < MAX_VIDEO_SURFACES; i++) { + // can't hold references past context lifetime + assert(!ctx->video_surfaces[i].in_use); + if (ctx->video_surfaces[i].surface != VDP_INVALID_HANDLE) { + vdp_st = vdp->video_surface_destroy(ctx->video_surfaces[i].surface); + CHECK_ST_WARNING("Error when calling vdp_video_surface_destroy"); + } + } + + if (ctx->vdp_device != VDP_INVALID_HANDLE) { + vdp_st = vdp->device_destroy(ctx->vdp_device); + CHECK_ST_WARNING("Error when calling vdp_device_destroy"); + } + + talloc_free(ctx); } bool mp_vdpau_get_format(int imgfmt, VdpChromaType *out_chroma_type, diff --git a/video/vdpau.h b/video/vdpau.h index 79fc842971..9b67025347 100644 --- a/video/vdpau.h +++ b/video/vdpau.h @@ -31,19 +31,38 @@ struct vdp_functions { #undef VDP_FUNCTION }; + +#define MAX_VIDEO_SURFACES 50 + // Shared state. Objects created from different VdpDevices are often (always?) // incompatible to each other, so all code must use a shared VdpDevice. struct mp_vdpau_ctx { struct vdp_functions *vdp; + VdpGetProcAddress *get_proc_address; VdpDevice vdp_device; bool is_preempted; // set to true during unavailability uint64_t preemption_counter; // incremented after _restoring_ - bool (*status_ok)(struct mp_vdpau_ctx *ctx); - struct mp_image *(*get_video_surface)(struct mp_vdpau_ctx *ctx, int fmt, - VdpChromaType chroma, int w, int h); - void *priv; // for VO + + bool preemption_user_notified; + double last_preemption_retry_fail; + + struct vo_x11_state *x11; + + // Surface pool + struct surface_entry { + VdpVideoSurface surface; + int fmt, w, h; + VdpChromaType chroma; + bool in_use; + } video_surfaces[MAX_VIDEO_SURFACES]; + + struct mp_log *log; }; +struct mp_vdpau_ctx *mp_vdpau_create_device_x11(struct mp_log *log, + struct vo_x11_state *x11); +void mp_vdpau_destroy(struct mp_vdpau_ctx *ctx); + bool mp_vdpau_status_ok(struct mp_vdpau_ctx *ctx); struct mp_image *mp_vdpau_get_video_surface(struct mp_vdpau_ctx *ctx, int fmt, -- cgit v1.2.3