[mutter] Fix problems resulting in left-over queued frames



commit 9745f9f8ce90525fd4cbddd0930c176ca1710ff0
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Fri Oct 17 08:49:54 2014 -0400

    Fix problems resulting in left-over queued frames
    
    * Use -1 rather than 0 as a flag for pending queue entries; 0 is
      a valid frame_counter value from Cogl.
    * Consistently handle the fact we can have more than one pending
      entry. It's app misbehavior to submit a new frame before
      _NET_WM_FRAME_DRAWN is received; but we accept such frame messages,
      so we can't just leak them.
    * If we remove send_frame_message_timer, assign the current frame counter
      to pending entries.
    * To try to avoid regressing on this, when sending _NET_WM_FRAME_TIMINGS
      messages, if we have stale messages, or messages with no frame drawn
      time, warn and remove them from the queue rather than just accumulating.
    * Improve commenting.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=738686

 src/compositor/meta-window-actor.c |  121 +++++++++++++++++++++++++-----------
 1 files changed, 85 insertions(+), 36 deletions(-)
---
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index eb21c46..254a1a9 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -100,7 +100,7 @@ struct _MetaWindowActorPrivate
   guint                    disposed               : 1;
 
   /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
-   * client message using the most recent frame in ->frames */
+   * client message for one or more messages in ->frames */
   guint             needs_frame_drawn      : 1;
   guint             repaint_scheduled      : 1;
 
@@ -118,10 +118,21 @@ struct _MetaWindowActorPrivate
 
 typedef struct _FrameData FrameData;
 
+/* Each time the application updates the sync request counter to a new even value
+ * value, we queue a frame into the windows list of frames. Once we're painting
+ * an update "in response" to the window, we fill in frame_counter with the
+ * Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the
+ * frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback.
+ *
+ * As an exception, if a window is completely obscured, we try to throttle drawning
+ * to a slower frame rate. In this case, frame_counter stays -1 until
+ * send_frame_message_timeout() runs, at which point we send both the
+ * _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages.
+ */
 struct _FrameData
 {
-  int64_t frame_counter;
   guint64 sync_request_serial;
+  int64_t frame_counter;
   gint64 frame_drawn_time;
 };
 
@@ -656,6 +667,30 @@ clip_shadow_under_window (MetaWindowActor *self)
 }
 
 static void
