[Cogl] [PATCH 1/3] Add CoglFrameTimings

Robert Bragg robert at sixbynine.org
Tue Dec 4 07:47:25 PST 2012


From: "Owen W. Taylor" <otaylor at fishsoup.net>

Add a CoglFrameTimings object that tracks timing information
for frames that are drawn. We track a frame counter and frame
timing information for each CoglOnscreen. Frames timing information
is deliminated by a new cogl_onscreen_begin_frame() and retrieved using
cogl_onscreen_get_frame_timings().
---
 cogl/Makefile.am                  |    3 +
 cogl/cogl-frame-timings-private.h |   44 +++++++
 cogl/cogl-frame-timings.c         |   78 ++++++++++++
 cogl/cogl-frame-timings.h         |  133 ++++++++++++++++++++
 cogl/cogl-glx-display-private.h   |    1 +
 cogl/cogl-glx-renderer-private.h  |    9 ++
 cogl/cogl-onscreen-private.h      |   28 ++++
 cogl/cogl-onscreen.c              |  114 +++++++++++++++++
 cogl/cogl-onscreen.h              |  118 ++++++++++++++++++
 cogl/cogl-x11-renderer-private.h  |    1 +
 cogl/winsys/cogl-winsys-glx.c     |  247 ++++++++++++++++++++++++++++++++++---
 11 files changed, 761 insertions(+), 15 deletions(-)
 create mode 100644 cogl/cogl-frame-timings-private.h
 create mode 100644 cogl/cogl-frame-timings.c
 create mode 100644 cogl/cogl-frame-timings.h

diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 022caf5..efa94c4 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -104,6 +104,7 @@ cogl_experimental_h = \
 	$(srcdir)/cogl-attribute.h 		\
 	$(srcdir)/cogl-primitive.h 		\
 	$(srcdir)/cogl-clip-state.h		\
+	$(srcdir)/cogl-frame-timings.h		\
 	$(srcdir)/cogl-framebuffer.h		\
 	$(srcdir)/cogl-onscreen.h		\
 	$(srcdir)/cogl-output.h			\
@@ -342,6 +343,8 @@ cogl_sources_c = \
 	$(srcdir)/cogl-spans.c				\
 	$(srcdir)/cogl-journal-private.h		\
 	$(srcdir)/cogl-journal.c			\
+	$(srcdir)/cogl-frame-timings-private.h		\
+	$(srcdir)/cogl-frame-timings.c			\
 	$(srcdir)/cogl-framebuffer-private.h		\
 	$(srcdir)/cogl-framebuffer.c 			\
 	$(srcdir)/cogl-onscreen-private.h		\
