[gtk/wip/on-the-surface-good-fences-can-make-bad-neighbors] x11: Handle window getting unmap while frame still pending



commit f2f61bfed61a26ba3a3bd07f45b19558f66d8b0f
Author: Ray Strode <rstrode redhat com>
Date:   Mon Jun 29 10:21:58 2020 -0400

    x11: Handle window getting unmap 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 processed, 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.
    
    Fixes: #2902

 gdk/x11/gdkglcontext-x11.c | 59 ++++++++++++++++++++++++++++++++++++++--------
 gdk/x11/gdksurface-x11.c   |  8 +++++++
 2 files changed, 57 insertions(+), 10 deletions(-)
---
diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c
index f0393d4802..8e027bd7ef 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 (display),
+                                   "notify::state",
+                                   G_CALLBACK (on_surface_state_changed),
+                                   context,
+                                   G_CONNECT_SWAPPED);
+
+        }
     }
 #endif
 
diff --git a/gdk/x11/gdksurface-x11.c b/gdk/x11/gdksurface-x11.c
index 54bf26aae6..c4dbf629ac 100644
--- a/gdk/x11/gdksurface-x11.c
+++ b/gdk/x11/gdksurface-x11.c
@@ -1374,6 +1374,14 @@ gdk_x11_surface_hide (GdkSurface *surface)
   _gdk_x11_surface_grab_check_unmap (surface,
                                     NextRequest (GDK_SURFACE_XDISPLAY (surface)));
 
+#ifdef HAVE_XDAMAGE
+  /* If we're about to withdraw the surface, then we don't care that the frame is
+   * still getting rendered by the GPU. The compositor is going to remove the surface
+   * from the scene anyway.
+   */
+  _gdk_x11_surface_set_frame_still_painting (surface, FALSE);
+#endif
+
   gdk_x11_surface_withdraw (surface);
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]