mirror of https://github.com/mpv-player/mpv
wayland: add presentation time
Use ust/msc/refresh values from wayland's presentation time in mpv's ra_swapchain_fns.get_vsync for the wayland contexts.
This commit is contained in:
parent
3568aed164
commit
bedca07a02
|
@ -25,6 +25,9 @@
|
|||
#include "egl_helpers.h"
|
||||
#include "utils.h"
|
||||
|
||||
// Generated from presentation-time.xml
|
||||
#include "video/out/wayland/presentation-time.h"
|
||||
|
||||
struct priv {
|
||||
GL gl;
|
||||
EGLDisplay egl_display;
|
||||
|
@ -45,13 +48,62 @@ static void frame_callback(void *data, struct wl_callback *callback, uint32_t ti
|
|||
|
||||
wl->frame_callback = wl_surface_frame(wl->surface);
|
||||
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
|
||||
wl->callback_wait = false;
|
||||
wl->frame_wait = false;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener = {
|
||||
frame_callback,
|
||||
};
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedback_listener;
|
||||
|
||||
static void feedback_sync_output(void *data, struct wp_presentation_feedback *fback,
|
||||
struct wl_output *output)
|
||||
{
|
||||
}
|
||||
|
||||
static void feedback_presented(void *data, struct wp_presentation_feedback *fback,
|
||||
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec, uint32_t refresh_nsec,
|
||||
uint32_t seq_hi, uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
wp_presentation_feedback_destroy(fback);
|
||||
vo_wayland_sync_shift(wl);
|
||||
|
||||
// Very similar to oml_sync_control, in this case we assume that every
|
||||
// time the compositor receives feedback, a buffer swap has been already
|
||||
// been performed.
|
||||
//
|
||||
// Notes:
|
||||
// - tv_sec_lo + tv_sec_hi is the equivalent of oml's ust
|
||||
// - seq_lo + seq_hi is the equivalent of oml's msc
|
||||
// - these values are updated everytime the compositor receives feedback.
|
||||
|
||||
int index = last_available_sync(wl);
|
||||
if (index < 0) {
|
||||
queue_new_sync(wl);
|
||||
index = 0;
|
||||
}
|
||||
int64_t sec = (uint64_t) tv_sec_lo + ((uint64_t) tv_sec_hi << 32);
|
||||
wl->sync[index].sbc = wl->user_sbc;
|
||||
wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
|
||||
wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
|
||||
wl->sync[index].refresh_usec = (uint64_t)refresh_nsec/1000;
|
||||
wl->sync[index].filled = true;
|
||||
}
|
||||
|
||||
static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
|
||||
{
|
||||
wp_presentation_feedback_destroy(fback);
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedback_listener = {
|
||||
feedback_sync_output,
|
||||
feedback_presented,
|
||||
feedback_discarded,
|
||||
};
|
||||
|
||||
static void resize(struct ra_ctx *ctx)
|
||||
{
|
||||
|
@ -77,9 +129,32 @@ static void wayland_egl_swap_buffers(struct ra_ctx *ctx)
|
|||
struct priv *p = ctx->priv;
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
|
||||
if (wl->presentation) {
|
||||
wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
|
||||
wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
|
||||
wl->user_sbc += 1;
|
||||
int index = last_available_sync(wl);
|
||||
if (index < 0)
|
||||
queue_new_sync(wl);
|
||||
}
|
||||
|
||||
eglSwapBuffers(p->egl_display, p->egl_surface);
|
||||
vo_wayland_wait_frame(wl);
|
||||
wl->callback_wait = true;
|
||||
|
||||
if (wl->presentation)
|
||||
wayland_sync_swap(wl);
|
||||
|
||||
wl->frame_wait = true;
|
||||
}
|
||||
|
||||
static void wayland_egl_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
|
||||
{
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
if (wl->presentation) {
|
||||
info->vsync_duration = wl->vsync_duration;
|
||||
info->skipped_vsyncs = wl->last_skipped_vsyncs;
|
||||
info->last_queue_display_time = wl->last_queue_display_time;
|
||||
}
|
||||
}
|
||||
|
||||
static bool egl_create_context(struct ra_ctx *ctx)
|
||||
|
@ -103,6 +178,7 @@ static bool egl_create_context(struct ra_ctx *ctx)
|
|||
|
||||
struct ra_gl_ctx_params params = {
|
||||
.swap_buffers = wayland_egl_swap_buffers,
|
||||
.get_vsync = wayland_egl_get_vsync,
|
||||
};
|
||||
|
||||
if (!ra_gl_ctx_init(ctx, &p->gl, params))
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include "common.h"
|
||||
|
||||
struct ra_vk_ctx_params {
|
||||
// See ra_swapchain_fns.get_vsync.
|
||||
void (*get_vsync)(struct ra_ctx *ctx, struct vo_vsync_info *info);
|
||||
|
||||
// In case something special needs to be done on the buffer swap.
|
||||
void (*swap_buffers)(struct ra_ctx *ctx);
|
||||
};
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
#include "context.h"
|
||||
#include "utils.h"
|
||||
|
||||
// Generated from presentation-time.xml
|
||||
#include "video/out/wayland/presentation-time.h"
|
||||
|
||||
struct priv {
|
||||
struct mpvk_ctx vk;
|
||||
};
|
||||
|
@ -37,19 +40,92 @@ static void frame_callback(void *data, struct wl_callback *callback, uint32_t ti
|
|||
|
||||
wl->frame_callback = wl_surface_frame(wl->surface);
|
||||
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
|
||||
wl->callback_wait = false;
|
||||
wl->frame_wait = false;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener = {
|
||||
frame_callback,
|
||||
};
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedback_listener;
|
||||
|
||||
static void feedback_sync_output(void *data, struct wp_presentation_feedback *fback,
|
||||
struct wl_output *output)
|
||||
{
|
||||
}
|
||||
|
||||
static void feedback_presented(void *data, struct wp_presentation_feedback *fback,
|
||||
uint32_t tv_sec_hi, uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec, uint32_t refresh_nsec,
|
||||
uint32_t seq_hi, uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
wp_presentation_feedback_destroy(fback);
|
||||
vo_wayland_sync_shift(wl);
|
||||
|
||||
// Very similar to oml_sync_control, in this case we assume that every
|
||||
// time the compositor receives feedback, a buffer swap has been already
|
||||
// been performed.
|
||||
//
|
||||
// Notes:
|
||||
// - tv_sec_lo + tv_sec_hi is the equivalent of oml's ust
|
||||
// - seq_lo + seq_hi is the equivalent of oml's msc
|
||||
// - these values are updated everytime the compositor receives feedback.
|
||||
|
||||
int index = last_available_sync(wl);
|
||||
if (index < 0) {
|
||||
queue_new_sync(wl);
|
||||
index = 0;
|
||||
}
|
||||
int64_t sec = (uint64_t) tv_sec_lo + ((uint64_t) tv_sec_hi << 32);
|
||||
wl->sync[index].sbc = wl->user_sbc;
|
||||
wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
|
||||
wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
|
||||
wl->sync[index].refresh_usec = (uint64_t)refresh_nsec/1000;
|
||||
wl->sync[index].filled = true;
|
||||
}
|
||||
|
||||
static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
|
||||
{
|
||||
wp_presentation_feedback_destroy(fback);
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener feedback_listener = {
|
||||
feedback_sync_output,
|
||||
feedback_presented,
|
||||
feedback_discarded,
|
||||
};
|
||||
|
||||
static void wayland_vk_swap_buffers(struct ra_ctx *ctx)
|
||||
{
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
|
||||
if (wl->presentation) {
|
||||
wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
|
||||
wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
|
||||
wl->user_sbc += 1;
|
||||
int index = last_available_sync(wl);
|
||||
if (index < 0)
|
||||
queue_new_sync(wl);
|
||||
}
|
||||
|
||||
vo_wayland_wait_frame(wl);
|
||||
wl->callback_wait = true;
|
||||
|
||||
if (wl->presentation)
|
||||
wayland_sync_swap(wl);
|
||||
|
||||
wl->frame_wait = true;
|
||||
}
|
||||
|
||||
static void wayland_vk_get_vsync(struct ra_ctx *ctx, struct vo_vsync_info *info)
|
||||
{
|
||||
struct vo_wayland_state *wl = ctx->vo->wl;
|
||||
if (wl->presentation) {
|
||||
info->vsync_duration = wl->vsync_duration;
|
||||
info->skipped_vsyncs = wl->last_skipped_vsyncs;
|
||||
info->last_queue_display_time = wl->last_queue_display_time;
|
||||
}
|
||||
}
|
||||
|
||||
static void wayland_vk_uninit(struct ra_ctx *ctx)
|
||||
|
@ -81,6 +157,7 @@ static bool wayland_vk_init(struct ra_ctx *ctx)
|
|||
|
||||
struct ra_vk_ctx_params params = {
|
||||
.swap_buffers = wayland_vk_swap_buffers,
|
||||
.get_vsync = wayland_vk_get_vsync,
|
||||
};
|
||||
|
||||
VkInstance inst = vk->vkinst->instance;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/input.h>
|
||||
#include <time.h>
|
||||
#include "common/msg.h"
|
||||
#include "input/input.h"
|
||||
#include "input/keycodes.h"
|
||||
|
@ -37,6 +38,9 @@
|
|||
// Generated from xdg-decoration-unstable-v1.xml
|
||||
#include "video/out/wayland/xdg-decoration-v1.h"
|
||||
|
||||
// Generated from presentation-time.xml
|
||||
#include "video/out/wayland/presentation-time.h"
|
||||
|
||||
static void xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
|
||||
{
|
||||
xdg_wm_base_pong(wm_base, serial);
|
||||
|
@ -796,6 +800,19 @@ static const struct wl_surface_listener surface_listener = {
|
|||
surface_handle_leave,
|
||||
};
|
||||
|
||||
static void pres_set_clockid(void *data, struct wp_presentation *pres,
|
||||
uint32_t clockid)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
|
||||
wl->presentation = pres;
|
||||
clockid = CLOCK_MONOTONIC;
|
||||
}
|
||||
|
||||
static const struct wp_presentation_listener pres_listener = {
|
||||
pres_set_clockid,
|
||||
};
|
||||
|
||||
static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id,
|
||||
const char *interface, uint32_t ver)
|
||||
{
|
||||
|
@ -844,6 +861,11 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
|
|||
wl->xdg_decoration_manager = wl_registry_bind(reg, id, &zxdg_decoration_manager_v1_interface, 1);
|
||||
}
|
||||
|
||||
if (!strcmp(interface, wp_presentation_interface.name) && found++) {
|
||||
wl->presentation = wl_registry_bind(reg, id, &wp_presentation_interface, 1);
|
||||
wp_presentation_add_listener(wl->presentation, &pres_listener, wl);
|
||||
}
|
||||
|
||||
if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) && found++) {
|
||||
wl->idle_inhibit_manager = wl_registry_bind(reg, id, &zwp_idle_inhibit_manager_v1_interface, 1);
|
||||
}
|
||||
|
@ -1056,6 +1078,16 @@ int vo_wayland_init(struct vo *vo)
|
|||
wl_data_device_manager_interface.name);
|
||||
}
|
||||
|
||||
if (wl->presentation) {
|
||||
wl->sync = talloc_zero_array(wl, struct vo_wayland_sync, 1);
|
||||
struct vo_wayland_sync sync = {0, 0, 0, 0};
|
||||
wl->sync[0] = sync;
|
||||
wl->sync_size += 1;
|
||||
} else {
|
||||
MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
|
||||
wp_presentation_interface.name);
|
||||
}
|
||||
|
||||
if (wl->xdg_decoration_manager) {
|
||||
wl->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(wl->xdg_decoration_manager, wl->xdg_toplevel);
|
||||
set_border_decorations(wl, vo->opts->border);
|
||||
|
@ -1142,6 +1174,9 @@ void vo_wayland_uninit(struct vo *vo)
|
|||
if (wl->frame_callback)
|
||||
wl_callback_destroy(wl->frame_callback);
|
||||
|
||||
if (wl->presentation)
|
||||
wp_presentation_destroy(wl->presentation);
|
||||
|
||||
if (wl->pointer)
|
||||
wl_pointer_destroy(wl->pointer);
|
||||
|
||||
|
@ -1403,6 +1438,73 @@ int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
|
|||
return VO_NOTIMPL;
|
||||
}
|
||||
|
||||
void vo_wayland_sync_shift(struct vo_wayland_state *wl)
|
||||
{
|
||||
for (int i = wl->sync_size - 1; i > 0; --i) {
|
||||
wl->sync[i] = wl->sync[i-1];
|
||||
}
|
||||
struct vo_wayland_sync sync = {0, 0, 0, 0};
|
||||
wl->sync[0] = sync;
|
||||
}
|
||||
|
||||
int last_available_sync(struct vo_wayland_state *wl)
|
||||
{
|
||||
for (int i = wl->sync_size - 1; i > -1; --i) {
|
||||
if (!wl->sync[i].filled)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void queue_new_sync(struct vo_wayland_state *wl)
|
||||
{
|
||||
wl->sync_size += 1;
|
||||
wl->sync = talloc_realloc(wl, wl->sync, struct vo_wayland_sync, wl->sync_size);
|
||||
vo_wayland_sync_shift(wl);
|
||||
wl->sync[0].sbc = wl->user_sbc;
|
||||
}
|
||||
|
||||
void wayland_sync_swap(struct vo_wayland_state *wl)
|
||||
{
|
||||
int index = wl->sync_size - 1;
|
||||
|
||||
wl->last_skipped_vsyncs = 0;
|
||||
|
||||
// If these are the same (can happen if a frame takes too long), update
|
||||
// the ust/msc/sbc based on when the next frame is expected to arrive.
|
||||
if (wl->sync[index].ust == wl->last_ust && wl->last_ust) {
|
||||
wl->sync[index].ust += wl->sync[index].refresh_usec;
|
||||
wl->sync[index].msc += 1;
|
||||
wl->sync[index].sbc += 1;
|
||||
}
|
||||
|
||||
int64_t ust_passed = wl->sync[index].ust ? wl->sync[index].ust - wl->last_ust: 0;
|
||||
wl->last_ust = wl->sync[index].ust;
|
||||
int64_t msc_passed = wl->sync[index].msc ? wl->sync[index].msc - wl->last_msc: 0;
|
||||
wl->last_msc = wl->sync[index].msc;
|
||||
int64_t sbc_passed = wl->sync[index].sbc ? wl->sync[index].sbc - wl->last_sbc: 0;
|
||||
wl->last_sbc = wl->sync[index].sbc;
|
||||
|
||||
if (msc_passed && ust_passed)
|
||||
wl->vsync_duration = ust_passed / msc_passed;
|
||||
|
||||
if (sbc_passed) {
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t now_monotonic = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
|
||||
uint64_t ust_mp_time = mp_time_us() - (now_monotonic - wl->sync[index].ust);
|
||||
wl->last_sbc_mp_time = ust_mp_time;
|
||||
}
|
||||
|
||||
if (!wl->sync[index].sbc)
|
||||
return;
|
||||
|
||||
wl->last_queue_display_time = wl->last_sbc_mp_time + sbc_passed*wl->vsync_duration;
|
||||
}
|
||||
|
||||
void vo_wayland_wakeup(struct vo *vo)
|
||||
{
|
||||
struct vo_wayland_state *wl = vo->wl;
|
||||
|
@ -1416,9 +1518,9 @@ void vo_wayland_wait_frame(struct vo_wayland_state *wl)
|
|||
};
|
||||
|
||||
double vblank_time = 1e6 / wl->current_output->refresh_rate;
|
||||
int64_t finish_time = mp_time_us() + vblank_time;
|
||||
int64_t finish_time = mp_time_us() + vblank_time + 1000;
|
||||
|
||||
while (wl->callback_wait && finish_time > mp_time_us()) {
|
||||
while (wl->frame_wait && finish_time > mp_time_us()) {
|
||||
|
||||
while (wl_display_prepare_read(wl->display) != 0)
|
||||
wl_display_dispatch_pending(wl->display);
|
||||
|
|
|
@ -25,6 +25,14 @@
|
|||
#include "vo.h"
|
||||
#include "input/event.h"
|
||||
|
||||
struct vo_wayland_sync {
|
||||
int64_t ust;
|
||||
int64_t msc;
|
||||
int64_t sbc;
|
||||
int64_t refresh_usec;
|
||||
bool filled;
|
||||
};
|
||||
|
||||
struct vo_wayland_output {
|
||||
struct vo_wayland_state *wl;
|
||||
uint32_t id;
|
||||
|
@ -56,7 +64,7 @@ struct vo_wayland_state {
|
|||
bool fullscreen;
|
||||
bool maximized;
|
||||
bool configured;
|
||||
bool callback_wait;
|
||||
bool frame_wait;
|
||||
int wakeup_pipe[2];
|
||||
int pending_vo_events;
|
||||
int mouse_x;
|
||||
|
@ -74,11 +82,25 @@ struct vo_wayland_state {
|
|||
struct xdg_wm_base *wm_base;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct wp_presentation *presentation;
|
||||
struct wp_presentation_feedback *feedback;
|
||||
struct zxdg_decoration_manager_v1 *xdg_decoration_manager;
|
||||
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
||||
struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager;
|
||||
struct zwp_idle_inhibitor_v1 *idle_inhibitor;
|
||||
|
||||
/* Presentation Feedback */
|
||||
struct vo_wayland_sync *sync;
|
||||
int sync_size;
|
||||
int64_t user_sbc;
|
||||
int64_t last_ust;
|
||||
int64_t last_msc;
|
||||
int64_t last_sbc;
|
||||
int64_t last_sbc_mp_time;
|
||||
int64_t vsync_duration;
|
||||
int64_t last_skipped_vsyncs;
|
||||
int64_t last_queue_display_time;
|
||||
|
||||
/* Input */
|
||||
struct wl_seat *seat;
|
||||
struct wl_pointer *pointer;
|
||||
|
@ -109,10 +131,14 @@ struct vo_wayland_state {
|
|||
int vo_wayland_init(struct vo *vo);
|
||||
int vo_wayland_reconfig(struct vo *vo);
|
||||
int vo_wayland_control(struct vo *vo, int *events, int request, void *arg);
|
||||
int last_available_sync(struct vo_wayland_state *wl);
|
||||
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);
|
||||
void wayland_sync_swap(struct vo_wayland_state *wl);
|
||||
void vo_wayland_sync_shift(struct vo_wayland_state *wl);
|
||||
void queue_new_sync(struct vo_wayland_state *wl);
|
||||
|
||||
#endif /* MPLAYER_WAYLAND_COMMON_H */
|
||||
|
|
|
@ -129,6 +129,12 @@ def build(ctx):
|
|||
ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR,
|
||||
protocol = "unstable/idle-inhibit/idle-inhibit-unstable-v1",
|
||||
target = "video/out/wayland/idle-inhibit-v1.h")
|
||||
ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR,
|
||||
protocol = "stable/presentation-time/presentation-time",
|
||||
target = "video/out/wayland/presentation-time.c")
|
||||
ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR,
|
||||
protocol = "stable/presentation-time/presentation-time",
|
||||
target = "video/out/wayland/presentation-time.h")
|
||||
ctx.wayland_protocol_code(proto_dir = ctx.env.WL_PROTO_DIR,
|
||||
protocol = "unstable/xdg-decoration/xdg-decoration-unstable-v1",
|
||||
target = "video/out/wayland/xdg-decoration-v1.c")
|
||||
|
@ -499,6 +505,7 @@ def build(ctx):
|
|||
( "video/out/vulkan/utils.c", "vulkan" ),
|
||||
( "video/out/w32_common.c", "win32-desktop" ),
|
||||
( "video/out/wayland/idle-inhibit-v1.c", "wayland" ),
|
||||
( "video/out/wayland/presentation-time.c", "wayland" ),
|
||||
( "video/out/wayland/xdg-decoration-v1.c", "wayland" ),
|
||||
( "video/out/wayland/xdg-shell.c", "wayland" ),
|
||||
( "video/out/wayland_common.c", "wayland" ),
|
||||
|
|
Loading…
Reference in New Issue