diff --git a/cogl/cogl-frame-timings-private.h b/cogl/cogl-frame-timings-private.h
new file mode 100644
index 0000000..b17cd34
--- /dev/null
+++ b/cogl/cogl-frame-timings-private.h
@@ -0,0 +1,44 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifndef __COGL_FRAME_TIMINGS_PRIVATE_H
+#define __COGL_FRAME_TIMINGS_PRIVATE_H
+
+#include "cogl-frame-timings.h"
+#include "cogl-object-private.h"
+
+struct _CoglFrameTimings
+{
+  CoglObject _parent;
+
+  int64_t frame_counter;
+  int64_t frame_time;
+  int64_t presentation_time;
+  int64_t refresh_interval;
+
+  guint complete : 1;
+};
+
+CoglFrameTimings *_cogl_frame_timings_new (void);
+
+#endif /* __COGL_FRAME_TIMINGS_PRIVATE_H */
diff --git a/cogl/cogl-frame-timings.c b/cogl/cogl-frame-timings.c
new file mode 100644
index 0000000..ba8003e
--- /dev/null
+++ b/cogl/cogl-frame-timings.c
@@ -0,0 +1,78 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-frame-timings-private.h"
+
+static void _cogl_frame_timings_free (CoglFrameTimings *frame_timings);
+
+COGL_OBJECT_DEFINE (FrameTimings, frame_timings);
+
+CoglFrameTimings *
+_cogl_frame_timings_new (void)
+{
+  CoglFrameTimings *timings;
+
+  timings = g_slice_new0 (CoglFrameTimings);
+
+  return _cogl_frame_timings_object_new (timings);
+}
+
+static void
+_cogl_frame_timings_free (CoglFrameTimings *timings)
+{
+  g_slice_free (CoglFrameTimings, timings);
+}
+
+CoglBool
+cogl_frame_timings_get_complete (CoglFrameTimings *timings)
+{
+  return timings->complete;
+}
+
+gint64
+cogl_frame_timings_get_frame_counter (CoglFrameTimings *timings)
+{
+  return timings->frame_counter;
+}
+
+gint64
+cogl_frame_timings_get_frame_time (CoglFrameTimings *timings)
+{
+  return timings->frame_time;
+}
+
+gint64
+cogl_frame_timings_get_presentation_time (CoglFrameTimings *timings)
+{
+  return timings->presentation_time;
+}
+
+gint64
+cogl_frame_timings_get_refresh_interval (CoglFrameTimings *timings)
+{
+  return timings->refresh_interval;
+}
diff --git a/cogl/cogl-frame-timings.h b/cogl/cogl-frame-timings.h
new file mode 100644
index 0000000..c00e920
--- /dev/null
+++ b/cogl/cogl-frame-timings.h
@@ -0,0 +1,133 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ *
+ *
+ * Authors:
+ *   Owen Taylor <otaylor at redhat.com>
+ */
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_FRAME_TIMINGS_H
+#define __COGL_FRAME_TIMINGS_H
+
+#include <cogl/cogl-types.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _CoglFrameTimings CoglFrameTimings;
+#define COGL_FRAME_TIMINGS(X) ((CoglFrameTimings *)(X))
+
+/**
+ * cogl_is_frame_timings:
+ * @object: A #CoglObject pointer
+ *
+ * Gets whether the given object references a #CoglFrameTimings.
+ *
+ * Return value: %TRUE if the object references a #CoglFrameTimings
+ *   and %FALSE otherwise.
+ * Since: 2.0
+ * Stability: unstable
+ */
+CoglBool
+cogl_is_frame_timings (void *object);
+
+/**
+ * cogl_frame_timings_get_complete:
+ * @timings: a #CoglFrameTimings object
+ *
+ * Gets whether all information that will potentially be provided for
+ * the frame has been provided. Once a frame timings object is complete,
+ * no further changes will be made to it.
+ *
+ * Return value: whether the frame timings object is complete.
+ * Since: 2.0
+ * Stability: unstable
+ */
+CoglBool cogl_frame_timings_get_complete (CoglFrameTimings *timings);
+
+/**
+ * cogl_frame_timings_get_frame_counter:
+ * @timings: a #CoglFrameTimings object
+ *
+ * Gets the frame counter for the #CoglOnscreen that corresponds
+ * to this frame.
+ *
+ * Return value: The frame counter value
+ * Since: 2.0
+ * Stability: unstable
+ */
+int64_t cogl_frame_timings_get_frame_counter (CoglFrameTimings *timings);
+
+/**
+ * cogl_frame_timings_get_frame_time:
+ * @timings: a #CoglFrameTimings object
+ *
+ * Gets the time used for creating content for the frame. This
+ * is determined by the time passed to cogl_onscreen_begin_frame(),
+ * and will typically be the current time when rendering started
+ * for the frame.
+ *
+ * Return value: the time used for coreating content for the frame,
+ *  in the timescale of g_get_monotonic_time().
+ * Since: 2.0
+ * Stability: unstable
+ */
+int64_t cogl_frame_timings_get_frame_time (CoglFrameTimings *timings);
+
+/**
+ * cogl_frame_timings_get_presentation_time:
+ * @timings: a #CoglFrameTimings object
+ *
+ * Gets the presentation time for the frame. This is the time at which
+ * the frame became visible to the user.
+ *
+ * Return value: the presentation time for the frame, in
+ *  the timescale of g_get_monotonic_time().
+ * Since: 2.0
+ * Stability: unstable
+ */
+int64_t cogl_frame_timings_get_presentation_time (CoglFrameTimings *timings);
+
+/**
+ * cogl_frame_timings_get_refresh_interval:
+ * @timings: a #CoglFrameTimings object
+ *
+ * Gets the refresh interval for the output that the frame was on at the
+ * time the frame was presented. This is the number of microseconds between
+ * refreshes of the screen, and is equal to 1000000 / refresh_rate.
+ *
+ * Return value: the refresh interval, in microsecoonds.
+ *  .
+ * Since: 2.0
+ * Stability: unstable
+ */
+int64_t cogl_frame_timings_get_refresh_interval (CoglFrameTimings *timings);
+
+G_END_DECLS
+
+#endif /* __COGL_FRAME_TIMINGS_H */
+
+
+
diff --git a/cogl/cogl-glx-display-private.h b/cogl/cogl-glx-display-private.h
index 1ffcd32..cb0fd0d 100644
--- a/cogl/cogl-glx-display-private.h
+++ b/cogl/cogl-glx-display-private.h
@@ -52,6 +52,7 @@ typedef struct _CoglGLXDisplay
   Window dummy_xwin;
   CoglBool pending_swap_notify;
   CoglBool pending_resize_notify;
+  CoglBool pending_frame_timings_notify;
 } CoglGLXDisplay;
 
 #endif /* __COGL_DISPLAY_GLX_PRIVATE_H */
diff --git a/cogl/cogl-glx-renderer-private.h b/cogl/cogl-glx-renderer-private.h
index 4415ea5..ec9fb66 100644
--- a/cogl/cogl-glx-renderer-private.h
+++ b/cogl/cogl-glx-renderer-private.h
@@ -42,6 +42,15 @@ typedef struct _CoglGLXRenderer
   /* Vblank stuff */
   int dri_fd;
 
