mirror of
https://github.com/glfw/glfw.git
synced 2026-09-19 07:05:49 +00:00
Wayland: Fix EGL buffer swap blocking indefinitely
When the swap interval was nonzero, EGL on Wayland would wait indefinitely for a frame callback on a suspended window, causing applications that call eglSwapBuffers to halt. This re-implements the wait for a surface frame so that we can add a reasonable timeout, while setting the EGL swap interval to zero. That allows applications to make progress despite the window being suspended. Some insights for how to structure this workaround were gleaned from dri2_wl_swap_buffers_with_damage in Mesa. This initial implementation still has a race between glfwHideWindow and glfwSwapBuffers when rendering on a separate thread. This could lead to a window becoming visible again after being hidden on the main thread. Related to #1350 Fixes #2582 Fixes #2640 Fixes #2719 Fixes #2723 Fixes #2800 Fixes #2827
This commit is contained in:
@@ -2296,6 +2296,18 @@ static const struct xdg_activation_token_v1_listener xdgActivationListener =
|
||||
xdgActivationHandleDone
|
||||
};
|
||||
|
||||
static void callbackHandleFrame(void* userData, struct wl_callback* callback, uint32_t data)
|
||||
{
|
||||
_GLFWwindow* window = userData;
|
||||
wl_callback_destroy(callback);
|
||||
window->wl.egl.callback = NULL;
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frameCallbackListener =
|
||||
{
|
||||
callbackHandleFrame
|
||||
};
|
||||
|
||||
void _glfwAddSeatListenerWayland(struct wl_seat* seat)
|
||||
{
|
||||
wl_seat_add_listener(seat, &seatListener, NULL);
|
||||
@@ -2306,6 +2318,39 @@ void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device)
|
||||
wl_data_device_add_listener(device, &dataDeviceListener, NULL);
|
||||
}
|
||||
|
||||
GLFWbool _glfwWaitForEGLFrameWayland(_GLFWwindow* window)
|
||||
{
|
||||
double timeout = 0.02;
|
||||
|
||||
while (window->wl.egl.callback)
|
||||
{
|
||||
while (wl_display_prepare_read_queue(_glfw.wl.display, window->wl.egl.queue) != 0)
|
||||
wl_display_dispatch_queue_pending(_glfw.wl.display, window->wl.egl.queue);
|
||||
|
||||
if (!flushDisplay())
|
||||
{
|
||||
wl_display_cancel_read(_glfw.wl.display);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLIN };
|
||||
|
||||
if (!_glfwPollPOSIX(&fd, 1, &timeout))
|
||||
{
|
||||
wl_display_cancel_read(_glfw.wl.display);
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
wl_display_read_events(_glfw.wl.display);
|
||||
wl_display_dispatch_queue_pending(_glfw.wl.display, window->wl.egl.queue);
|
||||
}
|
||||
|
||||
window->wl.egl.callback = wl_surface_frame(window->wl.egl.wrapper);
|
||||
wl_callback_add_listener(window->wl.egl.callback, &frameCallbackListener, window);
|
||||
|
||||
// If the window is hidden when the wait is over then don't swap
|
||||
return window->wl.visible;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
@@ -2334,6 +2379,27 @@ GLFWbool _glfwCreateWindowWayland(_GLFWwindow* window,
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->wl.egl.queue = wl_display_create_queue(_glfw.wl.display);
|
||||
if (!window->wl.egl.queue)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to create EGL frame queue");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
window->wl.egl.wrapper = wl_proxy_create_wrapper(window->wl.surface);
|
||||
if (!window->wl.egl.wrapper)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to create surface wrapper");
|
||||
return GLFW_FALSE;
|
||||
}
|
||||
|
||||
wl_proxy_set_queue((struct wl_proxy*) window->wl.egl.wrapper,
|
||||
window->wl.egl.queue);
|
||||
|
||||
window->wl.egl.interval = 1;
|
||||
|
||||
if (!_glfwInitEGL())
|
||||
return GLFW_FALSE;
|
||||
if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig))
|
||||
@@ -2405,6 +2471,15 @@ void _glfwDestroyWindowWayland(_GLFWwindow* window)
|
||||
if (window->wl.fallback.buffer)
|
||||
wl_buffer_destroy(window->wl.fallback.buffer);
|
||||
|
||||
if (window->wl.egl.callback)
|
||||
wl_callback_destroy(window->wl.egl.callback);
|
||||
|
||||
if (window->wl.egl.wrapper)
|
||||
wl_proxy_wrapper_destroy(window->wl.egl.wrapper);
|
||||
|
||||
if (window->wl.egl.queue)
|
||||
wl_event_queue_destroy(window->wl.egl.queue);
|
||||
|
||||
if (window->wl.egl.window)
|
||||
wl_egl_window_destroy(window->wl.egl.window);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user