x11: Handle window getting unmapped while frame still pending

Since commit 972134abe4 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.
This commit is contained in:
Ray Strode 2020-06-29 10:21:58 -04:00
parent 72d3a9042c
commit e3b5b76cdd

View File

@ -582,6 +582,23 @@ create_legacy_context (GdkDisplay *display,
} }
#ifdef HAVE_XDAMAGE #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 static void
bind_context_for_frame_fence (GdkGLContext *context) bind_context_for_frame_fence (GdkGLContext *context)
{ {
@ -628,7 +645,6 @@ on_gl_surface_xevent (GdkGLContext *context,
GdkX11Display *display_x11) GdkX11Display *display_x11)
{ {
GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context); GdkX11GLContext *context_x11 = GDK_X11_GL_CONTEXT (context);
GdkSurface *surface = gdk_gl_context_get_surface (context);
XDamageNotifyEvent *damage_xevent; XDamageNotifyEvent *damage_xevent;
if (!context_x11->is_attached) if (!context_x11->is_attached)
@ -675,9 +691,7 @@ on_gl_surface_xevent (GdkGLContext *context,
case GL_WAIT_FAILED: case GL_WAIT_FAILED:
if (wait_result == GL_WAIT_FAILED) if (wait_result == GL_WAIT_FAILED)
g_warning ("failed to wait on GL fence associated with last swap buffers call"); g_warning ("failed to wait on GL fence associated with last swap buffers call");
glDeleteSync (context_x11->frame_fence); finish_frame (context);
context_x11->frame_fence = 0;
_gdk_x11_surface_set_frame_still_painting (surface, FALSE);
break; break;
/* We assume that if the fence hasn't been signaled, that this /* We assume that if the fence hasn't been signaled, that this
@ -696,6 +710,21 @@ on_gl_surface_xevent (GdkGLContext *context,
return FALSE; 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 #endif
static gboolean static gboolean
@ -878,13 +907,23 @@ gdk_x11_gl_context_realize (GdkGLContext *context,
gdk_x11_surface_get_xid (surface), gdk_x11_surface_get_xid (surface),
XDamageReportRawRectangles); XDamageReportRawRectangles);
if (gdk_x11_display_error_trap_pop (display)) if (gdk_x11_display_error_trap_pop (display))
{
context_x11->xdamage = 0; context_x11->xdamage = 0;
}
else else
{
g_signal_connect_object (G_OBJECT (display), g_signal_connect_object (G_OBJECT (display),
"xevent", "xevent",
G_CALLBACK (on_gl_surface_xevent), G_CALLBACK (on_gl_surface_xevent),
context, context,
G_CONNECT_SWAPPED); G_CONNECT_SWAPPED);
g_signal_connect_object (G_OBJECT (surface),
"notify::state",
G_CALLBACK (on_surface_state_changed),
context,
G_CONNECT_SWAPPED);
}
} }
#endif #endif