+  /* enumeration with relatioship between OML_sync_control
+   * UST (unadjusted-system-time) and the system clock */
+  enum {
+    COGL_GLX_UST_IS_UNKNOWN,
+    COGL_GLX_UST_IS_GETTIMEOFDAY,
+    COGL_GLX_UST_IS_MONOTONIC_TIME,
+    COGL_GLX_UST_IS_OTHER
+  } ust_type;
+
   /* GModule pointing to libGL which we use to get glX functions out of */
   GModule *libgl_module;
 
diff --git a/cogl/cogl-onscreen-private.h b/cogl/cogl-onscreen-private.h
index 06bdd4d..8c35f30 100644
--- a/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl-onscreen-private.h
@@ -33,6 +33,8 @@
 #include <windows.h>
 #endif
 
+#define COGL_ONSCREEN_MAX_FRAME_TIMINGS 16
+
 typedef struct _CoglSwapBuffersNotifyEntry CoglSwapBuffersNotifyEntry;
 
 COGL_TAILQ_HEAD (CoglSwapBuffersNotifyList, CoglSwapBuffersNotifyEntry);
@@ -59,6 +61,19 @@ struct _CoglResizeNotifyEntry
   unsigned int id;
 };
 
+typedef struct _CoglFrameTimingsCallbackEntry CoglFrameTimingsCallbackEntry;
+
+COGL_TAILQ_HEAD (CoglFrameTimingsCallbackList, CoglFrameTimingsCallbackEntry);
+
+struct _CoglFrameTimingsCallbackEntry
+{
+  COGL_TAILQ_ENTRY (CoglFrameTimingsCallbackEntry) list_node;
+
+  CoglFrameTimingsCallback callback;
+  void *user_data;
+  unsigned int id;
+};
+
 struct _CoglOnscreen
 {
   CoglFramebuffer  _parent;
@@ -80,6 +95,16 @@ struct _CoglOnscreen
   CoglBool resizable;
   CoglResizeNotifyList resize_callbacks;
 
+  CoglFrameTimingsCallbackList frame_timings_callbacks;
+
+  int64_t frame_counter;
+  int64_t swap_frame_counter; /* frame counter at last all to
+                               * cogl_onscreen_swap_region() or
+                               * cogl_onscreen_swap_buffers() */
+  CoglFrameTimings *frame_timings[COGL_ONSCREEN_MAX_FRAME_TIMINGS];
+  int current_frame_timings;
+  int n_frame_timings;
+
   void *winsys;
 };
 
@@ -96,4 +121,7 @@ _cogl_onscreen_notify_swap_buffers (CoglOnscreen *onscreen);
 void
 _cogl_onscreen_notify_resize (CoglOnscreen *onscreen);
 
+void
+_cogl_onscreen_notify_frame_timings (CoglOnscreen *onscreen);
+
 #endif /* __COGL_ONSCREEN_PRIVATE_H */
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index 4aaa5de..39ffa8d 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -27,6 +27,7 @@
 
 #include "cogl-util.h"
 #include "cogl-onscreen-private.h"
+#include "cogl-frame-timings-private.h"
 #include "cogl-framebuffer-private.h"
 #include "cogl-onscreen-template-private.h"
 #include "cogl-context-private.h"
@@ -35,6 +36,8 @@
 
 static void _cogl_onscreen_free (CoglOnscreen *onscreen);
 
+static void cogl_onscreen_before_swap (CoglOnscreen *onscreen);
+
 COGL_OBJECT_DEFINE_WITH_CODE (Onscreen, onscreen,
                               _cogl_onscreen_class.virt_unref =
                               _cogl_framebuffer_unref);
@@ -47,6 +50,7 @@ _cogl_onscreen_init_from_template (CoglOnscreen *onscreen,
 
   COGL_TAILQ_INIT (&onscreen->swap_callbacks);
   COGL_TAILQ_INIT (&onscreen->resize_callbacks);
+  COGL_TAILQ_INIT (&onscreen->frame_timings_callbacks);
 
   framebuffer->config = onscreen_template->config;
   cogl_object_ref (framebuffer->config.swap_chain);
@@ -75,6 +79,9 @@ _cogl_onscreen_new (void)
 
   COGL_FRAMEBUFFER (onscreen)->allocated = TRUE;
 
+  onscreen->frame_counter = -1;
+  onscreen->current_frame_timings = COGL_ONSCREEN_MAX_FRAME_TIMINGS - 1;
+
   /* XXX: Note we don't initialize onscreen->winsys in this case. */
 
   return _cogl_onscreen_object_new (onscreen);
@@ -149,6 +156,8 @@ cogl_onscreen_swap_buffers (CoglOnscreen *onscreen)
 
   _COGL_RETURN_IF_FAIL  (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN);
 
+  cogl_onscreen_before_swap (onscreen);
+
   /* FIXME: we shouldn't need to flush *all* journals here! */
   cogl_flush ();
   winsys = _cogl_framebuffer_get_winsys (framebuffer);
@@ -169,6 +178,8 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
 
   _COGL_RETURN_IF_FAIL  (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN);
 
+  cogl_onscreen_before_swap (onscreen);
+
   /* FIXME: we shouldn't need to flush *all* journals here! */
   cogl_flush ();
 
@@ -462,3 +473,106 @@ cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen,
     }
 }
 
