summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--video/out/opengl/context_wayland.c80
-rw-r--r--video/out/vulkan/context.h3
-rw-r--r--video/out/vulkan/context_wayland.c81
-rw-r--r--video/out/wayland_common.c106
-rw-r--r--video/out/wayland_common.h28
-rw-r--r--wscript_build.py7
6 files changed, 298 insertions, 7 deletions
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 6511e34499..5e9150eb0d 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -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))
diff --git a/video/out/vulkan/context.h b/video/out/vulkan/context.h
index 90d7f8b8b2..0b420e2daa 100644
--- a/video/out/vulkan/context.h
+++ b/video/out/vulkan/context.h
@@ -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);
};
diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c
index 783509e4a1..d3da628137 100644
--- a/video/out/vulkan/context_wayland.c
+++ b/video/out/vulkan/context_wayland.c
@@ -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;
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index fa409a5541..f66753018b 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -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);
diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h
index 1415c6935d..606d9ed218 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -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 */
diff --git a/wscript_build.py b/wscript_build.py
index 63495689fb..9f47153413 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -130,6 +130,12 @@ def build(ctx):
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")
ctx.wayland_protocol_header(proto_dir = ctx.env.WL_PROTO_DIR,
@@ -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" ),