[mutter/wip/carlosg/frames-client: 17/32] compositor: Move frame drawn x11 management to MetaSyncCounter




commit 685c6809c8e482faef1a20a15f477039ea6b8bf4
Author: Carlos Garnacho <carlosg gnome org>
Date:   Thu Sep 15 00:30:23 2022 +0200

    compositor: Move frame drawn x11 management to MetaSyncCounter
    
    This is part of the same MetaSyncCounter mechanism, so move it together
    on one place.

 src/compositor/meta-window-actor-x11.c | 282 ++++-----------------------------
 src/x11/meta-sync-counter.c            | 266 ++++++++++++++++++++++++++++++-
 src/x11/meta-sync-counter.h            |  20 +++
 3 files changed, 313 insertions(+), 255 deletions(-)
---
diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c
index 4d0a8f044f..b34feec5da 100644
--- a/src/compositor/meta-window-actor-x11.c
+++ b/src/compositor/meta-window-actor-x11.c
@@ -41,6 +41,7 @@
 #include "meta/meta-x11-errors.h"
 #include "meta/window.h"
 #include "x11/window-x11.h"
+#include "x11/meta-sync-counter.h"
 #include "x11/meta-x11-display-private.h"
 #include "x11/window-x11.h"
 
@@ -54,19 +55,12 @@ struct _MetaWindowActorX11
 {
   MetaWindowActor parent;
 
-  /* List of FrameData for recent frames */
-  GList *frames;
-
   guint send_frame_messages_timer;
-  int64_t frame_drawn_time;
   gboolean pending_schedule_update_now;
 
   gulong repaint_scheduled_id;
   gulong size_changed_id;
 
-  /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
-   * client message for one or more messages in ->frames */
-  gboolean needs_frame_drawn;
   gboolean repaint_scheduled;
 
   /*
@@ -113,30 +107,6 @@ static void cullable_iface_init (MetaCullableInterface *iface);
 G_DEFINE_TYPE_WITH_CODE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR,
                          G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init))
 
-/* 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.
- */
-typedef struct
-{
-  uint64_t sync_request_serial;
-  int64_t frame_counter;
-  int64_t frame_drawn_time;
-} FrameData;
-
-static void
-frame_data_free (FrameData *frame)
-{
-  g_free (frame);
-}
-
 static void
 surface_repaint_scheduled (MetaSurfaceActor *actor,
                            gpointer          user_data)
@@ -154,165 +124,16 @@ remove_frame_messages_timer (MetaWindowActorX11 *actor_x11)
   g_clear_handle_id (&actor_x11->send_frame_messages_timer, g_source_remove);
 }
 