+int64_t
+cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen)
+{
+  return onscreen->frame_counter;
+}
+
+void
+cogl_onscreen_begin_frame (CoglOnscreen *onscreen,
+                           gint64        frame_time)
+{
+  onscreen->frame_counter++;
+  onscreen->current_frame_timings = (onscreen->current_frame_timings + 1) % COGL_ONSCREEN_MAX_FRAME_TIMINGS;
+
+  if (onscreen->n_frame_timings < COGL_ONSCREEN_MAX_FRAME_TIMINGS)
+    onscreen->n_frame_timings++;
+  else
+    cogl_object_unref (onscreen->frame_timings[onscreen->current_frame_timings]);
+
+  onscreen->frame_timings[onscreen->current_frame_timings] = _cogl_frame_timings_new ();
+  onscreen->frame_timings[onscreen->current_frame_timings]->frame_counter = onscreen->frame_counter;
+  onscreen->frame_timings[onscreen->current_frame_timings]->frame_time = frame_time;
+}
+
+static void
+cogl_onscreen_before_swap (CoglOnscreen *onscreen)
+{
+  if (onscreen->swap_frame_counter == onscreen->frame_counter)
+    cogl_onscreen_begin_frame (onscreen, 0);
+
+  onscreen->swap_frame_counter = onscreen->frame_counter;
+}
+
+int64_t
+cogl_onscreen_get_frame_history_start (CoglOnscreen *onscreen)
+{
+  return onscreen->frame_counter - onscreen->n_frame_timings;
+}
+
+CoglFrameTimings *
+cogl_onscreen_get_frame_timings (CoglOnscreen *onscreen,
+                                 int64_t       frame_counter)
+{
+  int pos;
+
+  if (frame_counter > onscreen->frame_counter)
+    return NULL;
+
+  if (frame_counter <= onscreen->frame_counter - onscreen->n_frame_timings)
+    return NULL;
+
+  pos = ((onscreen->current_frame_timings -
+          (onscreen->frame_counter - frame_counter) + COGL_ONSCREEN_MAX_FRAME_TIMINGS)
+         % COGL_ONSCREEN_MAX_FRAME_TIMINGS);
+
+  return onscreen->frame_timings[pos];
+}
+
+unsigned int
+cogl_onscreen_add_frame_timings_callback (CoglOnscreen *onscreen,
+                                          CoglFrameTimingsCallback callback,
+                                          void *user_data)
+{
+  CoglFrameTimingsCallbackEntry *entry = g_slice_new (CoglFrameTimingsCallbackEntry);
+  static int next_resize_callback_id = 0;
+
+  entry->callback = callback;
+  entry->user_data = user_data;
+  entry->id = next_resize_callback_id++;
+
+  COGL_TAILQ_INSERT_TAIL (&onscreen->frame_timings_callbacks, entry, list_node);
+
+  return entry->id;
+}
+
+void
+cogl_onscreen_remove_frame_timings_callback (CoglOnscreen *onscreen,
+                                             unsigned int id)
+{
+  CoglFrameTimingsCallbackEntry *entry;
+
+  COGL_TAILQ_FOREACH (entry, &onscreen->frame_timings_callbacks, list_node)
+    {
+      if (entry->id == id)
+        {
+          COGL_TAILQ_REMOVE (&onscreen->frame_timings_callbacks, entry, list_node);
+          g_slice_free (CoglFrameTimingsCallbackEntry, entry);
+          break;
+        }
+    }
+}
+
+void
+_cogl_onscreen_notify_frame_timings (CoglOnscreen *onscreen)
+{
+  CoglFrameTimingsCallbackEntry *entry, *tmp;
+
+  COGL_TAILQ_FOREACH_SAFE (entry,
+                           &onscreen->frame_timings_callbacks,
+                           list_node,
+                           tmp)
+    entry->callback (onscreen, entry->user_data);
+}
+
diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h
index c4c4dfd..3338991 100644
--- a/cogl/cogl-onscreen.h
+++ b/cogl/cogl-onscreen.h
@@ -34,6 +34,7 @@
 
 #include <cogl/cogl-context.h>
 #include <cogl/cogl-framebuffer.h>
+#include <cogl/cogl-frame-timings.h>
 #include <glib.h>
 
 G_BEGIN_DECLS
