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:
dudemanguy 2019-10-10 14:14:40 -05:00 committed by Dudemanguy
parent 3568aed164
commit bedca07a02
6 changed files with 298 additions and 7 deletions

View File

@ -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))

View File

@ -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);
};

View File

@ -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;

View File

@ -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);

View File

@ -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 */

View File

@ -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" ),