wayland: Implement a timeout extension for eglSwapBuffers
Background: I've been working on Wayland support for SDL this year...
https://github.com/libsdl-org/SDL/tree/main/src/video/wayland
... and the results as of writing have been great! Most games work really well with today's drivers and desktop environments; I've been posting test cases here:
- https://twitter.com/flibitijibibo/status/1403895424872759296
- https://twitter.com/flibitijibibo/status/1404550971422908420
There are two issues we've run into with the existing catalog of games, however, and both seem to be rooted in EGL. This is issue 1 (see #4933 (closed) for issue 2):
The infinite block in eglSwapBuffers for backgrounded windows is killing us - we're constantly running into completely random cases where EGL will just infinitely stall a program, in Transistor's case it even happens when the window is visible! For games expecting not to be stalled by the compositor it's presented a huge usability problem since the only fix in just about every case is for end users to pull out kill -9
.
This is unique to OpenGL, however - with Vulkan we have access to the timeout value in vkAcquireNextImageKHR. Whether or not games actually use the timeout is a whole other issue, but the ability to have a timeout is at least there. EGL does not have such a feature! This puts SDL in an uncomfortable spot where we've had to come up with increasingly hare-brained schemes to try and simulate timeout ourselves, and the results are ugly and don't even work anyway:
- https://github.com/libsdl-org/SDL/blob/7f261d3b7667978ced93a4fdc6aec2380df3d87f/src/video/wayland/SDL_waylandopengles.c#L111-L157
- https://github.com/libsdl-org/SDL/blob/7f261d3b7667978ced93a4fdc6aec2380df3d87f/src/video/wayland/SDL_waylandwindow.c#L205-L219
For SDL, all we would need to fix this is an eglSwapBuffersWithTimeoutWL. From what I can tell, this loop here...
... would just need to be more like this loop in the Vulkan WSI:
https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/vulkan/wsi/wsi_common_wayland.c#L780-801
So, something like this might do the trick?
while (dri2_surf->throttle_callback != NULL) {
if (wl_display_dispatch_queue_pending(dri2_dpy->wl_dpy,
dri2_surf->wl_queue) == -1)
return -1;
/* Check for timeout. */
struct timespec current_time;
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
if (timespec_after(¤t_time, &end_time))
return EGL_FALSE;
}
This touches a lot of projects at once and proposes a whole new EGL extension (which directly interacts with another extension, swap_buffers_with_damage), so I wanted to throw this out there to see if I'm on the right track before trying to draft anything serious.