@@ -532,6 +533,123 @@ cogl_onscreen_remove_resize_handler (CoglOnscreen *onscreen,
 CoglBool
 cogl_is_onscreen (void *object);
 
+/**
+ * cogl_onscreen_get_frame_counter:
+ *
+ * Gets the value of the framebuffers frame counter. This is
+ * a counter that increases by one each time
+ * cogl_onscreen_swap_buffers() or cogl_onscreen_swap_region()
+ * is called.
+ *
+ * Return value: the current frame counter value
+ * Since: 2.0
+ */
+int64_t
+cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen);
+
+/**
+ * cogl_onscreen_begin_frame:
+ * @onscreen: a #CoglOnscreen framebuffer
+ * @frame_time: the time that should be used for creating
+ *   content for this frame.
+ *
+ * Marks the beginning of a frame. This increases the frame
+ * counter value and creates a new #CoglFrameTimings objeect.
+ *
+ * Since: 2.0
+ */
+void
+cogl_onscreen_begin_frame (CoglOnscreen *onscreen,
+                           gint64        frame_time);
+
+/**
+ * cogl_onscreen_get_frame_history_start:
+ * @onscreen: a #CoglOnscreen framebuffer
+ *
+ * Gets the frame counter for the oldest #CoglFrameTiming that is
+ * being kept in the history. cogl_onscreen_get_frame_timings() will
+ * always return %NULl for any frame counter before this.
+ *
+ * Return value: the frame counter for the oldest #CoglFrameTimings
+ *  in the history.
+ * Since: 2.0
+ */
+int64_t
+cogl_onscreen_get_frame_history_start (CoglOnscreen *onscreen);
+
+
+/**
+ * cogl_onscreen_get_frame_timings:
+ * @onscreen: A #CoglOnscreen framebuffer
+ * @frame_counter: the value of cogl_onscreen_get_frame_counter()
+ *       when the frame finished drawing.
+ *
+ * Gets frame timing information for a particular frame.
+ *
+ * Return value: a #CoglFrameTiming object, or %NULL if frame timing
+ *   information is not available for the given frame.
+ * Since: 2.0
+ */
+CoglFrameTimings *
+cogl_onscreen_get_frame_timings (CoglOnscreen *onscreen,
+                                 int64_t       frame_counter);
+
+/**
+ * CoglFrameTimingsCallback:
+ * @onscreen: A #CoglOnscreen framebuffer that has updated timing information
+ * @user_data: The private passed to
+ *             cogl_onscreen_add_frame_timings_callback()
+ *
+ * Is a callback type used with the
+ * cogl_onscreen_add_frame_timings_callback() allowing applications to be
+ * notified whenever new frame timings information is available
+ * via cogl_onscreen_get_frame_timings().
+ *
+ * <note>A frame timings callback will only ever be called while dispatching
+ * Cogl events from the system mainloop; so for example during
+ * cogl_poll_dispatch(). This is so that callbacks shouldn't occur
+ * while an application might have arbitrary locks held for
+ * example.</note>
+ *
+ * Since: 2.0
+ */
+typedef void (*CoglFrameTimingsCallback) (CoglOnscreen *onscreen,
+                                          void         *user_data);
+
+/**
+ * cogl_onscreen_add_frame_timings_callback:
+ * @onscreen: A #CoglOnscreen framebuffer
+ * @callback: A callback function to call when new frame timings information is available
+ * @user_data: A private pointer to be passed to @callback
+ *
+ * Installs a @callback function that should be called whenever new data
+ * is available via cogl_onscreen_get_frame_timings().
+ *
+ * Return value: a unique identifier that can be used to remove to remove
+ *               the callback later.
+ * Since: 2.0
+ * Stability: unstable
+ */
+unsigned int
+cogl_onscreen_add_frame_timings_callback (CoglOnscreen            *onscreen,
+                                          CoglFrameTimingsCallback callback,
+                                          void                    *user_data);
+
+/**
+ * cogl_onscreen_remove_frame_timings_callback:
+ * @onscreen: A #CoglOnscreen framebuffer
+ * @id: An identifier returned from cogl_onscreen_add_frame_timings_callback()
+ *
+ * Removes a callback that was previously registered
+ * using cogl_onscreen_add_frame_timings_callback().
+ *
+ * Since: 1.10
+ * Stability: unstable
+ */
+void
+cogl_onscreen_remove_frame_timings_callback (CoglOnscreen *onscreen,
+                                             unsigned int  id);
+
 G_END_DECLS
 
 #endif /* __COGL_ONSCREEN_H */
diff --git a/cogl/cogl-x11-renderer-private.h b/cogl/cogl-x11-renderer-private.h
index 54a5639..0b70012 100644
--- a/cogl/cogl-x11-renderer-private.h
+++ b/cogl/cogl-x11-renderer-private.h
@@ -27,6 +27,7 @@
 typedef struct _CoglX11Renderer
 {
   int damage_base;
+  int randr_base;
 } CoglX11Renderer;
 
 #endif /* __COGL_RENDERER_X11_PRIVATE_H */
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 3a3988f..636a744 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -42,6 +42,7 @@
 #include "cogl-texture-2d-private.h"
 #include "cogl-texture-rectangle-private.h"
 #include "cogl-pipeline-opengl-private.h"