-static void
-do_send_frame_drawn (MetaWindowActorX11 *actor_x11,
-                     FrameData          *frame)
-{
-  MetaWindow *window =
-    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
-  MetaDisplay *display = meta_window_get_display (window);
-  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
-  int64_t now_us;
-
-  XClientMessageEvent ev = { 0, };
-
-  COGL_TRACE_BEGIN (MetaWindowActorX11FrameDrawn,
-                    "X11: Send _NET_WM_FRAME_DRAWN");
-
-  now_us = g_get_monotonic_time ();
-  frame->frame_drawn_time =
-    meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
-                                                        now_us);
-  actor_x11->frame_drawn_time = frame->frame_drawn_time;
-
-  ev.type = ClientMessage;
-  ev.window = meta_window_get_xwindow (window);
-  ev.message_type = display->x11_display->atom__NET_WM_FRAME_DRAWN;
-  ev.format = 32;
-  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
-  ev.data.l[1] = frame->sync_request_serial >> 32;
-  ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT (0xffffffff);
-  ev.data.l[3] = frame->frame_drawn_time >> 32;
-
-  meta_x11_error_trap_push (display->x11_display);
-  XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
-  XFlush (xdisplay);
-  meta_x11_error_trap_pop (display->x11_display);
-
-#ifdef COGL_HAS_TRACING
-  if (G_UNLIKELY (cogl_is_tracing_enabled ()))
-    {
-      g_autofree char *description = NULL;
-
-      description = g_strdup_printf ("frame drawn time: %" G_GINT64_FORMAT ", "
-                                     "sync request serial: %" G_GINT64_FORMAT,
-                                     frame->frame_drawn_time,
-                                     frame->sync_request_serial);
-      COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameDrawn,
-                           description);
-      COGL_TRACE_END (MetaWindowActorX11FrameDrawn);
-    }
-#endif
-}
-
-static void
-do_send_frame_timings (MetaWindowActorX11 *actor_x11,
-                       FrameData          *frame,
-                       int                 refresh_interval,
-                       int64_t             presentation_time)
-{
-  MetaWindow *window =
-    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
-  MetaDisplay *display = meta_window_get_display (window);
-  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
-
-  XClientMessageEvent ev = { 0, };
-
-  COGL_TRACE_BEGIN (MetaWindowActorX11FrameTimings,
-                    "X11: Send _NET_WM_FRAME_TIMINGS");
-
-  ev.type = ClientMessage;
-  ev.window = meta_window_get_xwindow (window);
-  ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
-  ev.format = 32;
-  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
-  ev.data.l[1] = frame->sync_request_serial >> 32;
-
-  if (presentation_time != 0)
-    {
-      MetaCompositor *compositor = display->compositor;
-      int64_t presentation_time_server;
-
-      presentation_time_server =
-        meta_compositor_monotonic_to_high_res_xserver_time (compositor,
-                                                            presentation_time);
-      int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
-      if (presentation_time_offset == 0)
-        presentation_time_offset = 1;
-
-      if ((int32_t)presentation_time_offset == presentation_time_offset)
-        ev.data.l[2] = presentation_time_offset;
-    }
-
-  ev.data.l[3] = refresh_interval;
-  ev.data.l[4] = 1000 * META_SYNC_DELAY;
-
-  meta_x11_error_trap_push (display->x11_display);
-  XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
-  XFlush (xdisplay);
-  meta_x11_error_trap_pop (display->x11_display);
-
-#ifdef COGL_HAS_TRACING
-  if (G_UNLIKELY (cogl_is_tracing_enabled ()))
-    {
-      g_autofree char *description = NULL;
-
-      description =
-        g_strdup_printf ("refresh interval: %d, "
-                         "presentation time: %" G_GINT64_FORMAT ", "
-                         "sync request serial: %" G_GINT64_FORMAT,
-                         refresh_interval,
-                         frame->sync_request_serial,
-                         presentation_time);
-      COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameTimings, description);
-      COGL_TRACE_END (MetaWindowActorX11FrameTimings);
-    }
-#endif
-}
-
-static void
-send_frame_timings (MetaWindowActorX11 *actor_x11,
-                    FrameData          *frame,
-                    ClutterFrameInfo   *frame_info,
-                    int64_t             presentation_time)
-{
-  float refresh_rate;
-  int refresh_interval;
-
-  refresh_rate = frame_info->refresh_rate;
-  /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
-  if (refresh_rate >= 1.0)
-    refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
-  else
-    refresh_interval = 0;
-
-  do_send_frame_timings (actor_x11, frame, refresh_interval, presentation_time);
-}
-
 static gboolean
 send_frame_messages_timeout (gpointer data)
 {
   MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
-  GList *l;
-
-  for (l = actor_x11->frames; l;)
-    {
-      GList *l_next = l->next;
-      FrameData *frame = l->data;
-
-      if (frame->frame_counter == -1)
-        {
-          do_send_frame_drawn (actor_x11, frame);
-          do_send_frame_timings (actor_x11, frame, 0, 0);
-
-          actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
-          frame_data_free (frame);
-        }
-
-      l = l_next;
-    }
+  MetaWindow *window =
+    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
+  MetaSyncCounter *sync_counter;
 
-  actor_x11->needs_frame_drawn = FALSE;
+  sync_counter = meta_window_x11_get_sync_counter (window);
+  meta_sync_counter_finish_incomplete (sync_counter);
   actor_x11->send_frame_messages_timer = 0;
 
   return G_SOURCE_REMOVE;
@@ -325,6 +146,7 @@ queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
     meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
   MetaDisplay *display = meta_window_get_display (window);
   MetaLogicalMonitor *logical_monitor;
+  MetaSyncCounter *sync_counter;
   int64_t now_us;
   int64_t current_time;
   float refresh_rate;
@@ -355,7 +177,8 @@ queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
     meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
                                                         now_us);
   interval = (int) (1000000 / refresh_rate) * 6;