+assign_frame_counter_to_frames (MetaWindowActor *self)
+{
+  MetaWindowActorPrivate *priv = self->priv;
+  GList *l;
+
+  /* If the window is obscured, then we're expecting to deal with sending
+   * frame messages in a timeout, rather than in this paint cycle.
+   */
+  if (priv->send_frame_messages_timer != 0)
+    return;
+
+  for (l = priv->frames; l; l = l->next)
+    {
+      FrameData *frame = l->data;
+
+      if (frame->frame_counter == -1)
+        {
+          CoglOnscreen *onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer());
+          frame->frame_counter = cogl_onscreen_get_frame_counter (onscreen);
+        }
+    }
+}
+
+static void
 meta_window_actor_paint (ClutterActor *actor)
 {
   MetaWindowActor *self = META_WINDOW_ACTOR (actor);
@@ -671,6 +706,8 @@ meta_window_actor_paint (ClutterActor *actor)
     {
       g_source_remove (priv->send_frame_messages_timer);
       priv->send_frame_messages_timer = 0;
+
+      assign_frame_counter_to_frames (self);
     }
 
   if (shadow != NULL)
@@ -873,16 +910,27 @@ send_frame_messages_timeout (gpointer data)
 {
   MetaWindowActor *self = (MetaWindowActor *) data;
   MetaWindowActorPrivate *priv = self->priv;
-  FrameData *frame = g_slice_new0 (FrameData);
+  GList *l;
 
-  frame->sync_request_serial = priv->window->sync_request_serial;
+  for (l = priv->frames; l;)
+    {
+      GList *l_next = l->next;
+      FrameData *frame = l->data;
 
-  do_send_frame_drawn (self, frame);
-  do_send_frame_timings (self, frame, 0, 0);
+      if (frame->frame_counter == -1)
+        {
+          do_send_frame_drawn (self, frame);
+          do_send_frame_timings (self, frame, 0, 0);
+
+          priv->frames = g_list_delete_link (priv->frames, l);
+          frame_data_free (frame);
+        }
+
+      l = l_next;
+    }
 
   priv->needs_frame_drawn = FALSE;
   priv->send_frame_messages_timer = 0;
-  frame_data_free (frame);
 
   return FALSE;
 }
@@ -933,6 +981,7 @@ meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
     return;
 
   frame = g_slice_new0 (FrameData);
+  frame->frame_counter = -1;
 
   priv->needs_frame_drawn = TRUE;
 
@@ -1905,24 +1954,12 @@ meta_window_actor_handle_updates (MetaWindowActor *self)
 void
 meta_window_actor_pre_paint (MetaWindowActor *self)
 {
-  MetaWindowActorPrivate *priv = self->priv;
-  GList *l;
-
   if (meta_window_actor_is_destroyed (self))
     return;
 
   meta_window_actor_handle_updates (self);
 
-  for (l = priv->frames; l != NULL; l = l->next)
-    {
-      FrameData *frame = l->data;
-
-      if (frame->frame_counter == 0)
-        {
-          CoglOnscreen *onscreen = COGL_ONSCREEN (cogl_get_draw_framebuffer());
-          frame->frame_counter = cogl_onscreen_get_frame_counter (onscreen);
-        }
-    }
+  assign_frame_counter_to_frames (self);
 }
 
 static void
@@ -1963,16 +2000,23 @@ meta_window_actor_post_paint (MetaWindowActor *self)
   if (meta_window_actor_is_destroyed (self))
     return;
 
- /* This window had damage, but wasn't actually redrawn because
-  * it is obscured. So we should wait until timer expiration
-  * before sending _NET_WM_FRAME_* messages.
-  */
-  if (priv->send_frame_messages_timer != 0)
-    return;
-
-  if (priv->needs_frame_drawn)
+  /* If the window had damage, but wasn't actually redrawn because
+   * it is obscured, we should wait until timer expiration before
+   * sending _NET_WM_FRAME_* messages.
+   */
+  if (priv->send_frame_messages_timer == 0 &&
+      priv->needs_frame_drawn)
     {
-      do_send_frame_drawn (self, priv->frames->data);
+      GList *l;
+
+      for (l = priv->frames; l; l = l->next)
+        {
+          FrameData *frame = l->data;
+
+          if (frame->frame_drawn_time == 0)
+            do_send_frame_drawn (self, frame);
+        }
+
       priv->needs_frame_drawn = FALSE;
     }
 
@@ -2057,15 +2101,20 @@ meta_window_actor_frame_complete (MetaWindowActor *self,
     {
       GList *l_next = l->next;
       FrameData *frame = l->data;
+      gint64 frame_counter = cogl_frame_info_get_frame_counter (frame_info);
 
-      if (frame->frame_counter == cogl_frame_info_get_frame_counter (frame_info))
+      if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
         {
-          if (frame->frame_drawn_time != 0)
-            {
-              priv->frames = g_list_delete_link (priv->frames, l);
-              send_frame_timings (self, frame, frame_info, presentation_time);
-              frame_data_free (frame);
-            }
+          if (G_UNLIKELY (frame->frame_drawn_time == 0))
+            g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
+                       priv->window->desc);
+          if (G_UNLIKELY (frame->frame_counter < frame_counter))
+            g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
+                       priv->window->desc, frame->frame_counter);
+
+          priv->frames = g_list_delete_link (priv->frames, l);
+          send_frame_timings (self, frame, frame_info, presentation_time);
+          frame_data_free (frame);
         }
 
       l = l_next;


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