+#include "cogl-frame-timings-private.h"
 #include "cogl-framebuffer-private.h"
 #include "cogl-onscreen-private.h"
 #include "cogl-swap-chain-private.h"
@@ -52,6 +53,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/time.h>
 #include <fcntl.h>
 
 #include <glib/gi18n-lib.h>
@@ -83,6 +85,7 @@ typedef struct _CoglOnscreenGLX
   uint32_t last_swap_vsync_counter;
   CoglBool pending_swap_notify;
   CoglBool pending_resize_notify;
+  CoglBool pending_frame_timings_notify;
 } CoglOnscreenGLX;
 
 typedef struct _CoglTexturePixmapGLX
@@ -167,16 +170,134 @@ find_onscreen_for_xid (CoglContext *context, uint32_t xid)
 }
 
 static void
-notify_swap_buffers (CoglContext *context, GLXDrawable drawable)
+ensure_ust_type (CoglRenderer *renderer,
+                 GLXDrawable   drawable)
 {
-  CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)drawable);
+  CoglGLXRenderer *glx_renderer =  renderer->winsys;
+  CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
+  int64_t ust;
+  int64_t msc;
+  int64_t sbc;
+  struct timeval tv;
+  gint64 current_system_time;
+  gint64 current_monotonic_time;
+
+  if (glx_renderer->ust_type != COGL_GLX_UST_IS_UNKNOWN)
+    return;
+
+  glx_renderer->ust_type = COGL_GLX_UST_IS_OTHER;
+
+  if (glx_renderer->pf_glXGetSyncValues == NULL)
+    goto out;
+
+  if (!glx_renderer->pf_glXGetSyncValues (xlib_renderer->xdpy, drawable,
+                                          &ust, &msc, &sbc))
+    goto out;
+
+  /* This is the method that Xorg uses currently */
+  gettimeofday(&tv, NULL);
+  current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec;
+
+  if (current_system_time > ust - 1000000 && current_system_time < ust + 1000000)
+    {
+      glx_renderer->ust_type = COGL_GLX_UST_IS_GETTIMEOFDAY;
+      goto out;
+    }
+
+  /* This is the method that would make sense for a GL implementation to use -
+   * clock_gettime (CLOCK_MONOTONIC, &ts) */
+  current_monotonic_time = g_get_monotonic_time ();
+  if (current_monotonic_time > ust - 1000000 && current_monotonic_time < ust + 1000000)
+    {
+      glx_renderer->ust_type = COGL_GLX_UST_IS_MONOTONIC_TIME;
+      goto out;
+    }
+
+ out:
+  COGL_NOTE (WINSYS, "Classified OML system time as: %s",
+             glx_renderer->ust_type == COGL_GLX_UST_IS_GETTIMEOFDAY ? "gettimeofday" :
+             (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME ? "monotonic" :
+              "other"));
+  return;
+}
+
+static gint64
+ust_to_monotonic_time (CoglRenderer *renderer,
+                       GLXDrawable   drawable,
+                       int64_t       ust)
+{
+  CoglGLXRenderer *glx_renderer =  renderer->winsys;
+  CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer);
+
+  ensure_ust_type (renderer, drawable);
+
+  switch (glx_renderer->ust_type)
+    {
+    case COGL_GLX_UST_IS_UNKNOWN:
+      g_assert_not_reached ();
+      break;
+    case COGL_GLX_UST_IS_GETTIMEOFDAY:
+      {
+        struct timeval tv;
+        gint64 current_system_time;
+        gint64 current_monotonic_time;
+
+        gettimeofday(&tv, NULL);
+        current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec;
+        current_monotonic_time = g_get_monotonic_time ();
+
+        return ust + current_monotonic_time - current_system_time;
+      }
+    case COGL_GLX_UST_IS_MONOTONIC_TIME:
+      return ust;
+    case COGL_GLX_UST_IS_OTHER:
+      {
+        if (glx_renderer->pf_glXGetSyncValues)
+          {
+            gint64 current_monotonic_time;
+            int64_t ust;
+            int64_t msc;
+            int64_t sbc;
+
+            glx_renderer->pf_glXGetSyncValues (xlib_renderer->xdpy, drawable,
+                                               &ust, &msc, &sbc);
+
+            current_monotonic_time = g_get_monotonic_time ();
+            return ust + current_monotonic_time - ust;
+          }
+        break;
+      }
+    }
+
+  return 0;
+}
+
+static void
+set_timings_complete (CoglOnscreen *onscreen)
+{
+  CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglGLXDisplay *glx_display = context->display->winsys;
+  CoglFrameTimings *timings = cogl_onscreen_get_frame_timings (onscreen,
+                                                               cogl_onscreen_get_frame_counter (onscreen));
+
+  timings->complete = TRUE;
+
+  glx_display->pending_frame_timings_notify = TRUE;
+  glx_onscreen->pending_frame_timings_notify = TRUE;
+}
+
+static void
+notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event)
+{
+  CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable);
   CoglDisplay *display = context->display;
   CoglGLXDisplay *glx_display = display->winsys;
   CoglOnscreenGLX *glx_onscreen;