-  offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 1000;
+  sync_counter = meta_window_x11_get_sync_counter (window);
+  offset = MAX (0, sync_counter->frame_drawn_time + interval - current_time) / 1000;
 
  /* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
   * so the timer will run *after* the clutter frame handling, if a frame is ready
@@ -376,7 +199,7 @@ assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
     meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
   MetaCompositor *compositor = window->display->compositor;
   ClutterStage *stage = meta_compositor_get_stage (compositor);
-  GList *l;
+  MetaSyncCounter *sync_counter;
 
   /* If the window is obscured, then we're expecting to deal with sending
    * frame messages in a timeout, rather than in this paint cycle.
@@ -384,13 +207,9 @@ assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
   if (actor_x11->send_frame_messages_timer != 0)
     return;
 
-  for (l = actor_x11->frames; l; l = l->next)
-    {
-      FrameData *frame = l->data;
-
-      if (frame->frame_counter == -1)
-        frame->frame_counter = clutter_stage_get_frame_counter (stage);
-    }
+  sync_counter = meta_window_x11_get_sync_counter (window);
+  meta_sync_counter_assign_counter_to_frames (sync_counter,
+                                              clutter_stage_get_frame_counter (stage));
 }
 
 static void
@@ -398,37 +217,16 @@ meta_window_actor_x11_frame_complete (MetaWindowActor  *actor,
                                       ClutterFrameInfo *frame_info,
                                       int64_t           presentation_time)
 {
-  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
-  GList *l;
+  MetaWindow *window = meta_window_actor_get_meta_window (actor);
+  MetaSyncCounter *sync_counter;
 
   if (meta_window_actor_is_destroyed (actor))
     return;
 
-  for (l = actor_x11->frames; l;)
-    {
-      GList *l_next = l->next;
-      FrameData *frame = l->data;
-      int64_t frame_counter = frame_info->frame_counter;
-
-      if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
-        {
-          MetaWindow *window =
-            meta_window_actor_get_meta_window (actor);
-
-          if (G_UNLIKELY (frame->frame_drawn_time == 0))
-            g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
-                       window->desc);
-          if (G_UNLIKELY (frame->frame_counter < frame_counter))
-            g_debug ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
-                     window->desc, frame->frame_counter);
-
-          actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
-          send_frame_timings (actor_x11, frame, frame_info, presentation_time);
-          frame_data_free (frame);
-        }
-
-      l = l_next;
-    }
+  sync_counter = meta_window_x11_get_sync_counter (window);
+  meta_sync_counter_complete_frame (sync_counter,
+                                    frame_info,
+                                    presentation_time);
 }
 
 static MetaSurfaceActor *
@@ -501,23 +299,10 @@ meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
                                          gboolean         skip_sync_delay)
 {
   MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
-  MetaWindow *window =
-    meta_window_actor_get_meta_window (actor);
-  MetaSyncCounter *sync_counter =
-    meta_window_x11_get_sync_counter (window);
-  FrameData *frame;
 
   if (meta_window_actor_is_destroyed (actor))
     return;
 
-  frame = g_new0 (FrameData, 1);
-  frame->frame_counter = -1;
-  frame->sync_request_serial = sync_counter->sync_request_serial;
-
-  actor_x11->frames = g_list_prepend (actor_x11->frames, frame);
-
-  actor_x11->needs_frame_drawn = TRUE;
-
   if (skip_sync_delay)
     {
       ClutterFrameClock *frame_clock;
@@ -1406,6 +1191,7 @@ meta_window_actor_x11_after_paint (MetaWindowActor  *actor,
                                    ClutterStageView *stage_view)
 {
   MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+  MetaSyncCounter *sync_counter;
   MetaWindow *window;
 
   actor_x11->repaint_scheduled = FALSE;
@@ -1413,28 +1199,19 @@ meta_window_actor_x11_after_paint (MetaWindowActor  *actor,
   if (meta_window_actor_is_destroyed (actor))
     return;
 
+  window = meta_window_actor_get_meta_window (actor);
+
   /* 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 (actor_x11->send_frame_messages_timer == 0 &&
-      actor_x11->needs_frame_drawn)
+  if (actor_x11->send_frame_messages_timer == 0)
     {
-      GList *l;
-
-      for (l = actor_x11->frames; l; l = l->next)
-        {
-          FrameData *frame = l->data;
-
-          if (frame->frame_drawn_time == 0)
-            do_send_frame_drawn (actor_x11, frame);
-        }
-
-      actor_x11->needs_frame_drawn = FALSE;
+      sync_counter = meta_window_x11_get_sync_counter (window);
+      meta_sync_counter_send_frame_drawn (sync_counter);
     }
 
   /* This is for Xwayland, and a no-op on plain Xorg */
