From e3b5b76cdd910f990a81fbedd6462fe616ebf1cd Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Mon, 29 Jun 2020 10:21:58 -0400 Subject: [PATCH] x11: Handle window getting unmapped while frame still pending Since commit 972134abe48a4c9c7b6ad41b0723f30f4e7ae16b we now call glClientWaitSync for the vendor nvidia driver, to know when a frame is ready for the compositor to process. If a surface is hidden while a frame is still being rendered by the GPU, the surface will never produce the damage event the code relies on to trigger the call to glClientWaitSync. This leaves the fence dangling, and the next time the surface is shown, it will start a fresh frame and blow an assertion since the fence from the last frame is still hanging around. This commit ensures a frame gets fully wrapped up before hiding a surface. --- gdk/x11/gdkglcontext-x11.c | 59 +++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c index f0393d4802..9fdbca9c89 100644 --- a/gdk/x11/gdkglcontext-x11.c +++ b/gdk/x11/gdkglcontext-x11.c @@ -582,6 +582,23 @@ create_legacy_context (GdkDisplay *display, } #ifdef HAVE_XDAMAGE +static void +finish_frame (GdkGLContext *context) +{ + GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context); + GdkSurface *surface = gdk_gl_context_get_surface (context); + + if (context_x11->xdamage == 0) + return; + + if (context_x11->frame_fence == 0) + return; + + glDeleteSync (context_x11->frame_fence); + context_x11->frame_fence = 0; + _gdk_x11_surface_set_frame_still_painting (surface, FALSE); +} + static void bind_context_for_frame_fence (GdkGLContext *context) { @@ -628,7 +645,6 @@ on_gl_surface_xevent (GdkGLContext *context, GdkX11Display *display_x11) { GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context); - GdkSurface *surface = gdk_gl_context_get_surface (context); XDamageNotifyEvent *damage_xevent; if (!context_x11->is_attached) @@ -675,9 +691,7 @@ on_gl_surface_xevent (GdkGLContext *context, case GL_WAIT_FAILED: if (wait_result == GL_WAIT_FAILED) g_warning ("failed to wait on GL fence associated with last swap buffers call"); - glDeleteSync (context_x11->frame_fence); - context_x11->frame_fence = 0; - _gdk_x11_surface_set_frame_still_painting (surface, FALSE); + finish_frame (context); break; /* We assume that if the fence hasn't been signaled, that this @@ -696,6 +710,21 @@ on_gl_surface_xevent (GdkGLContext *context, return FALSE; } + +static void +on_surface_state_changed (GdkGLContext *context) +{ + GdkSurface *surface = gdk_gl_context_get_surface (context); + + if ((surface->state & GDK_SURFACE_STATE_WITHDRAWN) == 0) + return; + + /* If we're about to withdraw the surface, then we don't care if the frame is + * still getting rendered by the GPU. The compositor is going to remove the surface + * from the scene anyway, so wrap up the frame. + */ + finish_frame (context); +} #endif static gboolean @@ -878,13 +907,23 @@ gdk_x11_gl_context_realize (GdkGLContext *context, gdk_x11_surface_get_xid (surface), XDamageReportRawRectangles); if (gdk_x11_display_error_trap_pop (display)) - context_x11->xdamage = 0; + { + context_x11->xdamage = 0; + } else - g_signal_connect_object (G_OBJECT (display), - "xevent", - G_CALLBACK (on_gl_surface_xevent), - context, - G_CONNECT_SWAPPED); + { + g_signal_connect_object (G_OBJECT (display), + "xevent", + G_CALLBACK (on_gl_surface_xevent), + context, + G_CONNECT_SWAPPED); + g_signal_connect_object (G_OBJECT (surface), + "notify::state", + G_CALLBACK (on_surface_state_changed), + context, + G_CONNECT_SWAPPED); + + } } #endif