+  CoglFrameTimings *timings;
 
   if (!onscreen)
     return;
-
   glx_onscreen = onscreen->winsys;
 
   /* We only want to notify that the swap is complete when the
@@ -184,6 +305,15 @@ notify_swap_buffers (CoglContext *context, GLXDrawable drawable)
      notifying we'll set a flag to remember to notify later */
   glx_display->pending_swap_notify = TRUE;
   glx_onscreen->pending_swap_notify = TRUE;
+
+  timings = cogl_onscreen_get_frame_timings (onscreen,
+                                             cogl_onscreen_get_frame_counter (onscreen));
+  if (swap_event->ust != 0)
+    timings->presentation_time = ust_to_monotonic_time (context->display->renderer,
+                                                        glx_onscreen->glxwin,
+                                                        swap_event->ust);
+
+  set_timings_complete (onscreen);
 }
 
 static void
@@ -291,7 +421,7 @@ glx_event_filter_cb (XEvent *xevent, void *data)
     {
       GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent;
 
-      notify_swap_buffers (context, swap_event->drawable);
+      notify_swap_buffers (context, swap_event);
 
       /* remove SwapComplete events from the queue */
       return COGL_FILTER_REMOVE;
@@ -1221,17 +1351,32 @@ _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen)
 }
 
 static void
-_cogl_winsys_wait_for_vblank (void)
+_cogl_winsys_wait_for_gpu (CoglOnscreen *onscreen)
 {
-  CoglGLXRenderer *glx_renderer;
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *ctx = framebuffer->context;
 
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+  ctx->glFinish ();
+}
+
+static void
+_cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglContext *ctx = framebuffer->context;
+  CoglGLXRenderer *glx_renderer;
+  CoglXlibRenderer *xlib_renderer;
 
   glx_renderer = ctx->display->renderer->winsys;
+  xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer);
 
   if (glx_renderer->pf_glXWaitForMsc ||
       glx_renderer->pf_glXGetVideoSync)
     {
+      CoglFrameTimings *timings;
+
+      timings = cogl_onscreen_get_frame_timings (onscreen,
+                                                 cogl_onscreen_get_frame_counter (onscreen));
 
       if (glx_renderer->pf_glXWaitForMsc)
         {
@@ -1246,6 +1391,9 @@ _cogl_winsys_wait_for_vblank (void)
           glx_renderer->pf_glXWaitForMsc (xlib_renderer->xdpy, drawable,
                                           0, 2, (msc + 1) % 2,
                                           &ust, &msc, &sbc);
+          timings->presentation_time = ust_to_monotonic_time (ctx->display->renderer,
+                                                              drawable,
+                                                              ust);
         }
       else
         {
@@ -1255,6 +1403,7 @@ _cogl_winsys_wait_for_vblank (void)
           glx_renderer->pf_glXWaitVideoSync (2,
                                              (current_count + 1) % 2,
                                              &current_count);
+          timings->presentation_time = g_get_monotonic_time ();
         }
     }
 }
@@ -1275,6 +1424,19 @@ _cogl_winsys_get_vsync_counter (void)
 }
 
 static void
+set_refresh_interval_from_output (CoglOnscreen *onscreen,
+                                  CoglOutput   *output)
+{
+  float refresh_rate = cogl_output_get_refresh_rate (output);
+  if (refresh_rate != 0.0)
+    {
+      CoglFrameTimings *timings = cogl_onscreen_get_frame_timings (onscreen,
+                                                                   cogl_onscreen_get_frame_counter (onscreen));
+      timings->refresh_interval = (int)(0.5 + (1000000. / refresh_rate));
+    }
+}
+
+static void
 _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
                                    const int *user_rectangles,
                                    int n_rectangles)
@@ -1291,6 +1453,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
   uint32_t end_frame_vsync_counter = 0;
   CoglBool have_counter;
   CoglBool can_wait;
+  int x_min = 0, x_max = 0, y_min = 0, y_max = 0;
 
   /*
    * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple
@@ -1301,6 +1464,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
   CoglBool blit_sub_buffer_is_synchronized =
      _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED);
 
+  int framebuffer_width =  cogl_framebuffer_get_width (framebuffer);
   int framebuffer_height =  cogl_framebuffer_get_height (framebuffer);
   int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4);
   int i;
@@ -1312,7 +1476,24 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
   for (i = 0; i < n_rectangles; i++)
     {
       int *rect = &rectangles[4 * i];
+
+      if (i == 0)
+        {
+          x_min = rect[0];
+          x_max = rect[0] + rect[2];
+          y_min = rect[1];
+          y_max = rect[1] + rect[3];
+        }
+      else
+        {
+          x_min = MIN (x_min, rect[0]);
+          x_max = MAX (x_max, rect[0] + rect[2]);
+          y_min = MIN (y_min, rect[1]);
+          y_max = MAX (y_max, rect[1] + rect[3]);
+        }
+
       rect[1] = framebuffer_height - rect[1] - rect[3];
+
     }
 
   _cogl_framebuffer_flush_state (framebuffer,
@@ -1366,7 +1547,7 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
    *   additional extension so we can report the limited region of
    *   the window damage to X/compositors.
    */
