[Cogl] [PATCH] ensure _SYNC and _COMPLETE events are always dispatched

Robert Bragg robert at sixbynine.org
Tue Jan 29 08:51:29 PST 2013


From: Robert Bragg <robert at linux.intel.com>

This ensures that _SYNC and _COMPLETE events will be dispatched by Cogl
on all systems.

It's important that we be able to consistently dispatch _COMPLETE events
so that we avoid building up an never-ending list of CoglFrameInfo
objects internally.

We want to guarantee the _SYNC events because that greatly simplifies
things for applications if they can just assume this event mechanism
handles throttling on all systems. Previously applications would check
for the _SWAP_BUFFERS_EVENT feature to see if they could use
cogl_onscreen_add_swap_buffers_callback() as a means to throttle their
paint cycle but they would also have to have a fallback that simply used
and idle callback for painting. This should simplify the boilerplate
required for using Cogl.

This patch removes COGL_FEATURE_ID_FRAME_SYNC feature since we now
guarantee that this event is always dispatched.

TODO: squash this back into _add_frame_callback patch
---
 cogl/cogl-context-private.h                     |  3 +
 cogl/cogl-context.h                             |  3 -
 cogl/cogl-onscreen-private.h                    | 16 ++++++
 cogl/cogl-onscreen.c                            | 76 +++++++++++++++++++++++++
 cogl/cogl-onscreen.h                            | 38 ++++++-------
 cogl/cogl-poll.c                                | 10 ++++
 cogl/cogl-sdl.c                                 |  2 +-
 cogl/cogl-types.h                               |  3 +
 cogl/winsys/cogl-winsys-egl-kms.c               |  6 +-
 cogl/winsys/cogl-winsys-glx-feature-functions.h |  3 +-
 cogl/winsys/cogl-winsys-glx.c                   | 35 ++++++++----
 11 files changed, 156 insertions(+), 39 deletions(-)

diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index ae8e3dc..fc528e7 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -49,6 +49,7 @@
 #include "cogl-gpu-info-private.h"
 #include "cogl-gl-header.h"
 #include "cogl-framebuffer-private.h"
+#include "cogl-onscreen-private.h"
 
 typedef struct
 {
@@ -175,6 +176,8 @@ struct _CoglContext
   GHashTable *swap_callback_closures;
   int next_swap_callback_id;
 
+  CoglOnscreenEventList onscreen_events_queue;
+
   CoglGLES2Context *current_gles2_context;
   GQueue gles2_context_stack;
 
diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h
index 2b4eaf2..1b6b878 100644
--- a/cogl/cogl-context.h
+++ b/cogl/cogl-context.h
@@ -204,8 +204,6 @@ cogl_is_context (void *object);
  *    suported.
  * @COGL_FEATURE_ID_DEPTH_TEXTURE: Whether #CoglFramebuffer support rendering
  *     the depth buffer to a texture.
- * @COGL_FEATURE_ID_FRAME_SYNC: Whether %COGL_FRAME_EVENT_SYNC events
- *    will be sent to registered #CoglFrameCallback functions.
  * @COGL_FEATURE_ID_PRESENTATION_TIME: Whether frame presentation
  *    time stamps will be recorded in #CoglFrameInfo objects.
  *
@@ -237,7 +235,6 @@ typedef enum _CoglFeatureID
   COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
   COGL_FEATURE_ID_GLES2_CONTEXT,
   COGL_FEATURE_ID_DEPTH_TEXTURE,
-  COGL_FEATURE_ID_FRAME_SYNC,
   COGL_FEATURE_ID_PRESENTATION_TIME,
 
   /*< private >*/
diff --git a/cogl/cogl-onscreen-private.h b/cogl/cogl-onscreen-private.h
index 7751d80..89efb0e 100644
--- a/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl-onscreen-private.h
@@ -59,6 +59,19 @@ struct _CoglResizeNotifyEntry
   unsigned int id;
 };
 
+typedef struct _CoglOnscreenEvent CoglOnscreenEvent;
+
+COGL_TAILQ_HEAD (CoglOnscreenEventList, CoglOnscreenEvent);
+
+struct _CoglOnscreenEvent
+{
+  COGL_TAILQ_ENTRY (CoglOnscreenEvent) list_node;
+
+  CoglOnscreen *onscreen;
+  CoglFrameInfo *info;
+  CoglFrameEvent type;
+};
+
 struct _CoglOnscreen
 {
   CoglFramebuffer  _parent;
@@ -102,4 +115,7 @@ _cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info);
 void
 _cogl_onscreen_notify_resize (CoglOnscreen *onscreen);
 
