wayland: refactor presentation time

The motivation for this change was a segfault caused by e107342 which
has complicated reasons for occuring (i.e. I'm not 100% sure but I think
it is a really weird race). The major part of this commit is moving the
initialization of presentation listener to the frame_callback function.
Calling it in swap_buffers worked fine but in practice it meant a lot of
meaningless function calls if a window was hidden (the presentation
would just be immediately discarded). By calling it in frame_callback,
we ensure the listener is only created when it is possible to receive a
presentation event.

Of course calling the presentation listener in feedback_presented or
feedback_discarded was considered, but ultimately these events are too
slow. Receiving the ust/msc/sbc triplet here and then passing it to mpv
results in higher vsync judder since there is (likely) not enough time
before the next pageflip. By design, the frame callback is meant to give
us as much time as possible before the next repaint so calling it here
is probably optimal.

Additionally, we can make better use of the feedback_discarded event.
The wp_presentation_feedback should not be destroyed here. It will be
taken care of either when we get feedback again or when the player
quits. Instead what we can do is set a bool that tells wayland_sync_swap
to update itself based on mp_time delta. In practice, the result is not
any different than before, but it should be more understandable what is
going on now.

Of course, the segfault mentioned at the beginning is fixed with this as
well.
This commit is contained in:
Dudemanguy 2020-08-15 16:07:53 -05:00
parent 8ec61c366a
commit e9cde72536
4 changed files with 68 additions and 61 deletions

View File

@ -39,24 +39,6 @@ struct priv {
struct wl_egl_window *egl_window;
};
static const struct wl_callback_listener frame_listener;
static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct vo_wayland_state *wl = data;
if (callback)
wl_callback_destroy(callback);
wl->frame_callback = wl_surface_frame(wl->surface);
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
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,
@ -71,9 +53,11 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
uint32_t flags)
{
struct vo_wayland_state *wl = data;
wp_presentation_feedback_destroy(fback);
vo_wayland_sync_shift(wl);
if (fback)
wp_presentation_feedback_destroy(fback);
// 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.
@ -93,11 +77,13 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
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;
wl->presentation_discarded = false;
}
static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
{
wp_presentation_feedback_destroy(fback);
struct vo_wayland_state *wl = data;
wl->presentation_discarded = true;
}
static const struct wp_presentation_feedback_listener feedback_listener = {
@ -106,6 +92,30 @@ static const struct wp_presentation_feedback_listener feedback_listener = {
feedback_discarded,
};
static const struct wl_callback_listener frame_listener;
static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct vo_wayland_state *wl = data;
if (callback)
wl_callback_destroy(callback);
wl->frame_callback = wl_surface_frame(wl->surface);
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
if (wl->presentation) {
wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
}
wl->frame_wait = false;
}
static const struct wl_callback_listener frame_listener = {
frame_callback,
};
static void resize(struct ra_ctx *ctx)
{
struct priv *p = ctx->priv;
@ -130,21 +140,15 @@ 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);
if (!wl->opts->disable_vsync)
vo_wayland_wait_frame(wl);
if (wl->presentation)
if (wl->presentation) {
wl->user_sbc += 1;
wayland_sync_swap(wl);
}
wl->frame_wait = true;
}

View File

@ -29,24 +29,6 @@ struct priv {
struct mpvk_ctx vk;
};
static const struct wl_callback_listener frame_listener;
static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct vo_wayland_state *wl = data;
if (callback)
wl_callback_destroy(callback);
wl->frame_callback = wl_surface_frame(wl->surface);
wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
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,
@ -61,9 +43,11 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
uint32_t flags)
{
struct vo_wayland_state *wl = data;
wp_presentation_feedback_destroy(fback);
vo_wayland_sync_shift(wl);
if (fback)
wp_presentation_feedback_destroy(fback);
// 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.
@ -83,11 +67,13 @@ static void feedback_presented(void *data, struct wp_presentation_feedback *fbac
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;
wl->presentation_discarded = false;
}
static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
{
wp_presentation_feedback_destroy(fback);
struct vo_wayland_state *wl = data;
wl->presentation_discarded = true;
}
static const struct wp_presentation_feedback_listener feedback_listener = {
@ -96,24 +82,41 @@ static const struct wp_presentation_feedback_listener feedback_listener = {
feedback_discarded,
};
static void wayland_vk_swap_buffers(struct ra_ctx *ctx)
static const struct wl_callback_listener frame_listener;
static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
{
struct vo_wayland_state *wl = ctx->vo->wl;
struct vo_wayland_state *wl = data;
if (callback)
wl_callback_destroy(callback);
wl->frame_callback = wl_surface_frame(wl->surface);
wl_callback_add_listener(wl->frame_callback, &frame_listener, 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);
}
wl->frame_wait = false;
}
static const struct wl_callback_listener frame_listener = {
frame_callback,
};
static void wayland_vk_swap_buffers(struct ra_ctx *ctx)
{
struct vo_wayland_state *wl = ctx->vo->wl;
if (!wl->opts->disable_vsync)
vo_wayland_wait_frame(wl);
if (wl->presentation)
if (wl->presentation) {
wl->user_sbc += 1;
wayland_sync_swap(wl);
}
wl->frame_wait = true;
}

View File

@ -1607,10 +1607,9 @@ void wayland_sync_swap(struct vo_wayland_state *wl)
wl->last_skipped_vsyncs = 0;
// If these are the same, presentation feedback has not been received.
// This will happen if the window is obscured/hidden in some way. Update
// the values based on the difference in mp_time.
if (wl->sync[index].ust == wl->last_ust && wl->last_ust) {
// If the presentation event was discarded, update the values based on
// the difference in mp_time.
if (wl->presentation_discarded) {
wl->sync[index].ust += mp_time - wl->sync[index].last_mp_time;
wl->sync[index].msc += 1;
wl->sync[index].sbc += 1;

View File

@ -107,6 +107,7 @@ struct vo_wayland_state {
/* Presentation Feedback */
struct vo_wayland_sync *sync;
int sync_size;
bool presentation_discarded;
int64_t user_sbc;
int64_t last_ust;
int64_t last_msc;