-  window = meta_window_actor_get_meta_window (actor);
   if (meta_window_x11_should_thaw_after_paint (window))
     {
       meta_window_x11_thaw_commits (window);
@@ -1620,7 +1397,10 @@ meta_window_actor_x11_constructed (GObject *object)
    */
   if (sync_counter->extended_sync_request_counter &&
       !meta_window_updates_are_frozen (window))
-    meta_window_actor_queue_frame_drawn (actor, FALSE);
+    {
+      meta_sync_counter_queue_frame_drawn (sync_counter);
+      meta_window_actor_queue_frame_drawn (actor, FALSE);
+    }
 }
 
 static void
@@ -1688,10 +1468,6 @@ meta_window_actor_x11_dispose (GObject *object)
 static void
 meta_window_actor_x11_finalize (GObject *object)
 {
-  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
-
-  g_list_free_full (actor_x11->frames, (GDestroyNotify) frame_data_free);
-
   G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->finalize (object);
 }
 
diff --git a/src/x11/meta-sync-counter.c b/src/x11/meta-sync-counter.c
index 1e3b3c4cd9..346ac49b49 100644
--- a/src/x11/meta-sync-counter.c
+++ b/src/x11/meta-sync-counter.c
@@ -21,10 +21,29 @@
 
 #include "meta-sync-counter.h"
 
+#include "compositor/compositor-private.h"
 #include "core/window-private.h"
 #include "meta/meta-x11-errors.h"
 #include "x11/meta-x11-display-private.h"
 
