From 78698a0ba2269ce54f82293e11807e2c492d9937 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 17 May 2022 12:37:16 -0400 Subject: [PATCH] wayland: Use a separate frame callback for setting the surface damage region Previously, the surface damage region was being set in the same callback used for preventing render hangs in the GL backend when the surface was not visible. This was not ideal, as the callback was never fired in the case of using a different render backend or having a swap interval of 0. Use a separate frame callback for setting the surface damage region to ensure that it fires reliably, regardless of the backend being used or swap interval. --- src/video/wayland/SDL_waylandopengles.c | 6 +-- src/video/wayland/SDL_waylandwindow.c | 61 +++++++++++++++++-------- src/video/wayland/SDL_waylandwindow.h | 7 +-- 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/video/wayland/SDL_waylandopengles.c b/src/video/wayland/SDL_waylandopengles.c index 4c834fd86..26626ee51 100644 --- a/src/video/wayland/SDL_waylandopengles.c +++ b/src/video/wayland/SDL_waylandopengles.c @@ -140,9 +140,9 @@ Wayland_GLES_SwapWindow(_THIS, SDL_Window *window) /* wl_display_prepare_read_queue() will return -1 if the event queue is not empty. * If the event queue is empty, it will prepare us for our SDL_IOReady() call. */ - if (WAYLAND_wl_display_prepare_read_queue(display, data->frame_event_queue) != 0) { + if (WAYLAND_wl_display_prepare_read_queue(display, data->gles_swap_frame_event_queue) != 0) { /* We have some pending events. Check if the frame callback happened. */ - WAYLAND_wl_display_dispatch_queue_pending(display, data->frame_event_queue); + WAYLAND_wl_display_dispatch_queue_pending(display, data->gles_swap_frame_event_queue); continue; } @@ -163,7 +163,7 @@ Wayland_GLES_SwapWindow(_THIS, SDL_Window *window) /* We have events. Read and dispatch them. */ WAYLAND_wl_display_read_events(display); - WAYLAND_wl_display_dispatch_queue_pending(display, data->frame_event_queue); + WAYLAND_wl_display_dispatch_queue_pending(display, data->gles_swap_frame_event_queue); } SDL_AtomicSet(&data->swap_interval_ready, 0); } diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 2c70d4055..8be9cd987 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -426,27 +426,44 @@ SetFullscreen(SDL_Window *window, struct wl_output *output, SDL_bool commit) } } -static const struct wl_callback_listener surface_frame_listener; +const struct wl_callback_listener surface_damage_frame_listener; static void -handle_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time) +surface_damage_frame_done(void *data, struct wl_callback *cb, uint32_t time) { - SDL_WindowData *wind = (SDL_WindowData *) data; - SDL_AtomicSet(&wind->swap_interval_ready, 1); /* mark window as ready to present again. */ + SDL_WindowData *wind = (SDL_WindowData *)data; + /* Manually set the damage region when using a viewport. */ if (!SDL_RectEmpty(&wind->viewport_rect)) { wl_surface_damage(wind->surface, wind->viewport_rect.x, wind->viewport_rect.y, wind->viewport_rect.w, wind->viewport_rect.h); } - /* reset this callback to fire again once a new frame was presented and compositor wants the next one. */ - wind->frame_callback = wl_surface_frame(wind->frame_surface_wrapper); wl_callback_destroy(cb); - wl_callback_add_listener(wind->frame_callback, &surface_frame_listener, data); + wind->surface_damage_frame_callback = wl_surface_frame(wind->surface); + wl_callback_add_listener(wind->surface_damage_frame_callback, &surface_damage_frame_listener, data); } -static const struct wl_callback_listener surface_frame_listener = { - handle_surface_frame_done +const struct wl_callback_listener surface_damage_frame_listener = { + surface_damage_frame_done +}; + +static const struct wl_callback_listener gles_swap_frame_listener; + +static void +gles_swap_frame_done(void *data, struct wl_callback *cb, uint32_t time) +{ + SDL_WindowData *wind = (SDL_WindowData *) data; + SDL_AtomicSet(&wind->swap_interval_ready, 1); /* mark window as ready to present again. */ + + /* reset this callback to fire again once a new frame was presented and compositor wants the next one. */ + wind->gles_swap_frame_callback = wl_surface_frame(wind->gles_swap_frame_surface_wrapper); + wl_callback_destroy(cb); + wl_callback_add_listener(wind->gles_swap_frame_callback, &gles_swap_frame_listener, data); +} + +static const struct wl_callback_listener gles_swap_frame_listener = { + gles_swap_frame_done }; @@ -1838,13 +1855,17 @@ int Wayland_CreateWindow(_THIS, SDL_Window *window) * window isn't visible. */ if (window->flags & SDL_WINDOW_OPENGL) { - data->frame_event_queue = WAYLAND_wl_display_create_queue(data->waylandData->display); - data->frame_surface_wrapper = WAYLAND_wl_proxy_create_wrapper(data->surface); - WAYLAND_wl_proxy_set_queue((struct wl_proxy *)data->frame_surface_wrapper, data->frame_event_queue); - data->frame_callback = wl_surface_frame(data->frame_surface_wrapper); - wl_callback_add_listener(data->frame_callback, &surface_frame_listener, data); + data->gles_swap_frame_event_queue = WAYLAND_wl_display_create_queue(data->waylandData->display); + data->gles_swap_frame_surface_wrapper = WAYLAND_wl_proxy_create_wrapper(data->surface); + WAYLAND_wl_proxy_set_queue((struct wl_proxy *)data->gles_swap_frame_surface_wrapper, data->gles_swap_frame_event_queue); + data->gles_swap_frame_callback = wl_surface_frame(data->gles_swap_frame_surface_wrapper); + wl_callback_add_listener(data->gles_swap_frame_callback, &gles_swap_frame_listener, data); } + /* Fire a callback when the compositor wants a new frame to set the surface damage region. */ + data->surface_damage_frame_callback = wl_surface_frame(data->surface); + wl_callback_add_listener(data->surface_damage_frame_callback, &surface_damage_frame_listener, data); + #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH if (c->surface_extension) { data->extended_surface = qt_surface_extension_get_extended_surface( @@ -2107,10 +2128,14 @@ void Wayland_DestroyWindow(_THIS, SDL_Window *window) SDL_free(wind->outputs); - if (wind->frame_callback) { - WAYLAND_wl_event_queue_destroy(wind->frame_event_queue); - WAYLAND_wl_proxy_wrapper_destroy(wind->frame_surface_wrapper); - wl_callback_destroy(wind->frame_callback); + if (wind->gles_swap_frame_callback) { + WAYLAND_wl_event_queue_destroy(wind->gles_swap_frame_event_queue); + WAYLAND_wl_proxy_wrapper_destroy(wind->gles_swap_frame_surface_wrapper); + wl_callback_destroy(wind->gles_swap_frame_callback); + } + + if (wind->surface_damage_frame_callback) { + wl_callback_destroy(wind->surface_damage_frame_callback); } #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index e77cb80a5..703ec53da 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -42,9 +42,10 @@ typedef struct { SDL_Window *sdlwindow; SDL_VideoData *waylandData; struct wl_surface *surface; - struct wl_callback *frame_callback; - struct wl_event_queue *frame_event_queue; - struct wl_surface *frame_surface_wrapper; + struct wl_callback *gles_swap_frame_callback; + struct wl_event_queue *gles_swap_frame_event_queue; + struct wl_surface *gles_swap_frame_surface_wrapper; + struct wl_callback *surface_damage_frame_callback; union { #ifdef HAVE_LIBDECOR_H