mirror of
https://github.com/glfw/glfw.git
synced 2026-04-19 03:13:17 +02: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:
@@ -368,7 +368,12 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
window->context.getProcAddress("glGetIntegerv");
|
||||
window->context.GetString = (PFNGLGETSTRINGPROC)
|
||||
window->context.getProcAddress("glGetString");
|
||||
if (!window->context.GetIntegerv || !window->context.GetString)
|
||||
window->context.Flush = (PFNGLFLUSHPROC)
|
||||
window->context.getProcAddress("glFlush");
|
||||
|
||||
if (!window->context.GetIntegerv ||
|
||||
!window->context.GetString ||
|
||||
!window->context.Flush)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken");
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
|
||||
@@ -254,6 +254,12 @@ static void makeContextCurrentEGL(_GLFWwindow* window)
|
||||
getEGLErrorString(eglGetError()));
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(_GLFW_WAYLAND)
|
||||
// NOTE: Disable EGL vsync so we can substitute our own that includes a timeout
|
||||
if (_glfw.platform.platformID == GLFW_PLATFORM_WAYLAND)
|
||||
eglSwapInterval(_glfw.egl.display, 0);
|
||||
#endif // _GLFW_WAYLAND
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -287,6 +293,15 @@ static void swapBuffersEGL(_GLFWwindow* window)
|
||||
// NOTE: Swapping buffers on a hidden window on Wayland makes it visible
|
||||
if (!window->wl.visible)
|
||||
return;
|
||||
|
||||
// NOTE: We wait for a frame manually so we can add a timeout,
|
||||
// as the EGL implementation will wait indefinitely
|
||||
if (window->wl.egl.interval > 0)
|
||||
{
|
||||
window->context.Flush();
|
||||
if (!_glfwWaitForEGLFrameWayland(window))
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -295,6 +310,16 @@ static void swapBuffersEGL(_GLFWwindow* window)
|
||||
|
||||
static void swapIntervalEGL(int interval)
|
||||
{
|
||||
#if defined(_GLFW_WAYLAND)
|
||||
if (_glfw.platform.platformID == GLFW_PLATFORM_WAYLAND)
|
||||
{
|
||||
_GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
assert(window != NULL);
|
||||
window->wl.egl.interval = interval;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
eglSwapInterval(_glfw.egl.display, interval);
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield);
|
||||
typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum);
|
||||
typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*);
|
||||
typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint);
|
||||
typedef void (APIENTRY * PFNGLFLUSHPROC)(void);
|
||||
|
||||
#define EGL_SUCCESS 0x3000
|
||||
#define EGL_NOT_INITIALIZED 0x3001
|
||||
@@ -498,6 +499,7 @@ struct _GLFWcontext
|
||||
PFNGLGETSTRINGIPROC GetStringi;
|
||||
PFNGLGETINTEGERVPROC GetIntegerv;
|
||||
PFNGLGETSTRINGPROC GetString;
|
||||
PFNGLFLUSHPROC Flush;
|
||||
|
||||
void (*makeCurrent)(_GLFWwindow*);
|
||||
void (*swapBuffers)(_GLFWwindow*);
|
||||
|
||||
@@ -578,6 +578,14 @@ int _glfwInitWayland(void)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_get_fd");
|
||||
_glfw.wl.client.display_prepare_read = (PFN_wl_display_prepare_read)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_prepare_read");
|
||||
_glfw.wl.client.display_create_queue = (PFN_wl_display_create_queue)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_create_queue");
|
||||
_glfw.wl.client.display_prepare_read_queue = (PFN_wl_display_prepare_read_queue)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_prepare_read_queue");
|
||||
_glfw.wl.client.display_dispatch_queue_pending = (PFN_wl_display_dispatch_queue_pending)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_display_dispatch_queue_pending");
|
||||
_glfw.wl.client.event_queue_destroy = (PFN_wl_event_queue_destroy)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_event_queue_destroy");
|
||||
_glfw.wl.client.proxy_marshal = (PFN_wl_proxy_marshal)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_marshal");
|
||||
_glfw.wl.client.proxy_add_listener = (PFN_wl_proxy_add_listener)
|
||||
@@ -600,6 +608,12 @@ int _glfwInitWayland(void)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_get_version");
|
||||
_glfw.wl.client.proxy_marshal_flags = (PFN_wl_proxy_marshal_flags)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_marshal_flags");
|
||||
_glfw.wl.client.proxy_create_wrapper = (PFN_wl_proxy_create_wrapper)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_create_wrapper");
|
||||
_glfw.wl.client.proxy_wrapper_destroy = (PFN_wl_proxy_wrapper_destroy)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_wrapper_destroy");
|
||||
_glfw.wl.client.proxy_set_queue = (PFN_wl_proxy_set_queue)
|
||||
_glfwPlatformGetModuleSymbol(_glfw.wl.client.handle, "wl_proxy_set_queue");
|
||||
|
||||
if (!_glfw.wl.client.display_flush ||
|
||||
!_glfw.wl.client.display_cancel_read ||
|
||||
@@ -609,6 +623,10 @@ int _glfwInitWayland(void)
|
||||
!_glfw.wl.client.display_roundtrip ||
|
||||
!_glfw.wl.client.display_get_fd ||
|
||||
!_glfw.wl.client.display_prepare_read ||
|
||||
!_glfw.wl.client.display_create_queue ||
|
||||
!_glfw.wl.client.display_prepare_read_queue ||
|
||||
!_glfw.wl.client.display_dispatch_queue_pending ||
|
||||
!_glfw.wl.client.event_queue_destroy ||
|
||||
!_glfw.wl.client.proxy_marshal ||
|
||||
!_glfw.wl.client.proxy_add_listener ||
|
||||
!_glfw.wl.client.proxy_destroy ||
|
||||
@@ -617,7 +635,10 @@ int _glfwInitWayland(void)
|
||||
!_glfw.wl.client.proxy_get_user_data ||
|
||||
!_glfw.wl.client.proxy_set_user_data ||
|
||||
!_glfw.wl.client.proxy_get_tag ||
|
||||
!_glfw.wl.client.proxy_set_tag)
|
||||
!_glfw.wl.client.proxy_set_tag ||
|
||||
!_glfw.wl.client.proxy_create_wrapper ||
|
||||
!_glfw.wl.client.proxy_wrapper_destroy ||
|
||||
!_glfw.wl.client.proxy_set_queue)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to load libwayland-client entry point");
|
||||
|
||||
@@ -51,6 +51,10 @@ typedef void (* PFN_wl_display_disconnect)(struct wl_display*);
|
||||
typedef int (* PFN_wl_display_roundtrip)(struct wl_display*);
|
||||
typedef int (* PFN_wl_display_get_fd)(struct wl_display*);
|
||||
typedef int (* PFN_wl_display_prepare_read)(struct wl_display*);
|
||||
typedef struct wl_event_queue* (* PFN_wl_display_create_queue)(struct wl_display*);
|
||||
typedef void (* PFN_wl_event_queue_destroy)(struct wl_event_queue*);
|
||||
typedef int (* PFN_wl_display_prepare_read_queue)(struct wl_display*,struct wl_event_queue*);
|
||||
typedef int (* PFN_wl_display_dispatch_queue_pending)(struct wl_display*,struct wl_event_queue*);
|
||||
typedef void (* PFN_wl_proxy_marshal)(struct wl_proxy*,uint32_t,...);
|
||||
typedef int (* PFN_wl_proxy_add_listener)(struct wl_proxy*,void(**)(void),void*);
|
||||
typedef void (* PFN_wl_proxy_destroy)(struct wl_proxy*);
|
||||
@@ -62,6 +66,9 @@ typedef void (* PFN_wl_proxy_set_tag)(struct wl_proxy*,const char*const*);
|
||||
typedef const char* const* (* PFN_wl_proxy_get_tag)(struct wl_proxy*);
|
||||
typedef uint32_t (* PFN_wl_proxy_get_version)(struct wl_proxy*);
|
||||
typedef struct wl_proxy* (* PFN_wl_proxy_marshal_flags)(struct wl_proxy*,uint32_t,const struct wl_interface*,uint32_t,uint32_t,...);
|
||||
typedef void* (* PFN_wl_proxy_create_wrapper)(void*);
|
||||
typedef void (* PFN_wl_proxy_wrapper_destroy)(void*);
|
||||
typedef void (* PFN_wl_proxy_set_queue)(struct wl_proxy*,struct wl_event_queue*);
|
||||
#define wl_display_flush _glfw.wl.client.display_flush
|
||||
#define wl_display_cancel_read _glfw.wl.client.display_cancel_read
|
||||
#define wl_display_dispatch_pending _glfw.wl.client.display_dispatch_pending
|
||||
@@ -70,6 +77,10 @@ typedef struct wl_proxy* (* PFN_wl_proxy_marshal_flags)(struct wl_proxy*,uint32_
|
||||
#define wl_display_roundtrip _glfw.wl.client.display_roundtrip
|
||||
#define wl_display_get_fd _glfw.wl.client.display_get_fd
|
||||
#define wl_display_prepare_read _glfw.wl.client.display_prepare_read
|
||||
#define wl_display_create_queue _glfw.wl.client.display_create_queue
|
||||
#define wl_display_prepare_read_queue _glfw.wl.client.display_prepare_read_queue
|
||||
#define wl_display_dispatch_queue_pending _glfw.wl.client.display_dispatch_queue_pending
|
||||
#define wl_event_queue_destroy _glfw.wl.client.event_queue_destroy
|
||||
#define wl_proxy_marshal _glfw.wl.client.proxy_marshal
|
||||
#define wl_proxy_add_listener _glfw.wl.client.proxy_add_listener
|
||||
#define wl_proxy_destroy _glfw.wl.client.proxy_destroy
|
||||
@@ -81,6 +92,9 @@ typedef struct wl_proxy* (* PFN_wl_proxy_marshal_flags)(struct wl_proxy*,uint32_
|
||||
#define wl_proxy_set_tag _glfw.wl.client.proxy_set_tag
|
||||
#define wl_proxy_get_version _glfw.wl.client.proxy_get_version
|
||||
#define wl_proxy_marshal_flags _glfw.wl.client.proxy_marshal_flags
|
||||
#define wl_proxy_create_wrapper _glfw.wl.client.proxy_create_wrapper
|
||||
#define wl_proxy_wrapper_destroy _glfw.wl.client.proxy_wrapper_destroy
|
||||
#define wl_proxy_set_queue _glfw.wl.client.proxy_set_queue
|
||||
|
||||
struct wl_shm;
|
||||
struct wl_output;
|
||||
@@ -364,7 +378,11 @@ typedef struct _GLFWwindowWayland
|
||||
struct wl_callback* callback;
|
||||
|
||||
struct {
|
||||
struct wl_event_queue* queue;
|
||||
struct wl_egl_window* window;
|
||||
struct wl_callback* callback;
|
||||
struct wl_surface* wrapper;
|
||||
int interval;
|
||||
} egl;
|
||||
|
||||
struct {
|
||||
@@ -537,6 +555,10 @@ typedef struct _GLFWlibraryWayland
|
||||
PFN_wl_display_roundtrip display_roundtrip;
|
||||
PFN_wl_display_get_fd display_get_fd;
|
||||
PFN_wl_display_prepare_read display_prepare_read;
|
||||
PFN_wl_display_create_queue display_create_queue;
|
||||
PFN_wl_display_prepare_read_queue display_prepare_read_queue;
|
||||
PFN_wl_display_dispatch_queue_pending display_dispatch_queue_pending;
|
||||
PFN_wl_event_queue_destroy event_queue_destroy;
|
||||
PFN_wl_proxy_marshal proxy_marshal;
|
||||
PFN_wl_proxy_add_listener proxy_add_listener;
|
||||
PFN_wl_proxy_destroy proxy_destroy;
|
||||
@@ -548,6 +570,9 @@ typedef struct _GLFWlibraryWayland
|
||||
PFN_wl_proxy_set_tag proxy_set_tag;
|
||||
PFN_wl_proxy_get_version proxy_get_version;
|
||||
PFN_wl_proxy_marshal_flags proxy_marshal_flags;
|
||||
PFN_wl_proxy_create_wrapper proxy_create_wrapper;
|
||||
PFN_wl_proxy_wrapper_destroy proxy_wrapper_destroy;
|
||||
PFN_wl_proxy_set_queue proxy_set_queue;
|
||||
} client;
|
||||
|
||||
struct {
|
||||
@@ -706,3 +731,5 @@ void _glfwUpdateBufferScaleFromOutputsWayland(_GLFWwindow* window);
|
||||
void _glfwAddSeatListenerWayland(struct wl_seat* seat);
|
||||
void _glfwAddDataDeviceListenerWayland(struct wl_data_device* device);
|
||||
|
||||
GLFWbool _glfwWaitForEGLFrameWayland(_GLFWwindow* window);
|
||||
|
||||
|
||||
@@ -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