From 0ded71ff109daae2baa83cd8e835ec775649d32c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Mon, 24 Jul 2023 17:41:35 +0200 Subject: [PATCH] Fix wxGLCanvas performance when using Wayland See #23554. (cherry picked from commit 414140113c19b100986f340e5875e209fca1fac1) --- src/unix/glegl.cpp | 50 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/unix/glegl.cpp b/src/unix/glegl.cpp index d01a0e5815..c35273cadd 100644 --- a/src/unix/glegl.cpp +++ b/src/unix/glegl.cpp @@ -41,6 +41,8 @@ #include #include +static const char* TRACE_EGL = "glegl"; + // ---------------------------------------------------------------------------- // wxGLContextAttrs: OpenGL rendering context attributes // ---------------------------------------------------------------------------- @@ -436,6 +438,8 @@ static void wl_frame_callback_handler(void* data, struct wl_callback *, uint32_t) { + wxLogTrace(TRACE_EGL, "In frame callback handler"); + wxGLCanvasEGL *glc = static_cast(data); glc->m_readyToDraw = true; g_clear_pointer(&glc->m_wlFrameCallbackHandler, wl_callback_destroy); @@ -483,7 +487,6 @@ bool wxGLCanvasEGL::CreateSurface() m_xwindow = GDK_WINDOW_XID(window); m_surface = eglCreatePlatformWindowSurface(m_display, *m_config, &m_xwindow, NULL); - m_readyToDraw = true; } #endif #ifdef GDK_WINDOWING_WAYLAND @@ -520,6 +523,10 @@ bool wxGLCanvasEGL::CreateSurface() &wl_frame_listener, this); g_signal_connect(m_widget, "size-allocate", G_CALLBACK(gtk_glcanvas_size_callback), this); + + // Ensure that eglSwapBuffers() doesn't block, as we use the surface + // callback to know when we should draw ourselves already. + eglSwapInterval(m_display, 0); } #endif @@ -647,11 +654,42 @@ void wxGLCanvasEGL::FreeDefaultConfig() bool wxGLCanvasEGL::SwapBuffers() { - // Under Wayland, if eglSwapBuffers() is called before the wl_surface has - // been realized, it will deadlock. Thus, we need to avoid swapping before - // this has happened. - if ( !m_readyToDraw ) - return false; + GdkWindow* const window = GTKGetDrawingWindow(); +#ifdef GDK_WINDOWING_X11 + if (wxGTKImpl::IsX11(window)) + { + if ( !IsShownOnScreen() ) + { + // Trying to draw on a hidden window is useless and can actually be + // harmful if the compositor blocks in eglSwapBuffers() in this + // case, so avoid it. + wxLogTrace(TRACE_EGL, "Not drawing hidden window"); + return false; + } + } +#endif // GDK_WINDOWING_X11 +#ifdef GDK_WINDOWING_WAYLAND + if (wxGTKImpl::IsWayland(window)) + { + // Under Wayland, we must not draw before we're actually ready to, as + // this would be inefficient at best and could result in a deadlock at + // worst if we're called before the window is realized. + if ( !m_readyToDraw ) + { + wxLogTrace(TRACE_EGL, "Not ready to draw yet"); + return false; + } + + // Ensure that we redraw again when the frame becomes ready. + m_readyToDraw = false; + wl_surface* surface = gdk_wayland_window_get_wl_surface(window); + m_wlFrameCallbackHandler = wl_surface_frame(surface); + wl_callback_add_listener(m_wlFrameCallbackHandler, + &wl_frame_listener, this); + } +#endif // GDK_WINDOWING_WAYLAND + + wxLogTrace(TRACE_EGL, "Swapping buffers"); return eglSwapBuffers(m_display, m_surface); }