+void
+_cogl_dispatch_onscreen_events (CoglContext *context);
+
 #endif /* __COGL_ONSCREEN_PRIVATE_H */
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index ba5efbb..3887a1a 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -119,6 +119,22 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
   g_free (onscreen);
 }
 
+static void
+_cogl_onscreen_queue_event (CoglOnscreen *onscreen,
+                            CoglFrameEvent type,
+                            CoglFrameInfo *info)
+{
+  CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context;
+
+  CoglOnscreenEvent *event = g_slice_new (CoglOnscreenEvent);
+
+  event->onscreen = cogl_object_ref (onscreen);
+  event->info = cogl_object_ref (info);
+  event->type = type;
+
+  COGL_TAILQ_INSERT_TAIL (&ctx->onscreen_events_queue, event, list_node);
+}
+
 void
 cogl_onscreen_swap_buffers (CoglOnscreen *onscreen)
 {
@@ -140,6 +156,21 @@ cogl_onscreen_swap_buffers (CoglOnscreen *onscreen)
                                     COGL_BUFFER_BIT_COLOR |
                                     COGL_BUFFER_BIT_DEPTH |
                                     COGL_BUFFER_BIT_STENCIL);
+
+  if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
+    {
+      CoglFrameInfo *info;
+
+      g_warn_if_fail (onscreen->pending_frame_infos.length == 1);
+
+      info = g_queue_pop_tail (&onscreen->pending_frame_infos);
+
+      _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
+      _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
+
+      cogl_object_unref (info);
+    }
+
   onscreen->frame_counter++;
 }
 
@@ -174,6 +205,21 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
                                     COGL_BUFFER_BIT_COLOR |
                                     COGL_BUFFER_BIT_DEPTH |
                                     COGL_BUFFER_BIT_STENCIL);
+
+  if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
+    {
+      CoglFrameInfo *info;
+
+      g_warn_if_fail (onscreen->pending_frame_infos.length == 1);
+
+      info = g_queue_pop_tail (&onscreen->pending_frame_infos);
+
+      _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
+      _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
+
+      cogl_object_unref (info);
+    }
+
   onscreen->frame_counter++;
 }
 
@@ -422,6 +468,36 @@ cogl_onscreen_hide (CoglOnscreen *onscreen)
         winsys->onscreen_set_visibility (onscreen, FALSE);
     }
 }
+void
+_cogl_dispatch_onscreen_events (CoglContext *context)
+{
+  CoglOnscreenEvent *event, *tmp;
+
+  COGL_TAILQ_FOREACH_SAFE (event,
+                           &context->onscreen_events_queue,
+                           list_node,
+                           tmp)
+    {
+      CoglOnscreen *onscreen = event->onscreen;
+      CoglFrameInfo *info = event->info;
+
+      switch (event->type)
+        {
+        case COGL_FRAME_EVENT_SYNC:
+          _cogl_onscreen_notify_frame_sync (onscreen, info);
+          break;
+        case COGL_FRAME_EVENT_COMPLETE:
+          _cogl_onscreen_notify_complete (onscreen, info);
+          break;
+        }
+
+      cogl_object_unref (onscreen);
+      cogl_object_unref (info);
+
+      COGL_TAILQ_REMOVE (&context->onscreen_events_queue, event, list_node);
+      g_slice_free (CoglOnscreenEvent, event);
+    }
+}
 
 void
 _cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info)
diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h
index d079cd7..0a59eb8 100644
--- a/cogl/cogl-onscreen.h
+++ b/cogl/cogl-onscreen.h
@@ -409,9 +409,7 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
  * CoglFrameEvent:
  * @COGL_FRAME_EVENT_SYNC: Notifies that the system compositor has
  *                         acknowledged a frame and is ready for a
- *                         new frame to be created. Only delivered
- *                         if the %COGL_FEATURE_ID_FRAME_SYNC
- *                         feature is supported.
+ *                         new frame to be created.
  * @COGL_FRAME_EVENT_COMPLETE: Notifies that a frame has ended. This
  *                             is a good time for applications to
  *                             collect statistics about the frame