+/* 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.
+ */
+typedef struct
+{
+  uint64_t sync_request_serial;
+  int64_t frame_counter;
+  int64_t frame_drawn_time;
+} FrameData;
+
 void
 meta_sync_counter_init (MetaSyncCounter *sync_counter,
                         MetaWindow      *window,
@@ -39,6 +58,7 @@ meta_sync_counter_clear (MetaSyncCounter *sync_counter)
 {
   g_clear_handle_id (&sync_counter->sync_request_timeout_id, g_source_remove);
   meta_sync_counter_destroy_sync_alarm (sync_counter);
+  g_list_free_full (sync_counter->frames, g_free);
   sync_counter->window = NULL;
   sync_counter->xwindow = None;
 }
@@ -312,8 +332,11 @@ meta_sync_counter_update (MetaSyncCounter *sync_counter,
   sync_counter->disabled = FALSE;
 
   if (needs_frame_drawn)
-    meta_compositor_queue_frame_drawn (window->display->compositor, window,
-                                       no_delay_frame);
+    {
+      meta_sync_counter_queue_frame_drawn (sync_counter);
+      meta_compositor_queue_frame_drawn (window->display->compositor, window,
+                                         no_delay_frame);
+    }
 
 #ifdef COGL_HAS_TRACING
   if (G_UNLIKELY (cogl_is_tracing_enabled ()))
@@ -349,3 +372,242 @@ meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter)
 {
   return sync_counter->sync_request_timeout_id != 0;
 }
+
+static void
+do_send_frame_drawn (MetaSyncCounter *sync_counter,
+                     FrameData       *frame)
+{
+  MetaWindow *window = sync_counter->window;
+  MetaDisplay *display = meta_window_get_display (window);
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
+  int64_t now_us;
+  XClientMessageEvent ev = { 0, };
+
+  COGL_TRACE_BEGIN (MetaWindowActorX11FrameDrawn,
+                    "X11: Send _NET_WM_FRAME_DRAWN");
+
+  now_us = g_get_monotonic_time ();
+  frame->frame_drawn_time =
+    meta_compositor_monotonic_to_high_res_xserver_time (display->compositor,
+                                                        now_us);
+  sync_counter->frame_drawn_time = frame->frame_drawn_time;
+
+  ev.type = ClientMessage;
+  ev.window = sync_counter->xwindow;
+  ev.message_type = display->x11_display->atom__NET_WM_FRAME_DRAWN;
+  ev.format = 32;
+  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[1] = frame->sync_request_serial >> 32;
+  ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[3] = frame->frame_drawn_time >> 32;
+
+  meta_x11_error_trap_push (display->x11_display);
+  XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
+  XFlush (xdisplay);
+  meta_x11_error_trap_pop (display->x11_display);
+
+#ifdef COGL_HAS_TRACING
+  if (G_UNLIKELY (cogl_is_tracing_enabled ()))
+    {
+      g_autofree char *description = NULL;
+
+      description = g_strdup_printf ("frame drawn time: %" G_GINT64_FORMAT ", "
+                                     "sync request serial: %" G_GINT64_FORMAT,
+                                     frame->frame_drawn_time,
+                                     frame->sync_request_serial);
+      COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameDrawn,
+                           description);
+      COGL_TRACE_END (MetaWindowActorX11FrameDrawn);
+    }
+#endif
+}
+
+static void
+do_send_frame_timings (MetaSyncCounter *sync_counter,
+                       FrameData       *frame,
+                       int              refresh_interval,
+                       int64_t          presentation_time)
+{
+  MetaWindow *window = sync_counter->window;
+  MetaDisplay *display = meta_window_get_display (window);
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
+
+  XClientMessageEvent ev = { 0, };
+
+  COGL_TRACE_BEGIN (MetaWindowActorX11FrameTimings,
+                    "X11: Send _NET_WM_FRAME_TIMINGS");
+
+  ev.type = ClientMessage;
+  ev.window = sync_counter->xwindow;
+  ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
+  ev.format = 32;
+  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[1] = frame->sync_request_serial >> 32;
+
+  if (presentation_time != 0)
+    {
+      MetaCompositor *compositor = display->compositor;
+      int64_t presentation_time_server;
+
+      presentation_time_server =
+        meta_compositor_monotonic_to_high_res_xserver_time (compositor,
+                                                            presentation_time);
+      int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
+      if (presentation_time_offset == 0)
+        presentation_time_offset = 1;
+
+      if ((int32_t)presentation_time_offset == presentation_time_offset)
+        ev.data.l[2] = presentation_time_offset;
+    }
+
+  ev.data.l[3] = refresh_interval;
+  ev.data.l[4] = 1000 * META_SYNC_DELAY;
+
+  meta_x11_error_trap_push (display->x11_display);
+  XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
+  XFlush (xdisplay);
+  meta_x11_error_trap_pop (display->x11_display);
+
+#ifdef COGL_HAS_TRACING
+  if (G_UNLIKELY (cogl_is_tracing_enabled ()))
+    {
+      g_autofree char *description = NULL;
+
+      description =
+        g_strdup_printf ("refresh interval: %d, "
+                         "presentation time: %" G_GINT64_FORMAT ", "
+                         "sync request serial: %" G_GINT64_FORMAT,
+                         refresh_interval,
+                         frame->sync_request_serial,
+                         presentation_time);
+      COGL_TRACE_DESCRIBE (MetaWindowActorX11FrameTimings, description);
+      COGL_TRACE_END (MetaWindowActorX11FrameTimings);
+    }
+#endif
+}
+
+static void
+send_frame_timings (MetaSyncCounter  *sync_counter,
+                    FrameData        *frame,
+                    ClutterFrameInfo *frame_info,
+                    int64_t           presentation_time)
+{
+  float refresh_rate;
+  int refresh_interval;
+
+  refresh_rate = frame_info->refresh_rate;
+  /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
+  if (refresh_rate >= 1.0)
+    refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
+  else
+    refresh_interval = 0;
+
+  do_send_frame_timings (sync_counter, frame,
+                         refresh_interval, presentation_time);
+}
+
+void
+meta_sync_counter_queue_frame_drawn (MetaSyncCounter *sync_counter)
+{
+  FrameData *frame;
+
+  frame = g_new0 (FrameData, 1);
+  frame->frame_counter = -1;
+  frame->sync_request_serial = sync_counter->sync_request_serial;
+
+  sync_counter->frames = g_list_prepend (sync_counter->frames, frame);
+
+  sync_counter->needs_frame_drawn = TRUE;
+}
+
+void
+meta_sync_counter_assign_counter_to_frames (MetaSyncCounter *sync_counter,
+                                            int64_t          counter)
+{
+  GList *l;
+
+  for (l = sync_counter->frames; l; l = l->next)
+    {
+      FrameData *frame = l->data;
+
+      if (frame->frame_counter == -1)
+        frame->frame_counter = counter;
+    }
+}
+
+void
+meta_sync_counter_complete_frame (MetaSyncCounter  *sync_counter,
+                                  ClutterFrameInfo *frame_info,
+                                  int64_t           presentation_time)
+{
+  GList *l;
+
+  for (l = sync_counter->frames; l;)
+    {
+      GList *l_next = l->next;
+      FrameData *frame = l->data;
+      int64_t frame_counter = frame_info->frame_counter;
+
+      if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
+        {
+          MetaWindow *window = sync_counter->window;
+
+          if (G_UNLIKELY (frame->frame_drawn_time == 0))
+            g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
+                       window->desc);
+          if (G_UNLIKELY (frame->frame_counter < frame_counter))
+            g_debug ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
+                     window->desc, frame->frame_counter);
+
+          sync_counter->frames = g_list_delete_link (sync_counter->frames, l);
+          send_frame_timings (sync_counter, frame, frame_info, presentation_time);
+          g_free (frame);
+        }
+
+      l = l_next;
+    }
+}
+
+void
+meta_sync_counter_finish_incomplete (MetaSyncCounter *sync_counter)
+{
+  GList *l;
+
+  for (l = sync_counter->frames; l;)
+    {
+      GList *l_next = l->next;
+      FrameData *frame = l->data;
+
+      if (frame->frame_counter == -1)
+        {
+          do_send_frame_drawn (sync_counter, frame);
+          do_send_frame_timings (sync_counter, frame, 0, 0);
+
+          sync_counter->frames = g_list_delete_link (sync_counter->frames, l);
+          g_free (frame);
+        }
+
+      l = l_next;
+    }
+
+  sync_counter->needs_frame_drawn = FALSE;
+}
+
+void
+meta_sync_counter_send_frame_drawn (MetaSyncCounter *sync_counter)
+{
+  GList *l;
+
+  if (!sync_counter->needs_frame_drawn)
+    return;
+
+  for (l = sync_counter->frames; l; l = l->next)
+    {
+      FrameData *frame = l->data;
+
+      if (frame->frame_drawn_time == 0)
+        do_send_frame_drawn (sync_counter, frame);
+    }
+
+  sync_counter->needs_frame_drawn = FALSE;
+}
diff --git a/src/x11/meta-sync-counter.h b/src/x11/meta-sync-counter.h
index 975ac5dee6..199bd6dba5 100644
--- a/src/x11/meta-sync-counter.h
+++ b/src/x11/meta-sync-counter.h
@@ -35,10 +35,17 @@ typedef struct {
   guint sync_request_timeout_id;
   /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
   XSyncAlarm sync_request_alarm;
+
+  int64_t frame_drawn_time;
+  GList *frames;
+
   /* if TRUE, the we have the new form of sync request counter which
    * also handles application frames */
   guint extended_sync_request_counter : 1;
   guint disabled : 1;
+  /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
+   * client message for one or more messages in ->frames */
+  guint needs_frame_drawn : 1;
 } MetaSyncCounter;
 
 void meta_sync_counter_init (MetaSyncCounter *sync_counter,
@@ -66,4 +73,17 @@ gboolean meta_sync_counter_is_waiting (MetaSyncCounter *sync_counter);
 
 gboolean meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter);
 
+void meta_sync_counter_queue_frame_drawn (MetaSyncCounter *sync_counter);
+
+void meta_sync_counter_assign_counter_to_frames (MetaSyncCounter *sync_counter,
+                                                 int64_t          counter);
+
+void meta_sync_counter_complete_frame (MetaSyncCounter  *sync_counter,
+                                       ClutterFrameInfo *frame_info,
+                                       int64_t           presentation_time);
+
+void meta_sync_counter_finish_incomplete (MetaSyncCounter *sync_counter);
+
+void meta_sync_counter_send_frame_drawn (MetaSyncCounter *sync_counter);
+
 #endif /* META_SYNC_COUNTER_H */


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