-  context->glFinish ();
+  _cogl_winsys_wait_for_gpu (onscreen);
 
   if (blit_sub_buffer_is_synchronized && have_counter && can_wait)
     {
@@ -1377,10 +1558,10 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
        * any waits if we can see that the video sync count has
        * already progressed. */
       if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter)
-        _cogl_winsys_wait_for_vblank ();
+        _cogl_winsys_wait_for_vblank (onscreen);
     }
   else if (can_wait)
-    _cogl_winsys_wait_for_vblank ();
+    _cogl_winsys_wait_for_vblank (onscreen);
 
   if (glx_renderer->pf_glXCopySubBuffer)
     {
@@ -1429,6 +1610,24 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
    */
   if (have_counter)
     glx_onscreen->last_swap_vsync_counter = end_frame_vsync_counter;
+
+  if (!xlib_onscreen->is_foreign_xwin)
+    {
+      CoglOutput *output;
+
+      x_min = CLAMP (x_min, 0, framebuffer_width);
+      x_max = CLAMP (x_max, 0, framebuffer_width);
+      y_min = CLAMP (y_min, 0, framebuffer_width);
+      y_max = CLAMP (y_max, 0, framebuffer_height);
+
+      output = _cogl_xlib_renderer_output_for_rectangle (context->display->renderer,
+                                                         xlib_onscreen->x + x_min, xlib_onscreen->y + y_min,
+                                                         x_max - x_min, y_max - y_min);
+      if (output)
+        set_refresh_interval_from_output (onscreen, output);
+    }
+
+  set_timings_complete (onscreen);
 }
 
 static void
@@ -1488,16 +1687,16 @@ _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
            * obviously does not happen when we use _GLX_SWAP and let
            * the driver do the right thing
            */
-          context->glFinish ();
+          _cogl_winsys_wait_for_gpu (onscreen);
 
           if (have_counter && can_wait)
             {
               if (glx_onscreen->last_swap_vsync_counter ==
                   end_frame_vsync_counter)
-                _cogl_winsys_wait_for_vblank ();
+                _cogl_winsys_wait_for_vblank (onscreen);
             }
           else if (can_wait)
-            _cogl_winsys_wait_for_vblank ();
+            _cogl_winsys_wait_for_vblank (onscreen);
         }
     }
   else
@@ -1507,6 +1706,13 @@ _cogl_winsys_onscreen_swap_buffers (CoglOnscreen *onscreen)
 
   if (have_counter)
     glx_onscreen->last_swap_vsync_counter = _cogl_winsys_get_vsync_counter ();
+
+  if (xlib_onscreen->output)
+    set_refresh_interval_from_output (onscreen, xlib_onscreen->output);
+
+  if (!(glx_renderer->pf_glXSwapInterval &&
+        _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_VBLANK_WAIT)))
+    set_timings_complete (onscreen);
 }
 
 static uint32_t
@@ -2178,7 +2384,9 @@ _cogl_winsys_poll_get_info (CoglContext *context,
 
   /* If we've already got a pending swap notify then we'll dispatch
      immediately */
-  if (glx_display->pending_swap_notify || glx_display->pending_resize_notify)
+  if (glx_display->pending_swap_notify ||
+      glx_display->pending_resize_notify ||
+      glx_display->pending_frame_timings_notify)
     *timeout = 0;
 }
 
@@ -2204,6 +2412,12 @@ flush_pending_notifications_cb (void *data,
           _cogl_onscreen_notify_resize (onscreen);
           glx_onscreen->pending_resize_notify = FALSE;
         }
+
+      if (glx_onscreen->pending_frame_timings_notify)
+        {
+          _cogl_onscreen_notify_frame_timings (onscreen);
+          glx_onscreen->pending_frame_timings_notify = FALSE;
+        }
     }
 }
 
@@ -2219,13 +2433,16 @@ _cogl_winsys_poll_dispatch (CoglContext *context,
                                      poll_fds,
                                      n_poll_fds);
 
-  if (glx_display->pending_swap_notify || glx_display->pending_resize_notify)
+  if (glx_display->pending_swap_notify ||
+      glx_display->pending_resize_notify ||
+      glx_display->pending_frame_timings_notify)
     {
       g_list_foreach (context->framebuffers,
                       flush_pending_notifications_cb,
                       NULL);
       glx_display->pending_swap_notify = FALSE;
       glx_display->pending_resize_notify = FALSE;
+      glx_display->pending_frame_timings_notify = FALSE;
     }
 }
 
-- 
1.7.7.6



More information about the Cogl mailing list