@@ -493,30 +491,26 @@ typedef struct _CoglFrameClosure CoglFrameClosure;
  * Installs a @callback function that will be called for significant
  * events relating to the given @onscreen framebuffer.
  *
- * If the %COGL_FEATURE_ID_FRAME_SYNC feature is supported
- * then @callback will be used to notify when the system compositor is
+ * The @callback will be used to notify when the system compositor is
  * ready for this application to render a new frame. In this case
  * %COGL_FRAME_EVENT_SYNC will be passed as the event argument to the
  * given @callback in addition to the #CoglFrameInfo corresponding to
  * the frame beeing acknowledged by the compositor.
  *
- * If the %COGL_FEATURE_ID_PRESENTATION_EVENTS is available then
- * @callback will be called to notify when the frame has become
- * visible to the user. In this case %COGL_FRAME_EVENT_PRESENTED will
- * be passed as the event argument to the given @callback in addition
- * to the #CoglFrameInfo corresponding to the newly presented frame.
- *
- * We recommend throttling your application according to
- * %COGL_FRAME_EVENT_SYNC events whenever the
- * %COGL_FEATURE_ID_FRAME_SYNC feature is available. This allows your
- * application to avoid being blocked by the driver which will block
- * your applications mainloop.
- *
- * <note>Applications should not make assumptions about the relative
- * ordering of different types of frame events. A
- * %COGL_FRAME_EVENT_PRESENTED event can be received before a
- * %COGL_FRAME_EVENT_SYNC if the compositor is agressively throttling
- * your application.</note>
+ * The @callback will also be called to notify when the frame has
+ * ended. In this case %COGL_FRAME_EVENT_COMPLETE will be passed as
+ * the event argument to the given @callback in addition to the
+ * #CoglFrameInfo corresponding to the newly presented frame.  The
+ * meaning of "ended" here simply means that no more timing
+ * information will be collected within the corresponding
+ * #CoglFrameInfo and so this is a good opportunity to analyse the
+ * given info. It does not necessarily mean that the GPU has finished
+ * rendering the corresponding frame.
+ *
+ * We highly recommend throttling your application according to
+ * %COGL_FRAME_EVENT_SYNC events so that your application can avoid
+ * wasting resources, drawing more frames than your system compositor
+ * can display.
  *
  * Return value: a #CoglFrameClosure pointer that can be used to
  *               remove the callback and associated @user_data later.
