From 288ec0e046c4cb975367f5ad043b76666c30fe9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michel=20D=C3=A4nzer?= Date: Fri, 24 Dec 2021 19:06:47 +0100 Subject: [PATCH] xwayland/present: Run fallback timer callback after more than a second If the Wayland compositor doesn't send a pending frame event, e.g. because the Wayland surface isn't visible anywhere, it could happen that the timer kept getting pushed back and never fired. This resulted in an enormous list of pending vblank events, which could take minutes to process when the frame event finally arrived. Closes: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1110 Reviewed-by: Olivier Fourdan Tested-by: Jaap Buurman --- hw/xwayland/xwayland-present.c | 29 ++++++++++++++++++++++++----- hw/xwayland/xwayland-present.h | 2 ++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c index 69c02dce4..ed497832c 100644 --- a/hw/xwayland/xwayland-present.c +++ b/hw/xwayland/xwayland-present.c @@ -93,6 +93,7 @@ xwl_present_free_timer(struct xwl_present_window *xwl_present_window) { TimerFree(xwl_present_window->frame_timer); xwl_present_window->frame_timer = NULL; + xwl_present_window->timer_armed = 0; } static CARD32 @@ -130,6 +131,7 @@ static void xwl_present_reset_timer(struct xwl_present_window *xwl_present_window) { if (xwl_present_has_pending_events(xwl_present_window)) { + CARD32 now = GetTimeInMillis(); CARD32 timeout; if (!xorg_list_is_empty(&xwl_present_window->frame_callback_list)) @@ -137,6 +139,21 @@ xwl_present_reset_timer(struct xwl_present_window *xwl_present_window) else timeout = TIMER_LEN_COPY; + /* Make sure the timer callback runs if at least a second has passed + * since we first armed the timer. This can happen e.g. if the Wayland + * compositor doesn't send a pending frame event, e.g. because the + * Wayland surface isn't visible anywhere. + */ + if (xwl_present_window->timer_armed) { + if ((int)(now - xwl_present_window->timer_armed) > 1000) { + xwl_present_timer_callback(xwl_present_window->frame_timer, now, + xwl_present_window); + return; + } + } else { + xwl_present_window->timer_armed = now; + } + xwl_present_window->frame_timer = TimerSet(xwl_present_window->frame_timer, 0, timeout, &xwl_present_timer_callback, @@ -386,6 +403,8 @@ xwl_present_msc_bump(struct xwl_present_window *xwl_present_window) xwl_present_window->ust = GetTimeInMicros(); + xwl_present_window->timer_armed = 0; + if (flip_pending && flip_pending->sync_flip) xwl_present_flip_notify_vblank(flip_pending, xwl_present_window->ust, msc); @@ -801,14 +820,14 @@ xwl_present_execute(present_vblank_ptr vblank, uint64_t ust, uint64_t crtc_msc) present_execute_copy(vblank, crtc_msc); assert(!vblank->queued); + /* Clear the pixmap field, so this will fall through to present_execute_post next time */ + dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id); + vblank->pixmap = NULL; + if (xwl_present_queue_vblank(screen, window, vblank->crtc, vblank->event_id, crtc_msc + 1) - == Success) { - /* Clear the pixmap field, so this will fall through to present_execute_post next time */ - dixDestroyPixmap(vblank->pixmap, vblank->pixmap->drawable.id); - vblank->pixmap = NULL; + == Success) return; - } } present_execute_post(vblank, ust, crtc_msc); diff --git a/hw/xwayland/xwayland-present.h b/hw/xwayland/xwayland-present.h index d3eff73f4..5d880e822 100644 --- a/hw/xwayland/xwayland-present.h +++ b/hw/xwayland/xwayland-present.h @@ -41,6 +41,8 @@ struct xwl_present_window { uint64_t ust; OsTimerPtr frame_timer; + /* Timestamp when the current timer was first armed */ + CARD32 timer_armed; struct wl_callback *sync_callback;