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 #include +#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 . + */ + +#include + +#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 . + */ + +#ifndef MP_PRESENT_SYNC_H +#define MP_PRESENT_SYNC_H + +#include +#include +#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 #include +#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 #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" ),