summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDudemanguy <random342@airmail.cc>2022-06-10 11:49:28 -0500
committerDudemanguy <random342@airmail.cc>2022-06-19 18:13:55 +0000
commit7ce26dd3248a45a4d5e0a8c15a981834e7efc733 (patch)
treeb79e41d7b30ceaa8df3d1c64427acb73775916ba
parent44ecf83a1bd94f69cbe7e55b900f2502d15d7d5c (diff)
downloadmpv-7ce26dd3248a45a4d5e0a8c15a981834e7efc733.tar.bz2
mpv-7ce26dd3248a45a4d5e0a8c15a981834e7efc733.tar.xz
vo: move wayland presentation to separate files
Wayland had some specific code that it used for implementing the presentation time protocol. It turns out that xorg's present extension is extremely similar, so it would be silly to duplicate this whole mess again. Factor this out to separate, independent code and introduce the mp_present struct which is used for handling the ust/msc values and some other associated values. Also, add in some helper functions so all the dirty details live specifically in present_sync. The only wayland-specific part is actually obtaining ust/msc values. Since only wayland or xorg are expected to use this, add a conditional to the build that only adds this file when either one of those are present. You may observe that sbc is completely omitted. This field existed in wayland, but was completely unused (presentation time doesn't return this). Xorg's present extension also doesn't use this so just get rid of it all together. The actual calculation is slightly altered so it is correct for our purposes. We want to get the presentation event of the last frame that was just occured (this function executes right after the buffer swap). The adjustment is to just remove the vsync_duration subtraction. Also, The overly-complicated queue approach is removed. This has no actual use in practice (on wayland or xorg). Presentation statistics are only ever used after the immediate preceding swap to update vsync timings or thrown away.
-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" ),