summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meson.build4
-rw-r--r--video/out/opengl/context_wayland.c10
-rw-r--r--video/out/present_sync.c85
-rw-r--r--video/out/present_sync.h48
-rw-r--r--video/out/vo_vaapi_wayland.c11
-rw-r--r--video/out/vo_wlshm.c10
-rw-r--r--video/out/vulkan/context_wayland.c10
-rw-r--r--video/out/wayland_common.c96
-rw-r--r--video/out/wayland_common.h8
-rw-r--r--wscript_build.py1
10 files changed, 163 insertions, 120 deletions
diff --git a/meson.build b/meson.build
index 5524828f88..018546d22a 100644
--- a/meson.build
+++ b/meson.build
@@ -1099,6 +1099,10 @@ if xv.found()
sources += files('video/out/vo_xv.c')
endif
+if wayland['use'] or x11['use']
+ sources += ('video/out/present_sync.c')
+endif
+
# OpenGL feature checking
gl = {
diff --git a/video/out/opengl/context_wayland.c b/video/out/opengl/context_wayland.c
index 4dd45c6412..b374be64c9 100644
--- a/video/out/opengl/context_wayland.c
+++ b/video/out/opengl/context_wayland.c
@@ -20,6 +20,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include "video/out/present_sync.h"
#include "video/out/wayland_common.h"
#include "context.h"
#include "egl_helpers.h"
@@ -101,17 +102,14 @@ static void wayland_egl_swap_buffers(struct ra_ctx *ctx)
vo_wayland_wait_frame(wl);
if (wl->presentation)
- vo_wayland_sync_swap(wl);
+ present_sync_swap(wl->present);
}
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;
- }
+ if (wl->presentation)
+ present_sync_get_info(wl->present, info);
}
static bool egl_create_context(struct ra_ctx *ctx)
diff --git a/video/out/present_sync.c b/video/out/present_sync.c
new file mode 100644
index 0000000000..16d7416d43
--- /dev/null
+++ b/video/out/present_sync.c
@@ -0,0 +1,85 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <time.h>
+
+#include "mpv_talloc.h"
+#include "osdep/timer.h"
+#include "present_sync.h"
+
+/* General nonsense about this mechanism.
+ *
+ * This requires that that caller has access to two, related values:
+ * (ust, msc): clock time and incrementing counter of last vsync (this is
+ * increased continuously, even if we don't swap)
+ *
+ * Note that this concept originates from the GLX_OML_sync_control extension
+ * which includes another parameter: sbc (swap counter of frame that was
+ * last displayed). Both the xorg present extension and wayland's
+ * presentation-time protocol do not include sbc values so they are omitted
+ * from this mechanism. mpv does not need to keep track of sbc calls and can
+ * have reliable presentation without it.
+ */
+
+void present_sync_get_info(struct mp_present *present, struct vo_vsync_info *info)
+{
+ info->vsync_duration = present->vsync_duration;
+ info->skipped_vsyncs = present->last_skipped_vsyncs;
+ info->last_queue_display_time = present->last_queue_display_time;
+}
+
+void present_sync_swap(struct mp_present *present)
+{
+ int64_t ust = present->current_ust;
+ int64_t msc = present->current_msc;
+
+ // Avoid attempting to use any presentation statistics if the ust is 0 or has
+ // not actually updated (i.e. the last_ust is equal to current_ust).
+ if (!ust || ust == present->last_ust) {
+ present->last_skipped_vsyncs = -1;
+ present->vsync_duration = -1;
+ present->last_queue_display_time = -1;
+ return;
+ }
+
+ present->last_skipped_vsyncs = 0;
+
+ int64_t ust_passed = ust ? ust - present->last_ust: 0;
+ present->last_ust = ust;
+ int64_t msc_passed = msc ? msc - present->last_msc: 0;
+ present->last_msc = msc;
+
+ if (msc_passed && ust_passed)
+ present->vsync_duration = ust_passed / msc_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 - ust);
+
+ present->last_queue_display_time = ust_mp_time;
+}
+
+void present_update_sync_values(struct mp_present *present, int64_t ust,
+ int64_t msc)
+{
+ present->current_ust = ust;
+ present->current_msc = msc;
+}
diff --git a/video/out/present_sync.h b/video/out/present_sync.h
new file mode 100644
index 0000000000..c310002b38
--- /dev/null
+++ b/video/out/present_sync.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of mpv video player.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef MP_PRESENT_SYNC_H
+#define MP_PRESENT_SYNC_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "vo.h"
+
+/* Generic helpers for obtaining presentation feedback from
+ * backend APIs. This requires ust/msc values. */
+
+struct mp_present {
+ int64_t current_ust;
+ int64_t current_msc;
+ int64_t last_ust;
+ int64_t last_msc;
+ int64_t vsync_duration;
+ int64_t last_skipped_vsyncs;
+ int64_t last_queue_display_time;
+};
+
+// Used during the get_vsync call to deliver the presentation statistics to the VO.
+void present_sync_get_info(struct mp_present *present, struct vo_vsync_info *info);
+
+// Called after every buffer swap to update presentation statistics.
+void present_sync_swap(struct mp_present *present);
+
+// Called anytime the backend delivers new ust/msc values.
+void present_update_sync_values(struct mp_present *present, int64_t ust,
+ int64_t msc);
+
+#endif /* MP_PRESENT_SYNC_H */
diff --git a/video/out/vo_vaapi_wayland.c b/video/out/vo_vaapi_wayland.c
index 44b438e94c..50cb71bcde 100644
--- a/video/out/vo_vaapi_wayland.c
+++ b/video/out/vo_vaapi_wayland.c
@@ -21,9 +21,11 @@
#include <va/va_wayland.h>
#include <va/va_drmcommon.h>
+#include "present_sync.h"
#include "sub/osd.h"
#include "video/vaapi.h"
#include "wayland_common.h"
+
#include "generated/wayland/linux-dmabuf-unstable-v1.h"
#include "generated/wayland/viewporter.h"
@@ -383,7 +385,7 @@ static void draw_frame(struct vo *vo, struct vo_frame *frame)
if (!wl->opts->disable_vsync)
vo_wayland_wait_frame(wl);
if (wl->presentation)
- vo_wayland_sync_swap(wl);
+ present_sync_swap(wl->present);
}
static void flip_page(struct vo *vo)
{
@@ -393,11 +395,8 @@ static void flip_page(struct vo *vo)
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
{
struct vo_wayland_state *wl = 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;
- }
+ if (wl->presentation)
+ present_sync_get_info(wl->present, info);
}
const struct vo_driver video_out_vaapi_wayland = {
diff --git a/video/out/vo_wlshm.c b/video/out/vo_wlshm.c
index 2ed20176be..b8db2ebd4e 100644
--- a/video/out/vo_wlshm.c
+++ b/video/out/vo_wlshm.c
@@ -24,6 +24,7 @@
#include <libswscale/swscale.h>
#include "osdep/endian.h"
+#include "present_sync.h"
#include "sub/osd.h"
#include "video/fmt-conversion.h"
#include "video/mp_image.h"
@@ -262,17 +263,14 @@ static void flip_page(struct vo *vo)
vo_wayland_wait_frame(wl);
if (wl->presentation)
- vo_wayland_sync_swap(wl);
+ present_sync_swap(wl->present);
}
static void get_vsync(struct vo *vo, struct vo_vsync_info *info)
{
struct vo_wayland_state *wl = 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;
- }
+ if (wl->presentation)
+ present_sync_get_info(wl->present, info);
}
static void uninit(struct vo *vo)
diff --git a/video/out/vulkan/context_wayland.c b/video/out/vulkan/context_wayland.c
index ab54789890..7bc26ab1c6 100644
--- a/video/out/vulkan/context_wayland.c
+++ b/video/out/vulkan/context_wayland.c
@@ -16,6 +16,7 @@
*/
#include "video/out/gpu/context.h"
+#include "video/out/present_sync.h"
#include "video/out/wayland_common.h"
#include "common.h"
@@ -39,17 +40,14 @@ static void wayland_vk_swap_buffers(struct ra_ctx *ctx)
vo_wayland_wait_frame(wl);
if (wl->presentation)
- vo_wayland_sync_swap(wl);
+ present_sync_swap(wl->present);
}
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;
- }
+ if (wl->presentation)
+ present_sync_get_info(wl->present, info);
}
static void wayland_vk_uninit(struct ra_ctx *ctx)
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c
index 698cec1ec8..c705f6e165 100644
--- a/video/out/wayland_common.c
+++ b/video/out/wayland_common.c
@@ -30,6 +30,7 @@
#include "options/m_config.h"
#include "osdep/io.h"
#include "osdep/timer.h"
+#include "present_sync.h"
#include "wayland_common.h"
#include "win_state.h"
@@ -143,28 +144,18 @@ struct vo_wayland_output {
struct wl_list link;
};
-struct vo_wayland_sync {
- int64_t ust;
- int64_t msc;
- int64_t sbc;
- bool filled;
-};
-
static int check_for_resize(struct vo_wayland_state *wl, wl_fixed_t x_w, wl_fixed_t y_w,
int edge_pixels, enum xdg_toplevel_resize_edge *edge);
static int get_mods(struct vo_wayland_state *wl);
-static int last_available_sync(struct vo_wayland_state *wl);
static int lookupkey(int key);
static int set_cursor_visibility(struct vo_wayland_state *wl, bool on);
static int spawn_cursor(struct vo_wayland_state *wl);
static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b);
-static void queue_new_sync(struct vo_wayland_state *wl);
static void remove_output(struct vo_wayland_output *out);
static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode);
static void set_geometry(struct vo_wayland_state *wl);
static void set_surface_scaling(struct vo_wayland_state *wl);
-static void sync_shift(struct vo_wayland_state *wl);
static void window_move(struct vo_wayland_state *wl, uint32_t serial);
/* Wayland listener boilerplate */
@@ -980,7 +971,6 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
uint32_t flags)
{
struct vo_wayland_state *wl = data;
- sync_shift(wl);
if (fback)
wp_presentation_feedback_destroy(fback);
@@ -996,15 +986,10 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
// - 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].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
- wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
- wl->sync[index].filled = true;
+ int64_t ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
+ int64_t msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
+ present_update_sync_values(wl->present, ust, msc);
}
static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
@@ -1378,15 +1363,6 @@ static struct vo_wayland_output *find_output(struct vo_wayland_state *wl)
return fallback_output;
}
-static 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;
-}
-
static int lookupkey(int key)
{
const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]";
@@ -1403,13 +1379,6 @@ static int lookupkey(int key)
return mpkey;
}
-static 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);
- sync_shift(wl);
-}
-
static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode)
{
wl->requested_decoration = mode;
@@ -1544,15 +1513,6 @@ static int spawn_cursor(struct vo_wayland_state *wl)
return 0;
}
-static void 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;
-}
-
static void toggle_fullscreen(struct vo_wayland_state *wl)
{
if (!wl->xdg_toplevel)
@@ -1813,6 +1773,7 @@ int vo_wayland_init(struct vo *vo)
.display = wl_display_connect(NULL),
.vo = vo,
.log = mp_log_new(wl, vo->log, "wayland"),
+ .refresh_interval = 0,
.scaling = 1,
.wakeup_pipe = {-1, -1},
.dnd_fd = -1,
@@ -1874,13 +1835,7 @@ int vo_wayland_init(struct vo *vo)
}
if (wl->presentation) {
- wl->last_ust = 0;
- wl->last_msc = 0;
- wl->refresh_interval = 0;
- 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;
+ wl->present = talloc_zero(wl, struct mp_present);
} else {
MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
wp_presentation_interface.name);
@@ -1979,43 +1934,6 @@ bool vo_wayland_supported_format(struct vo *vo, uint32_t drm_format)
return false;
}
-void vo_wayland_sync_swap(struct vo_wayland_state *wl)
-{
- int index = wl->sync_size - 1;
-
- // If these are the same, presentation feedback has not been received.
- // This can happen if a frame takes too long and misses vblank.
- // Additionally, a compositor may return an ust value of 0. In either case,
- // Don't attempt to use these statistics and wait until the next presentation
- // event arrives.
- if (!wl->sync[index].ust || wl->sync[index].ust == wl->last_ust) {
- wl->last_skipped_vsyncs = -1;
- wl->vsync_duration = -1;
- wl->last_queue_display_time = -1;
- return;
- }
-
- wl->last_skipped_vsyncs = 0;
-
- 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;
-
- if (msc_passed && ust_passed)
- wl->vsync_duration = ust_passed / msc_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_queue_display_time = ust_mp_time + wl->vsync_duration;
-}
-
void vo_wayland_uninit(struct vo *vo)
{
struct vo_wayland_state *wl = vo->wl;
@@ -2149,7 +2067,7 @@ void vo_wayland_wait_frame(struct vo_wayland_state *wl)
* 4. make up crap if vblank_time is still <= 0 (better than nothing) */
if (wl->presentation)
- vblank_time = wl->vsync_duration;
+ vblank_time = wl->present->vsync_duration;
if (vblank_time <= 0 && wl->refresh_interval > 0)
vblank_time = wl->refresh_interval;
diff --git a/video/out/wayland_common.h b/video/out/wayland_common.h
index 90bd1ca7e1..99c9769edd 100644
--- a/video/out/wayland_common.h
+++ b/video/out/wayland_common.h
@@ -87,14 +87,8 @@ struct vo_wayland_state {
/* presentation-time */
struct wp_presentation *presentation;
struct wp_presentation_feedback *feedback;
- struct vo_wayland_sync *sync;
- int sync_size;
- int64_t last_ust;
- int64_t last_msc;
- int64_t last_skipped_vsyncs;
- int64_t last_queue_display_time;
+ struct mp_present *present;
int64_t refresh_interval;
- int64_t vsync_duration;
/* xdg-decoration */
struct zxdg_decoration_manager_v1 *xdg_decoration_manager;
diff --git a/wscript_build.py b/wscript_build.py
index dad54816eb..d0cb4ef332 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -503,6 +503,7 @@ def build(ctx):
( "video/out/opengl/oml_sync.c", "egl-x11 || gl-x11" ),
( "video/out/opengl/ra_gl.c", "gl" ),
( "video/out/opengl/utils.c", "gl" ),
+ ( "video/out/present_sync.c", "wayland || x11" ),
( "video/out/vo.c" ),
( "video/out/vo_caca.c", "caca" ),
( "video/out/vo_direct3d.c", "direct3d" ),