diff --git a/cogl/cogl-poll.c b/cogl/cogl-poll.c
index 86be2cf..c6d19fb 100644
--- a/cogl/cogl-poll.c
+++ b/cogl/cogl-poll.c
@@ -44,6 +44,13 @@ cogl_poll_get_info (CoglContext *context,
   _COGL_RETURN_IF_FAIL (n_poll_fds != NULL);
   _COGL_RETURN_IF_FAIL (timeout != NULL);
 
+  if (!COGL_TAILQ_EMPTY (&context->onscreen_events_queue))
+    {
+      *n_poll_fds = 0;
+      *timeout = 0;
+      return;
+    }
+
   winsys = _cogl_context_get_winsys (context);
 
   if (winsys->poll_get_info)
@@ -70,6 +77,9 @@ cogl_poll_dispatch (CoglContext *context,
 
   _COGL_RETURN_IF_FAIL (cogl_is_context (context));
 
+  if (!COGL_TAILQ_EMPTY (&context->onscreen_events_queue))
+    _cogl_dispatch_onscreen_events (context);
+
   winsys = _cogl_context_get_winsys (context);
 
   if (winsys->poll_dispatch)
diff --git a/cogl/cogl-sdl.c b/cogl/cogl-sdl.c
index 03afeb7..b49890f 100644
--- a/cogl/cogl-sdl.c
+++ b/cogl/cogl-sdl.c
@@ -83,5 +83,5 @@ cogl_sdl_handle_event (CoglContext *context, SDL_Event *event)
 void
 cogl_sdl_idle (CoglContext *context)
 {
-  /* NOP since Cogl doesn't currently need to do anything when idle */
+  _cogl_dispatch_onscreen_events (context);
 }
diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h
index 01ad4c9..e4d8aae 100644
--- a/cogl/cogl-types.h
+++ b/cogl/cogl-types.h
@@ -658,6 +658,9 @@ typedef enum _CoglWinsysFeature
   /* Avaiable if the age of the back buffer can be queried */
   COGL_WINSYS_FEATURE_BUFFER_AGE,
 
+  /* Avaiable if the winsys directly handles _SYNC and _COMPLETE events */
+  COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
+
   COGL_WINSYS_FEATURE_N_FEATURES
 } CoglWinsysFeature;
 
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index a984afb..db2b496 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -753,12 +753,14 @@ _cogl_winsys_egl_context_init (CoglContext *context,
                                CoglError **error)
 {
   COGL_FLAGS_SET (context->features,
-                  COGL_FEATURE_ID_FRAME_SYNC, TRUE);
-  COGL_FLAGS_SET (context->features,
                   COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE);
+  /* TODO: remove this deprecated feature */
   COGL_FLAGS_SET (context->winsys_features,
                   COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT,
                   TRUE);
+  COGL_FLAGS_SET (context->winsys_features,
+                  COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
+                  TRUE);
 
   return TRUE;
 }
diff --git a/cogl/winsys/cogl-winsys-glx-feature-functions.h b/cogl/winsys/cogl-winsys-glx-feature-functions.h
index d0d2de3..156b58f 100644
--- a/cogl/winsys/cogl-winsys-glx-feature-functions.h
+++ b/cogl/winsys/cogl-winsys-glx-feature-functions.h
@@ -169,7 +169,8 @@ COGL_WINSYS_FEATURE_BEGIN (255, 255,
                            swap_event,
                            "INTEL\0",
                            "swap_event\0",
-                           COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT)
+                           COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT |
+                           COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)
 COGL_WINSYS_FEATURE_END ()
 
 COGL_WINSYS_FEATURE_BEGIN (255, 255,
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 2dbe0d7..0893af7 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -301,6 +301,17 @@ _cogl_winsys_get_clock_time (CoglContext *context)
 }
 
 static void
+set_sync_pending (CoglOnscreen *onscreen)
+{
+  CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
+  CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglGLXDisplay *glx_display = context->display->winsys;
+
+  glx_display->pending_sync_notify = TRUE;
+  glx_onscreen->pending_sync_notify = TRUE;
+}
+
+static void
 set_complete_pending (CoglOnscreen *onscreen)
 {
   CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
@@ -315,8 +326,6 @@ 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;
 
   if (!onscreen)
@@ -326,8 +335,7 @@ notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event)
   /* We only want to notify that the swap is complete when the
      application calls cogl_context_dispatch so instead of immediately
      notifying we'll set a flag to remember to notify later */
-  glx_display->pending_sync_notify = TRUE;
-  glx_onscreen->pending_sync_notify = TRUE;
+  set_sync_pending (onscreen);
 
   if (swap_event->ust != 0)
     {
@@ -716,11 +724,9 @@ update_winsys_features (CoglContext *context, CoglError **error)
     COGL_FLAGS_SET (context->winsys_features,
                     COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE);
 
-  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT))
+  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
     {
-      COGL_FLAGS_SET (context->features,
-                      COGL_FEATURE_ID_FRAME_SYNC,
-                      TRUE);
+      /* TODO: remove this deprecated feature */
       COGL_FLAGS_SET (context->features,
                       COGL_FEATURE_ID_SWAP_BUFFERS_EVENT,
                       TRUE);
@@ -1266,7 +1272,7 @@ _cogl_winsys_onscreen_init (CoglOnscreen *onscreen,
     }
 
 #ifdef GLX_INTEL_swap_event
-  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT))
+  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
     {
       GLXDrawable drawable =
         glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
@@ -1749,7 +1755,16 @@ _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
       set_frame_info_output (onscreen, output);
     }
 
-  set_complete_pending (onscreen);
+  /* XXX: we don't get SwapComplete events based on how we implement
+   * the _swap_region() API but if cogl-onscreen.c knows we are
+   * handling _SYNC and _COMPLETE events in the winsys then we need to
+   * send fake events in this case.
+   */
+  if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT))
+    {
+      set_sync_pending (onscreen);
+      set_complete_pending (onscreen);
+    }
 }
 
 static void
-- 
1.8.1.1



More information about the Cogl mailing list