summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2020-07-14 09:12:39 -0400
committerRay Strode <rstrode@redhat.com>2020-07-14 09:12:39 -0400
commitf05f5ec554a146cf4e1df71af05a3f3d909091ad (patch)
tree715db2b3978474a19d1f61ef247bfd4ca3ff8c53
parent577126e99c59d7dea56ea686afbcdbf8c1e17e22 (diff)
downloadgtk+-f05f5ec554a146cf4e1df71af05a3f3d909091ad.tar.gz
x11: Don't let compositor process frame unless large enough damage is posted
As of commit 972134abe48a4c9c7b6ad41b0723f30f4e7ae16b, under the vendor nvidia driver, GTK now inhibits the compositor from processing frame updates until a fence signals that the GPU is finished drawing, and until damage is reported from the X server. It assumes that if the fence has been signaled, that any damage event after the fence signaling is associated with the fence. That's, technically, not necessarly always true. The X server could have generated damage for an unrelated event (say the size of the window changing), at just the right moment and get in the way. This commit compensates for that eventuality by copying the painted region from gdk_x11_gl_context_end_frame, and subtracting the damaged areas from the copy as they come in. Once the copied region goes empty, we know that there won't be any underdraw and can mark the painting as suitable for the compositor to process.
-rw-r--r--gdk/x11/gdkglcontext-x11.c54
-rw-r--r--gdk/x11/gdkglcontext-x11.h1
2 files changed, 33 insertions, 22 deletions
diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c
index 9fdbca9c89..37efcf6c10 100644
--- a/gdk/x11/gdkglcontext-x11.c
+++ b/gdk/x11/gdkglcontext-x11.c
@@ -188,13 +188,15 @@ gdk_x11_gl_context_end_frame (GdkDrawContext *draw_context,
{
g_assert (context_x11->frame_fence == 0);
- context_x11->frame_fence = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
-
/* We consider the frame still getting painted until the GL operation is
- * finished, and the window gets damage reported from the X server.
- * It's only at this point the compositor can be sure it has full
+ * finished, and a suitably large enough damage area is reported from
+ * the X server. It's only at this point the compositor can be sure it has full
* access to the new updates.
*/
+ context_x11->frame_fence = glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ g_clear_pointer (&context_x11->pending_damage_region,
+ cairo_region_destroy);
+ context_x11->pending_damage_region = cairo_region_copy (painted);
_gdk_x11_surface_set_frame_still_painting (surface, TRUE);
}
#endif
@@ -662,30 +664,36 @@ on_gl_surface_xevent (GdkGLContext *context,
{
GLenum wait_result;
+ /* It's conceivable that this damage event is unrelated to the drawing
+ * of the current frame (say it was X server generated from the window
+ * resizing or something). There's no way to exactly finger any given
+ * damage event as "the one caused by the frame getting drawn", but we
+ * don't really need to know that anyway. All we need to know is two
+ * things:
+ *
+ * 1. the damaged area the compositor sees is big enough to accomodate
+ * the frame update
+ *
+ * 2. the frame update is available for the compostor to use.
+ *
+ * We check both below.
+ */
+ cairo_region_subtract_rectangle (context_x11->pending_damage_region,
+ &(cairo_rectangle_int_t)
+ { damage_xevent->area.x,
+ damage_xevent->area.y,
+ damage_xevent->area.width,
+ damage_xevent->area.height });
+
+ if (!cairo_region_is_empty (context_x11->pending_damage_region))
+ return FALSE;
+
bind_context_for_frame_fence (context);
wait_result = glClientWaitSync (context_x11->frame_fence, 0, 0);
switch (wait_result)
{
- /* We assume that if the fence has been signaled, that this damage
- * event is the damage event that was triggered by the GL drawing
- * associated with the fence. That's, technically, not necessarly
- * always true. The X server could have generated damage for
- * an unrelated event (say the size of the window changing), at
- * just the right moment such that we're picking it up instead.
- *
- * We're choosing not to handle this edge case, but if it does ever
- * happen in the wild, it could lead to slight underdrawing by
- * the compositor for one frame. In the future, if we find out
- * this edge case is noticeable, we can compensate by copying the
- * painted region from gdk_x11_gl_context_end_frame and subtracting
- * damaged areas from the copy as they come in. Once the copied
- * region goes empty, we know that there won't be any underdraw,
- * and can mark painting has finished. It's not worth the added
- * complexity and resource usage to do this bookkeeping, however,
- * unless the problem is practically visible.
- */
case GL_ALREADY_SIGNALED:
case GL_CONDITION_SATISFIED:
case GL_WAIT_FAILED:
@@ -958,6 +966,8 @@ gdk_x11_gl_context_dispose (GObject *gobject)
#ifdef HAVE_XDAMAGE
context_x11->xdamage = 0;
+ g_clear_pointer (&context_x11->pending_damage_region,
+ cairo_region_destroy);
#endif
G_OBJECT_CLASS (gdk_x11_gl_context_parent_class)->dispose (gobject);
diff --git a/gdk/x11/gdkglcontext-x11.h b/gdk/x11/gdkglcontext-x11.h
index b17ed2956e..b7de764d94 100644
--- a/gdk/x11/gdkglcontext-x11.h
+++ b/gdk/x11/gdkglcontext-x11.h
@@ -51,6 +51,7 @@ struct _GdkX11GLContext
#ifdef HAVE_XDAMAGE
GLsync frame_fence;
Damage xdamage;
+ cairo_region_t *pending_damage_region;
#endif
guint is_attached : 1;