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.
This commit is contained in:
Frank Praznik 2022-05-17 12:37:16 -04:00 committed by Sam Lantinga
parent 146ea9b0e1
commit 78698a0ba2
3 changed files with 50 additions and 24 deletions

View File

@ -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);
}

View File

@ -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. */
/* 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

View File

@ -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