Fix wxGLCanvas performance when using Wayland

See #23554.

(cherry picked from commit 414140113c19b100986f340e5875e209fca1fac1)
This commit is contained in:
Vadim Zeitlin 2023-07-24 17:41:35 +02:00
parent 056023cd3a
commit 0ded71ff10

View File

@ -41,6 +41,8 @@
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
static const char* TRACE_EGL = "glegl";
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxGLContextAttrs: OpenGL rendering context attributes // wxGLContextAttrs: OpenGL rendering context attributes
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -436,6 +438,8 @@ static void wl_frame_callback_handler(void* data,
struct wl_callback *, struct wl_callback *,
uint32_t) uint32_t)
{ {
wxLogTrace(TRACE_EGL, "In frame callback handler");
wxGLCanvasEGL *glc = static_cast<wxGLCanvasEGL *>(data); wxGLCanvasEGL *glc = static_cast<wxGLCanvasEGL *>(data);
glc->m_readyToDraw = true; glc->m_readyToDraw = true;
g_clear_pointer(&glc->m_wlFrameCallbackHandler, wl_callback_destroy); g_clear_pointer(&glc->m_wlFrameCallbackHandler, wl_callback_destroy);
@ -483,7 +487,6 @@ bool wxGLCanvasEGL::CreateSurface()
m_xwindow = GDK_WINDOW_XID(window); m_xwindow = GDK_WINDOW_XID(window);
m_surface = eglCreatePlatformWindowSurface(m_display, *m_config, m_surface = eglCreatePlatformWindowSurface(m_display, *m_config,
&m_xwindow, NULL); &m_xwindow, NULL);
m_readyToDraw = true;
} }
#endif #endif
#ifdef GDK_WINDOWING_WAYLAND #ifdef GDK_WINDOWING_WAYLAND
@ -520,6 +523,10 @@ bool wxGLCanvasEGL::CreateSurface()
&wl_frame_listener, this); &wl_frame_listener, this);
g_signal_connect(m_widget, "size-allocate", g_signal_connect(m_widget, "size-allocate",
G_CALLBACK(gtk_glcanvas_size_callback), this); 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 #endif
@ -647,11 +654,42 @@ void wxGLCanvasEGL::FreeDefaultConfig()
bool wxGLCanvasEGL::SwapBuffers() bool wxGLCanvasEGL::SwapBuffers()
{ {
// Under Wayland, if eglSwapBuffers() is called before the wl_surface has GdkWindow* const window = GTKGetDrawingWindow();
// been realized, it will deadlock. Thus, we need to avoid swapping before #ifdef GDK_WINDOWING_X11
// this has happened. if (wxGTKImpl::IsX11(window))
if ( !m_readyToDraw ) {
return false; 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); return eglSwapBuffers(m_display, m_surface);
} }