diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c index 22c18d8290..6511e34499 100644 --- a/video/out/opengl/context_wayland.c +++ b/video/out/opengl/context_wayland.c @@ -34,6 +34,25 @@ struct priv { struct wl_egl_window *egl_window; }; +static const struct wl_callback_listener frame_listener; + +static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) +{ + struct vo_wayland_state *wl = data; + + if (callback) + wl_callback_destroy(callback); + + wl->frame_callback = wl_surface_frame(wl->surface); + wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); + wl->callback_wait = false; +} + +static const struct wl_callback_listener frame_listener = { + frame_callback, +}; + + static void resize(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -56,7 +75,11 @@ static void resize(struct ra_ctx *ctx) static void wayland_egl_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + struct vo_wayland_state *wl = ctx->vo->wl; + eglSwapBuffers(p->egl_display, p->egl_surface); + vo_wayland_wait_frame(wl); + wl->callback_wait = true; } static bool egl_create_context(struct ra_ctx *ctx) @@ -87,6 +110,9 @@ static bool egl_create_context(struct ra_ctx *ctx) ra_add_native_resource(ctx->ra, "wl", wl->display); + wl->frame_callback = wl_surface_frame(wl->surface); + wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); + return true; } diff --git a/video/out/vulkan/context.c b/video/out/vulkan/context.c index 4ff48ed250..ea546519ec 100644 --- a/video/out/vulkan/context.c +++ b/video/out/vulkan/context.c @@ -111,6 +111,7 @@ const struct m_sub_options vulkan_conf = { struct priv { struct mpvk_ctx *vk; struct vulkan_opts *opts; + struct ra_vk_ctx_params params; const struct pl_swapchain *swapchain; struct ra_tex proxy_tex; }; @@ -147,6 +148,7 @@ void ra_vk_ctx_uninit(struct ra_ctx *ctx) } bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk, + struct ra_vk_ctx_params params, VkPresentModeKHR preferred_mode) { struct ra_swapchain *sw = ctx->swapchain = talloc_zero(NULL, struct ra_swapchain); @@ -155,6 +157,7 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk, struct priv *p = sw->priv = talloc_zero(sw, struct priv); p->vk = vk; + p->params = params; p->opts = mp_get_config_group(p, ctx->global, &vulkan_conf); assert(vk->ctx); @@ -176,16 +179,16 @@ bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk, goto error; // Create the swapchain - struct pl_vulkan_swapchain_params params = { + struct pl_vulkan_swapchain_params pl_params = { .surface = vk->surface, .present_mode = preferred_mode, .swapchain_depth = ctx->vo->opts->swapchain_depth, }; if (p->opts->swap_mode >= 0) // user override - params.present_mode = p->opts->swap_mode; + pl_params.present_mode = p->opts->swap_mode; - p->swapchain = pl_vulkan_create_swapchain(vk->vulkan, ¶ms); + p->swapchain = pl_vulkan_create_swapchain(vk->vulkan, &pl_params); if (!p->swapchain) goto error; @@ -239,6 +242,8 @@ static void swap_buffers(struct ra_swapchain *sw) { struct priv *p = sw->priv; pl_swapchain_swap_buffers(p->swapchain); + if (p->params.swap_buffers) + p->params.swap_buffers(sw->ctx); } static const struct ra_swapchain_fns vulkan_swapchain = { diff --git a/video/out/vulkan/context.h b/video/out/vulkan/context.h index 30c97cfb4f..90d7f8b8b2 100644 --- a/video/out/vulkan/context.h +++ b/video/out/vulkan/context.h @@ -3,9 +3,15 @@ #include "video/out/gpu/context.h" #include "common.h" +struct ra_vk_ctx_params { + // In case something special needs to be done on the buffer swap. + void (*swap_buffers)(struct ra_ctx *ctx); +}; + // Helpers for ra_ctx based on ra_vk. These initialize ctx->ra and ctx->swchain. void ra_vk_ctx_uninit(struct ra_ctx *ctx); bool ra_vk_ctx_init(struct ra_ctx *ctx, struct mpvk_ctx *vk, + struct ra_vk_ctx_params params, VkPresentModeKHR preferred_mode); // Handles a resize request, and updates ctx->vo->dwidth/dheight diff --git a/video/out/vulkan/context_android.c b/video/out/vulkan/context_android.c index 212fe0824b..ddab3917f1 100644 --- a/video/out/vulkan/context_android.c +++ b/video/out/vulkan/context_android.c @@ -53,6 +53,8 @@ static bool android_init(struct ra_ctx *ctx) .window = vo_android_native_window(ctx->vo) }; + struct ra_vk_ctx_params params = {0}; + VkInstance inst = vk->vkinst->instance; VkResult res = vkCreateAndroidSurfaceKHR(inst, &info, NULL, &vk->surface); if (res != VK_SUCCESS) { @@ -60,7 +62,7 @@ static bool android_init(struct ra_ctx *ctx) goto fail; } - if (!ra_vk_ctx_init(ctx, vk, VK_PRESENT_MODE_FIFO_KHR)) + if (!ra_vk_ctx_init(ctx, vk, params, VK_PRESENT_MODE_FIFO_KHR)) goto fail; return true; diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c index 160e8f9da4..783509e4a1 100644 --- a/video/out/vulkan/context_wayland.c +++ b/video/out/vulkan/context_wayland.c @@ -26,6 +26,32 @@ struct priv { struct mpvk_ctx vk; }; +static const struct wl_callback_listener frame_listener; + +static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) +{ + struct vo_wayland_state *wl = data; + + if (callback) + wl_callback_destroy(callback); + + wl->frame_callback = wl_surface_frame(wl->surface); + wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); + wl->callback_wait = false; +} + +static const struct wl_callback_listener frame_listener = { + frame_callback, +}; + +static void wayland_vk_swap_buffers(struct ra_ctx *ctx) +{ + struct vo_wayland_state *wl = ctx->vo->wl; + + vo_wayland_wait_frame(wl); + wl->callback_wait = true; +} + static void wayland_vk_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -53,6 +79,10 @@ static bool wayland_vk_init(struct ra_ctx *ctx) .surface = ctx->vo->wl->surface, }; + struct ra_vk_ctx_params params = { + .swap_buffers = wayland_vk_swap_buffers, + }; + VkInstance inst = vk->vkinst->instance; VkResult res = vkCreateWaylandSurfaceKHR(inst, &wlinfo, NULL, &vk->surface); if (res != VK_SUCCESS) { @@ -66,11 +96,14 @@ static bool wayland_vk_init(struct ra_ctx *ctx) * mean the entire player would block on acquiring swapchain images. Hence, * use MAILBOX to guarantee that there'll always be a swapchain image and * the player won't block waiting on those */ - if (!ra_vk_ctx_init(ctx, vk, VK_PRESENT_MODE_MAILBOX_KHR)) + if (!ra_vk_ctx_init(ctx, vk, params, VK_PRESENT_MODE_MAILBOX_KHR)) goto error; ra_add_native_resource(ctx->ra, "wl", ctx->vo->wl->display); + ctx->vo->wl->frame_callback = wl_surface_frame(ctx->vo->wl->surface); + wl_callback_add_listener(ctx->vo->wl->frame_callback, &frame_listener, ctx->vo->wl); + return true; error: diff --git a/video/out/vulkan/context_win.c b/video/out/vulkan/context_win.c index dfc3ba417f..a89c64414b 100644 --- a/video/out/vulkan/context_win.c +++ b/video/out/vulkan/context_win.c @@ -56,6 +56,8 @@ static bool win_init(struct ra_ctx *ctx) .hwnd = vo_w32_hwnd(ctx->vo), }; + struct ra_vk_ctx_params params = {0}; + VkInstance inst = vk->vkinst->instance; VkResult res = vkCreateWin32SurfaceKHR(inst, &wininfo, NULL, &vk->surface); if (res != VK_SUCCESS) { @@ -63,7 +65,7 @@ static bool win_init(struct ra_ctx *ctx) goto error; } - if (!ra_vk_ctx_init(ctx, vk, VK_PRESENT_MODE_FIFO_KHR)) + if (!ra_vk_ctx_init(ctx, vk, params, VK_PRESENT_MODE_FIFO_KHR)) goto error; return true; diff --git a/video/out/vulkan/context_xlib.c b/video/out/vulkan/context_xlib.c index e2fc732e00..6278bc3c69 100644 --- a/video/out/vulkan/context_xlib.c +++ b/video/out/vulkan/context_xlib.c @@ -56,6 +56,8 @@ static bool xlib_init(struct ra_ctx *ctx) .window = ctx->vo->x11->window, }; + struct ra_vk_ctx_params params = {0}; + VkInstance inst = vk->vkinst->instance; VkResult res = vkCreateXlibSurfaceKHR(inst, &xinfo, NULL, &vk->surface); if (res != VK_SUCCESS) { @@ -63,7 +65,7 @@ static bool xlib_init(struct ra_ctx *ctx) goto error; } - if (!ra_vk_ctx_init(ctx, vk, VK_PRESENT_MODE_FIFO_KHR)) + if (!ra_vk_ctx_init(ctx, vk, params, VK_PRESENT_MODE_FIFO_KHR)) goto error; ra_add_native_resource(ctx->ra, "x11", ctx->vo->x11->display); diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index cc07296a43..fa409a5541 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -796,23 +796,6 @@ static const struct wl_surface_listener surface_listener = { surface_handle_leave, }; -static const struct wl_callback_listener frame_listener; - -static void frame_callback(void *data, struct wl_callback *callback, uint32_t time) -{ - struct vo_wayland_state *wl = data; - - if (callback) - wl_callback_destroy(callback); - - wl->frame_callback = wl_surface_frame(wl->surface); - wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); -} - -static const struct wl_callback_listener frame_listener = { - frame_callback, -}; - static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { @@ -824,8 +807,6 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id wl->surface = wl_compositor_create_surface(wl->compositor); wl->cursor_surface = wl_compositor_create_surface(wl->compositor); wl_surface_add_listener(wl->surface, &surface_listener, wl); - wl->frame_callback = wl_surface_frame(wl->surface); - wl_callback_add_listener(wl->frame_callback, &frame_listener, wl); } if (!strcmp(interface, wl_output_interface.name) && (ver >= 2) && found++) { @@ -1428,6 +1409,33 @@ void vo_wayland_wakeup(struct vo *vo) (void)write(wl->wakeup_pipe[1], &(char){0}, 1); } +void vo_wayland_wait_frame(struct vo_wayland_state *wl) +{ + struct pollfd fds[1] = { + {.fd = wl->display_fd, .events = POLLIN }, + }; + + double vblank_time = 1e6 / wl->current_output->refresh_rate; + int64_t finish_time = mp_time_us() + vblank_time; + + while (wl->callback_wait && finish_time > mp_time_us()) { + + while (wl_display_prepare_read(wl->display) != 0) + wl_display_dispatch_pending(wl->display); + wl_display_flush(wl->display); + + int poll_time = (finish_time - mp_time_us()) / 1000; + if (poll_time < 0) { + poll_time = 0; + } + + poll(fds, 1, poll_time); + + wl_display_read_events(wl->display); + wl_display_dispatch_pending(wl->display); + } +} + void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us) { struct vo_wayland_state *wl = vo->wl; diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h index b9ce1c53ce..1415c6935d 100644 --- a/video/out/wayland_common.h +++ b/video/out/wayland_common.h @@ -56,6 +56,7 @@ struct vo_wayland_state { bool fullscreen; bool maximized; bool configured; + bool callback_wait; int wakeup_pipe[2]; int pending_vo_events; int mouse_x; @@ -112,5 +113,6 @@ void vo_wayland_check_events(struct vo *vo); void vo_wayland_uninit(struct vo *vo); void vo_wayland_wakeup(struct vo *vo); void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us); +void vo_wayland_wait_frame(struct vo_wayland_state *wl); #endif /* MPLAYER_WAYLAND_COMMON_H */