[Cogl] [PATCH 3/3] Add api for queuing idle callback internally

Robert Bragg robert at sixbynine.org
Sat Apr 27 17:12:41 PDT 2013


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

This adds a _cogl_poll_renderer_add_idle api that can be used internally
for queuing an idle callback without needing to make any assumption
about the system mainloop that is being used. This is now used to avoid
having the _cogl_poll_renderer_dispatch() directly check for all kinds of
events to dispatch, and to avoid having the winsys dispatch vfuncs need
to directly know about CoglContext. This means we can now avoid having a
back reference from CoglRenderer to the CoglContext.
---
 cogl/cogl-closure-list-private.h      |  14 ++-
 cogl/cogl-context-private.h           |   1 +
 cogl/cogl-context.c                   |  12 ---
 cogl/cogl-glx-renderer-private.h      |   7 +-
 cogl/cogl-onscreen.c                  |  14 +++
 cogl/cogl-poll-private.h              |  12 +++
 cogl/cogl-poll.c                      |  36 +++----
 cogl/cogl-renderer-private.h          |  16 +--
 cogl/cogl-renderer.c                  |   4 +
 cogl/cogl-sdl.c                       |  20 ++--
 cogl/winsys/cogl-winsys-egl-kms.c     | 112 ++++++++++----------
 cogl/winsys/cogl-winsys-egl-private.h |   2 +-
 cogl/winsys/cogl-winsys-egl-x11.c     |  92 ++++++++--------
 cogl/winsys/cogl-winsys-glx.c         | 191 ++++++++++++++++++----------------
 cogl/winsys/cogl-winsys-sdl.c         |  55 +++++-----
 cogl/winsys/cogl-winsys-sdl2.c        | 101 +++++++++---------
 16 files changed, 350 insertions(+), 339 deletions(-)

diff --git a/cogl/cogl-closure-list-private.h b/cogl/cogl-closure-list-private.h
index d0e10ce..88d4c4a 100644
--- a/cogl/cogl-closure-list-private.h
+++ b/cogl/cogl-closure-list-private.h
@@ -81,7 +81,8 @@ cogl_closure_list_add (CoglClosureList *list,
  * @cb_type: The name of a typedef for the closure callback function signature
  * @...: The the arguments to pass to the callback
  *
- * A convenience macro to invoke a closure list.
+ * A convenience macro to invoke a closure list with a variable number
+ * of arguments that will be passed to the closure callback functions.
  *
  * Note that the arguments will be evaluated multiple times so it is
  * not safe to pass expressions that have side-effects.
@@ -101,4 +102,15 @@ cogl_closure_list_add (CoglClosureList *list,
       }                                                         \
   } G_STMT_END
 
+#define cogl_closure_list_invoke_no_args(list)                  \
+  G_STMT_START {                                                \
+    CoglClosure *_c, *_tmp;                                     \
+                                                                \
+    COGL_LIST_FOREACH_SAFE (_c, (list), list_node, _tmp)        \
+      {                                                         \
+        void (*_cb)(void *) = _c->function;                     \
+        _cb (_c->user_data);                                    \
+      }                                                         \
+  } G_STMT_END
+
 #endif /* _COGL_CLOSURE_LIST_PRIVATE_H_ */
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index b601e33..cf41c88 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -174,6 +174,7 @@ struct _CoglContext
   CoglOffscreenAllocateFlags last_offscreen_allocate_flags;
 
   CoglOnscreenEventList onscreen_events_queue;
+  CoglClosure *onscreen_dispatch_idle;
 
   CoglGLES2Context *current_gles2_context;
   GQueue gles2_context_stack;
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index 392865c..e4714be 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -194,18 +194,6 @@ cogl_context_new (CoglDisplay *display,
 
   context->display = display;
 
-  /* Add a back reference to the context from the renderer because
-   * event dispatching is handled by the renderer and we don't
-   * currently have a generalized way of registering idle functions
-   * and such things internally so cogl_poll_renderer_dispatch()
-   * needs to poke inside the context if one is available to check
-   * if there are pending onscreen framebuffer events.
-   *
-   * FIXME: once we have a generalized way of registering idle
-   * functions then we can remove this back-reference.
-   */
-  display->renderer->context = context;
-
   /* This is duplicated data, but it's much more convenient to have
      the driver attached to the context and the value is accessed a
      lot throughout Cogl */
diff --git a/cogl/cogl-glx-renderer-private.h b/cogl/cogl-glx-renderer-private.h
index cf4e02d..aae2d9d 100644
--- a/cogl/cogl-glx-renderer-private.h
+++ b/cogl/cogl-glx-renderer-private.h
@@ -54,12 +54,7 @@ typedef struct _CoglGLXRenderer
   /* GModule pointing to libGL which we use to get glX functions out of */
   GModule *libgl_module;
 
-  /* Events get dispatched from the CoglRenderer and these are
-   * high-level flags that let us quickly check if there are any
-   * pending events to dispatch. */
-  CoglBool pending_sync_notify;
-  CoglBool pending_complete_notify;
-  CoglBool pending_resize_notify;
+  CoglClosure *flush_notifications_idle;
 
   /* Copy of the winsys features that are based purely on the
    * information we can get without using a GL context. We want to
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index ba7d888..8b20f0d 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -33,6 +33,7 @@
 #include "cogl-context-private.h"
 #include "cogl-object-private.h"
 #include "cogl-closure-list-private.h"
+#include "cogl-poll-private.h"
 
 static void _cogl_onscreen_free (CoglOnscreen *onscreen);
 
@@ -119,6 +120,16 @@ _cogl_onscreen_queue_event (CoglOnscreen *onscreen,
   event->type = type;
 
   COGL_TAILQ_INSERT_TAIL (&ctx->onscreen_events_queue, event, list_node);
+
+  if (!ctx->onscreen_dispatch_idle)
+    {
+      ctx->onscreen_dispatch_idle =
+        _cogl_poll_renderer_add_idle (ctx->display->renderer,
+                                      (CoglIdleCallback)
+                                      _cogl_dispatch_onscreen_events,
+                                      ctx,
+                                      NULL);
+    }
 }
 
 void
@@ -397,6 +408,9 @@ _cogl_dispatch_onscreen_events (CoglContext *context)
   COGL_TAILQ_CONCAT (&queue, &context->onscreen_events_queue, list_node);
   COGL_TAILQ_INIT (&context->onscreen_events_queue);
 
+  cogl_closure_disconnect (context->onscreen_dispatch_idle);
+  context->onscreen_dispatch_idle = NULL;
+
   COGL_TAILQ_FOREACH_SAFE (event,
                            &queue,
                            list_node,
diff --git a/cogl/cogl-poll-private.h b/cogl/cogl-poll-private.h
index 6e030fd..da1051a 100644
--- a/cogl/cogl-poll-private.h
+++ b/cogl/cogl-poll-private.h
@@ -25,6 +25,10 @@
 #ifndef __COGL_POLL_PRIVATE_H__
 #define __COGL_POLL_PRIVATE_H__
 
+#include "cogl-poll.h"
+#include "cogl-renderer.h"
+#include "cogl-closure-list-private.h"
+
 void
 _cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd);
 
@@ -33,4 +37,12 @@ _cogl_poll_renderer_add_fd (CoglRenderer *renderer,
                             int fd,
                             CoglPollFDEvent events);
 
+typedef void (*CoglIdleCallback) (void *user_data);
+
+CoglClosure *
+_cogl_poll_renderer_add_idle (CoglRenderer *renderer,
+                              CoglIdleCallback idle_cb,
+                              void *user_data,
+                              CoglUserDataDestroyCallback destroy_cb);
+
 #endif /* __COGL_POLL_PRIVATE_H__ */
diff --git a/cogl/cogl-poll.c b/cogl/cogl-poll.c
index 8009f5f..3171dc8 100644
--- a/cogl/cogl-poll.c
+++ b/cogl/cogl-poll.c
@@ -49,16 +49,10 @@ cogl_poll_renderer_get_info (CoglRenderer *renderer,
   *poll_fds = (void *)renderer->poll_fds->data;
   *n_poll_fds = renderer->poll_fds->len;
 
-  /* NB: This will be NULL until the renderer has been connected,
-   * associated with a CoglDisplay and then a CoglContext is
-   * created from that display. */
-  if (renderer->context)
+  if (!COGL_LIST_EMPTY (&renderer->idle_closures))
     {
-      if (!COGL_TAILQ_EMPTY (&renderer->context->onscreen_events_queue))
-        {
-          *timeout = 0;
-          return renderer->poll_fds_age;
-        }
+      *timeout = 0;
+      return renderer->poll_fds_age;
     }
 
   winsys = renderer->winsys_vtable;
@@ -80,17 +74,7 @@ cogl_poll_renderer_dispatch (CoglRenderer *renderer,
 
   _COGL_RETURN_IF_FAIL (cogl_is_renderer (renderer));
 
-  /* FIXME: arbitrary cogl components should just be able to queue
-   * idle functions so that we don't have to explicitly poke into
-   * CoglContext here and understand about the CoglOnscreen event
-   * queue... */
-  if (renderer->context)
-    {
-      CoglContext *context = renderer->context;
-
-      if (!COGL_TAILQ_EMPTY (&context->onscreen_events_queue))
-        _cogl_dispatch_onscreen_events (context);
-    }
+  cogl_closure_list_invoke_no_args (&renderer->idle_closures);
 
   winsys = renderer->winsys_vtable;
 
@@ -141,3 +125,15 @@ _cogl_poll_renderer_add_fd (CoglRenderer *renderer,
   g_array_append_val (renderer->poll_fds, pollfd);
   renderer->poll_fds_age++;
 }
+
+CoglClosure *
+_cogl_poll_renderer_add_idle (CoglRenderer *renderer,
+                              CoglIdleCallback idle_cb,
+                              void *user_data,
+                              CoglUserDataDestroyCallback destroy_cb)
+{
+  return cogl_closure_list_add (&renderer->idle_closures,
+                                idle_cb,
+                                user_data,
+                                destroy_cb);
+}
diff --git a/cogl/cogl-renderer-private.h b/cogl/cogl-renderer-private.h
index b01a1c9..476c955 100644
--- a/cogl/cogl-renderer-private.h
+++ b/cogl/cogl-renderer-private.h
@@ -31,6 +31,7 @@
 #include "cogl-driver.h"
 #include "cogl-texture-driver.h"
 #include "cogl-context.h"
+#include "cogl-closure-list-private.h"
 
 #ifdef COGL_HAS_XLIB_SUPPORT
 #include <X11/Xlib.h>
@@ -54,20 +55,7 @@ struct _CoglRenderer
   GArray *poll_fds;
   int poll_fds_age;
 
-  /* NB: Currently a CoglContext can only be associated with 1
-   * CoglDisplay which itself can only be associated with 1
-   * CoglRenderer.
-   *
-   * We currently do event dispatching from the renderer but once we
-   * have fully setup a context then we need to refer to the context
-   * to dispatch context events.
-   *
-   * This gives us a back-reference to the CoglContext that can be
-   * referenced during event dispatching.
-   *
-   * We always need to consider that this may be NULL.
-   */
-  CoglContext *context;
+  CoglClosureList idle_closures;
 
   GList *outputs;
 
diff --git a/cogl/cogl-renderer.c b/cogl/cogl-renderer.c
index 10bd5ea..33a2ad1 100644
--- a/cogl/cogl-renderer.c
+++ b/cogl/cogl-renderer.c
@@ -155,6 +155,8 @@ _cogl_renderer_free (CoglRenderer *renderer)
 {
   const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer);
 
+  cogl_closure_list_disconnect_all (&renderer->idle_closures);
+
   if (winsys)
     winsys->renderer_disconnect (renderer);
 
@@ -185,6 +187,8 @@ cogl_renderer_new (void)
 
   renderer->poll_fds = g_array_new (FALSE, TRUE, sizeof (CoglPollFD));
 
+  COGL_LIST_INIT (&renderer->idle_closures);
+
 #ifdef COGL_HAS_XLIB_SUPPORT
   renderer->xlib_enable_event_retrieval = TRUE;
 #endif
diff --git a/cogl/cogl-sdl.c b/cogl/cogl-sdl.c
index 19f3942..602cac0 100644
--- a/cogl/cogl-sdl.c
+++ b/cogl/cogl-sdl.c
@@ -68,19 +68,13 @@ cogl_sdl_context_new (int type, CoglError **error)
 void
 cogl_sdl_handle_event (CoglContext *context, SDL_Event *event)
 {
-  const CoglWinsysVtable *winsys;
   CoglRenderer *renderer;
 
   _COGL_RETURN_IF_FAIL (cogl_is_context (context));
 
   renderer = context->display->renderer;
 
-  winsys = renderer->winsys_vtable;
-
   _cogl_renderer_handle_native_event (renderer, event);
-
-  if (winsys->poll_dispatch)
-    winsys->poll_dispatch (renderer, NULL, 0);
 }
 
 static void
@@ -96,14 +90,16 @@ _cogl_sdl_push_wakeup_event (CoglContext *context)
 void
 cogl_sdl_idle (CoglContext *context)
 {
-  _cogl_dispatch_onscreen_events (context);
+  CoglRenderer *renderer = context->display->renderer;
+
+  cogl_poll_renderer_dispatch (renderer, NULL, 0);
 
   /* It is expected that this will be called from the application
    * immediately before blocking in SDL_WaitEvent. However,
-   * dispatching the onscreen events may cause more events to be
-   * queued. If that happens we need to make sure the blocking returns
-   * immediately. We'll post our dummy event to make sure that
-   * happens */
-  if (!COGL_TAILQ_EMPTY (&context->onscreen_events_queue))
+   * dispatching cause more work to be queued. If that happens we need
+   * to make sure the blocking returns immediately. We'll post our
+   * dummy event to make sure that happens
+   */
+  if (!COGL_LIST_EMPTY (&renderer->idle_closures))
     _cogl_sdl_push_wakeup_event (context);
 }
diff --git a/cogl/winsys/cogl-winsys-egl-kms.c b/cogl/winsys/cogl-winsys-egl-kms.c
index 288d652..536f9fa 100644
--- a/cogl/winsys/cogl-winsys-egl-kms.c
+++ b/cogl/winsys/cogl-winsys-egl-kms.c
@@ -63,8 +63,7 @@ typedef struct _CoglRendererKMS
 {
   int fd;
   struct gbm_device *gbm;
-
-  CoglBool pending_swap_notify;
+  CoglClosure *swap_notify_idle;
 } CoglRendererKMS;
 
 typedef struct _CoglOutputKMS
@@ -604,6 +603,48 @@ free_current_bo (CoglOnscreen *onscreen)
 }
 
 static void
+flush_pending_swap_notify_cb (void *data,
+                              void *user_data)
+{
+  CoglFramebuffer *framebuffer = data;
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+    {
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
+
+      if (kms_onscreen->pending_swap_notify)
+        {
+          CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
+
+          _cogl_onscreen_notify_frame_sync (onscreen, info);
+          _cogl_onscreen_notify_complete (onscreen, info);
+          kms_onscreen->pending_swap_notify = FALSE;
+
+          cogl_object_unref (info);
+        }
+    }
+}
+
+static void
+flush_pending_swap_notify_idle (void *user_data)
+{
+  CoglContext *context = user_data;
+  CoglRendererEGL *egl_renderer = context->display->renderer->winsys;
+  CoglRendererKMS *kms_renderer = egl_renderer->platform;
+
+  /* This needs to be disconnected before invoking the callbacks in
+   * case the callbacks cause it to be queued again */
+  cogl_closure_disconnect (kms_renderer->swap_notify_idle);
+  kms_renderer->swap_notify_idle = NULL;
+
+  g_list_foreach (context->framebuffers,
+                  flush_pending_swap_notify_cb,
+                  NULL);
+}
+
+static void
 page_flip_handler (int fd,
                    unsigned int frame,
                    unsigned int sec,
@@ -627,9 +668,16 @@ page_flip_handler (int fd,
 
       /* 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 */
-      kms_renderer->pending_swap_notify = TRUE;
+       * immediately notifying we queue an idle callback */
+      if (!kms_renderer->swap_notify_idle)
+        {
+          kms_renderer->swap_notify_idle =
+            _cogl_poll_renderer_add_idle (renderer,
+                                          flush_pending_swap_notify_idle,
+                                          context,
+                                          NULL);
+        }
+
       kms_onscreen->pending_swap_notify = TRUE;
 
       free_current_bo (onscreen);
@@ -861,42 +909,6 @@ _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen)
   onscreen->winsys = NULL;
 }
 
-static int64_t
-_cogl_winsys_get_dispatch_timeout (CoglRenderer *renderer)
-{
-  CoglRendererEGL *egl_renderer = renderer->winsys;
-  CoglRendererKMS *kms_renderer = egl_renderer->platform;
-
-  /* If we've already got a pending swap notify then we'll dispatch
-   * immediately */
-  return kms_renderer->pending_swap_notify ? 0 : -1;
-}
-
-static void
-flush_pending_swap_notify_cb (void *data,
-                              void *user_data)
-{
-  CoglFramebuffer *framebuffer = data;
-
-  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
-    {
-      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
-      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
-      CoglOnscreenKMS *kms_onscreen = egl_onscreen->platform;
-
-      if (kms_onscreen->pending_swap_notify)
-        {
-          CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
-
-          _cogl_onscreen_notify_frame_sync (onscreen, info);
-          _cogl_onscreen_notify_complete (onscreen, info);
-          kms_onscreen->pending_swap_notify = FALSE;
-
-          cogl_object_unref (info);
-        }
-    }
-}
-
 static void
 _cogl_winsys_poll_dispatch (CoglRenderer *renderer,
                             const CoglPollFD *poll_fds,
@@ -914,23 +926,6 @@ _cogl_winsys_poll_dispatch (CoglRenderer *renderer,
 
         break;
       }
-
-  /* FIXME: instead of requiring event dispatching which is handled at
-   * the CoglRenderer level to have to know about CoglContext we
-   * should have a generalized way of queuing an idle function */
-  if (renderer->context &&
-      kms_renderer->pending_swap_notify)
-    {
-      CoglContext *context = renderer->context;
-
-      /* This needs to be cleared before invoking the callbacks in
-       * case the callbacks cause it to be set again */
-      kms_renderer->pending_swap_notify = FALSE;
-
-      g_list_foreach (context->framebuffers,
-                      flush_pending_swap_notify_cb,
-                      NULL);
-    }
 }
 
 static const CoglWinsysEGLVtable
@@ -970,7 +965,6 @@ _cogl_winsys_egl_kms_get_vtable (void)
       vtable.onscreen_swap_region = NULL;
       vtable.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers;
 
-      vtable.get_dispatch_timeout = _cogl_winsys_get_dispatch_timeout;
       vtable.poll_dispatch = _cogl_winsys_poll_dispatch;
 
       vtable_inited = TRUE;
diff --git a/cogl/winsys/cogl-winsys-egl-private.h b/cogl/winsys/cogl-winsys-egl-private.h
index d21f1b0..40c4747 100644
--- a/cogl/winsys/cogl-winsys-egl-private.h
+++ b/cogl/winsys/cogl-winsys-egl-private.h
@@ -82,7 +82,7 @@ typedef struct _CoglRendererEGL
   EGLint egl_version_major;
   EGLint egl_version_minor;
 
-  CoglBool pending_resize_notify;
+  CoglClosure *resize_notify_idle;
 
   /* Data specific to the EGL platform */
   void *platform;
diff --git a/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/winsys/cogl-winsys-egl-x11.c
index ec1ae1a..3ca8385 100644
--- a/cogl/winsys/cogl-winsys-egl-x11.c
+++ b/cogl/winsys/cogl-winsys-egl-x11.c
@@ -43,6 +43,7 @@
 #include "cogl-texture-pixmap-x11-private.h"
 #include "cogl-texture-2d-private.h"
 #include "cogl-error-private.h"
+#include "cogl-poll-private.h"
 
 #define COGL_ONSCREEN_X11_EVENT_MASK StructureNotifyMask
 
@@ -91,6 +92,42 @@ find_onscreen_for_xid (CoglContext *context, uint32_t xid)
 }
 
 static void
+flush_pending_resize_notifications_cb (void *data,
+                                       void *user_data)
+{
+  CoglFramebuffer *framebuffer = data;
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+    {
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+
+      if (egl_onscreen->pending_resize_notify)
+        {
+          _cogl_onscreen_notify_resize (onscreen);
+          egl_onscreen->pending_resize_notify = FALSE;
+        }
+    }
+}
+
+static void
+flush_pending_resize_notifications_idle (void *user_data)
+{
+  CoglContext *context = user_data;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererEGL *egl_renderer = renderer->winsys;
+
+  /* This needs to be disconnected before invoking the callbacks in
+   * case the callbacks cause it to be queued again */
+  cogl_closure_disconnect (egl_renderer->resize_notify_idle);
+  egl_renderer->resize_notify_idle = NULL;
+
+  g_list_foreach (context->framebuffers,
+                  flush_pending_resize_notifications_cb,
+                  NULL);
+}
+
+static void
 notify_resize (CoglContext *context,
                Window drawable,
                int width,
@@ -111,8 +148,16 @@ notify_resize (CoglContext *context,
 
   /* We only want to notify that a resize happened when the
    * application calls cogl_context_dispatch so instead of immediately
-   * notifying we'll set a flag to remember to notify later */
-  egl_renderer->pending_resize_notify = TRUE;
+   * notifying we queue an idle callback */
+  if (!egl_renderer->resize_notify_idle)
+    {
+      egl_renderer->resize_notify_idle =
+        _cogl_poll_renderer_add_idle (renderer,
+                                      flush_pending_resize_notifications_idle,
+                                      context,
+                                      NULL);
+    }
+
   egl_onscreen->pending_resize_notify = TRUE;
 }
 
@@ -625,60 +670,17 @@ _cogl_winsys_xlib_get_visual_info (void)
 static int64_t
 _cogl_winsys_get_dispatch_timeout (CoglRenderer *renderer)
 {
-  CoglRendererEGL *egl_renderer = renderer->winsys;
-
-  if (egl_renderer->pending_resize_notify)
-    return 0;
-
   return _cogl_xlib_renderer_get_dispatch_timeout (renderer);
 }
 
 static void
-flush_pending_notifications_cb (void *data,
-                                void *user_data)
-{
-  CoglFramebuffer *framebuffer = data;
-
-  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
-    {
-      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
-      CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
-
-      if (egl_onscreen->pending_resize_notify)
-        {
-          _cogl_onscreen_notify_resize (onscreen);
-          egl_onscreen->pending_resize_notify = FALSE;
-        }
-    }
-}
-
-static void
 _cogl_winsys_poll_dispatch (CoglRenderer *renderer,
                             const CoglPollFD *poll_fds,
                             int n_poll_fds)
 {
-  CoglRendererEGL *egl_renderer = renderer->winsys;
-
   _cogl_xlib_renderer_poll_dispatch (renderer,
                                      poll_fds,
                                      n_poll_fds);
-
-  /* FIXME: instead of requiring event dispatching which is handled at
-   * the CoglRenderer level to have to know about CoglContext we
-   * should have a generalized way of queuing an idle function */
-  if (renderer->context &&
-      egl_renderer->pending_resize_notify)
-    {
-      CoglContext *context = renderer->context;
-
-      /* This needs to be cleared before invoking the callbacks in
-       * case the callbacks cause it to be set again */
-      egl_renderer->pending_resize_notify = FALSE;
-
-      g_list_foreach (context->framebuffers,
-                      flush_pending_notifications_cb,
-                      NULL);
-    }
 }
 
 #ifdef EGL_KHR_image_pixmap
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 15d34c2..dda4a9f 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -50,6 +50,7 @@
 #include "cogl-util.h"
 #include "cogl-winsys-glx-private.h"
 #include "cogl-error-private.h"
+#include "cogl-poll-private.h"
 
 #include <stdlib.h>
 #include <sys/types.h>
@@ -301,13 +302,85 @@ _cogl_winsys_get_clock_time (CoglContext *context)
 }
 
 static void
+flush_pending_notifications_cb (void *data,
+                                void *user_data)
+{
+  CoglFramebuffer *framebuffer = data;
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+    {
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
+      CoglBool pending_sync_notify = glx_onscreen->pending_sync_notify;
+      CoglBool pending_complete_notify = glx_onscreen->pending_complete_notify;
+
+      /* If swap_region is called then notifying the sync event could
+       * potentially immediately queue a subsequent pending notify so
+       * we need to clear the flag before invoking the callback */
+      glx_onscreen->pending_sync_notify = FALSE;
+      glx_onscreen->pending_complete_notify = FALSE;
+
+      if (pending_sync_notify)
+        {
+          CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos);
+
+          _cogl_onscreen_notify_frame_sync (onscreen, info);
+        }
+
+      if (pending_complete_notify)
+        {
+          CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
+
+          _cogl_onscreen_notify_complete (onscreen, info);
+
+          cogl_object_unref (info);
+        }
+
+      if (glx_onscreen->pending_resize_notify)
+        {
+          _cogl_onscreen_notify_resize (onscreen);
+          glx_onscreen->pending_resize_notify = FALSE;
+        }
+    }
+}
+
+static void
+flush_pending_notifications_idle (void *user_data)
+{
+  CoglContext *context = user_data;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+  /* This needs to be disconnected before invoking the callbacks in
+   * case the callbacks cause it to be queued again */
+  cogl_closure_disconnect (glx_renderer->flush_notifications_idle);
+  glx_renderer->flush_notifications_idle = NULL;
+
+  g_list_foreach (context->framebuffers,
+                  flush_pending_notifications_cb,
+                  NULL);
+}
+
+static void
 set_sync_pending (CoglOnscreen *onscreen)
 {
   CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
-  CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+  /* We only want to dispatch sync events when the application calls
+   * cogl_context_dispatch so instead of immediately notifying we
+   * queue an idle callback */
+  if (!glx_renderer->flush_notifications_idle)
+    {
+      glx_renderer->flush_notifications_idle =
+        _cogl_poll_renderer_add_idle (renderer,
+                                      flush_pending_notifications_idle,
+                                      context,
+                                      NULL);
+    }
 
-  glx_renderer->pending_sync_notify = TRUE;
   glx_onscreen->pending_sync_notify = TRUE;
 }
 
@@ -316,9 +389,21 @@ set_complete_pending (CoglOnscreen *onscreen)
 {
   CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
   CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context;
-  CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglGLXRenderer *glx_renderer = renderer->winsys;
+
+  /* We only want to notify swap completion when the application calls
+   * cogl_context_dispatch so instead of immediately notifying we
+   * queue an idle callback */
+  if (!glx_renderer->flush_notifications_idle)
+    {
+      glx_renderer->flush_notifications_idle =
+        _cogl_poll_renderer_add_idle (renderer,
+                                      flush_pending_notifications_idle,
+                                      context,
+                                      NULL);
+    }
 
-  glx_renderer->pending_complete_notify = TRUE;
   glx_onscreen->pending_complete_notify = TRUE;
 }
 
@@ -385,7 +470,8 @@ notify_resize (CoglContext *context,
   CoglOnscreen *onscreen = find_onscreen_for_xid (context,
                                                   configure_event->window);
   CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
-  CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglGLXRenderer *glx_renderer = renderer->winsys;
   CoglOnscreenGLX *glx_onscreen;
   CoglOnscreenXlib *xlib_onscreen;
 
@@ -400,9 +486,17 @@ notify_resize (CoglContext *context,
                                         configure_event->height);
 
   /* We only want to notify that a resize happened when the
-     application calls cogl_context_dispatch so instead of immediately
-     notifying we'll set a flag to remember to notify later */
-  glx_renderer->pending_resize_notify = TRUE;
+   * application calls cogl_context_dispatch so instead of immediately
+   * notifying we queue an idle callback */
+  if (!glx_renderer->flush_notifications_idle)
+    {
+      glx_renderer->flush_notifications_idle =
+        _cogl_poll_renderer_add_idle (renderer,
+                                      flush_pending_notifications_idle,
+                                      context,
+                                      NULL);
+    }
+
   glx_onscreen->pending_resize_notify = TRUE;
 
   if (!xlib_onscreen->is_foreign_xwin)
@@ -2499,94 +2593,15 @@ _cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap)
 static int64_t
 _cogl_winsys_get_dispatch_timeout (CoglRenderer *renderer)
 {
-  CoglGLXRenderer *glx_renderer = renderer->winsys;
-
-  /* If we've already got a pending swap notify then we'll dispatch
-   * immediately */
-  if (glx_renderer->pending_sync_notify ||
-      glx_renderer->pending_resize_notify ||
-      glx_renderer->pending_complete_notify)
-    return 0;
-
   return _cogl_xlib_renderer_get_dispatch_timeout (renderer);
 }
 
 static void
-flush_pending_notifications_cb (void *data,
-                                void *user_data)
-{
-  CoglFramebuffer *framebuffer = data;
-
-  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
-    {
-      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
-      CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
-      CoglBool pending_sync_notify = glx_onscreen->pending_sync_notify;
-      CoglBool pending_complete_notify = glx_onscreen->pending_complete_notify;
-
-      /* If swap_region is called then notifying the sync event could
-       * potentially immediately queue a subsequent pending notify so
-       * we need to clear the flag before invoking the callback */
-      glx_onscreen->pending_sync_notify = FALSE;
-      glx_onscreen->pending_complete_notify = FALSE;
-
-      if (pending_sync_notify)
-        {
-          CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos);
-
-          _cogl_onscreen_notify_frame_sync (onscreen, info);
-        }
-
-      if (pending_complete_notify)
-        {
-          CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos);
-
-          _cogl_onscreen_notify_complete (onscreen, info);
-
-          cogl_object_unref (info);
-        }
-
-      if (glx_onscreen->pending_resize_notify)
-        {
-          _cogl_onscreen_notify_resize (onscreen);
-          glx_onscreen->pending_resize_notify = FALSE;
-        }
-    }
-}
-
-static void
 _cogl_winsys_poll_dispatch (CoglRenderer *renderer,
                             const CoglPollFD *poll_fds,
                             int n_poll_fds)
 {
-  CoglGLXRenderer *glx_renderer = renderer->winsys;
-
-  _cogl_xlib_renderer_poll_dispatch (renderer,
-                                     poll_fds,
-                                     n_poll_fds);
-
-  /* FIXME: instead of requiring event dispatching which is handled at
-   * the CoglRenderer level to have to know about CoglContext we
-   * should have a generalized way of queuing an idle function */
-  if (renderer->context)
-    {
-      CoglContext *context = renderer->context;
-
-      if (glx_renderer->pending_sync_notify ||
-          glx_renderer->pending_resize_notify ||
-          glx_renderer->pending_complete_notify)
-        {
-          /* These need to be cleared before invoking the callbacks in
-           * case the callbacks cause them to be set again */
-          glx_renderer->pending_sync_notify = FALSE;
-          glx_renderer->pending_resize_notify = FALSE;
-          glx_renderer->pending_complete_notify = FALSE;
-
-          g_list_foreach (context->framebuffers,
-                          flush_pending_notifications_cb,
-                          NULL);
-        }
-    }
+  _cogl_xlib_renderer_poll_dispatch (renderer, poll_fds, n_poll_fds);
 }
 
 static CoglWinsysVtable _cogl_winsys_vtable =
diff --git a/cogl/winsys/cogl-winsys-sdl.c b/cogl/winsys/cogl-winsys-sdl.c
index ddaaff5..0f318a3 100644
--- a/cogl/winsys/cogl-winsys-sdl.c
+++ b/cogl/winsys/cogl-winsys-sdl.c
@@ -41,7 +41,7 @@
 
 typedef struct _CoglRendererSdl
 {
-  CoglBool pending_resize_notify;
+  CoglClosure *resize_notify_idle;
 } CoglRendererSdl;
 
 typedef struct _CoglDisplaySdl
@@ -209,6 +209,20 @@ error:
   return FALSE;
 }
 
+static void
+flush_pending_resize_notification_idle (CoglContext *context)
+{
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererSdl *sdl_renderer = renderer->winsys;
+
+  /* This needs to be disconnected before invoking the callbacks in
+   * case the callbacks cause it to be queued again */
+  cogl_closure_disconnect (sdl_renderer->resize_notify_idle);
+  sdl_renderer->resize_notify_idle = NULL;
+
+  _cogl_onscreen_notify_resize (onscreen);
+}
+
 static CoglFilterReturn
 sdl_event_filter_cb (SDL_Event *event, void *data)
 {
@@ -234,9 +248,15 @@ sdl_event_filter_cb (SDL_Event *event, void *data)
 
       /* We only want to notify that a resize happened when the
        * application calls cogl_context_dispatch so instead of
-       * immediately notifying we'll set a flag to remember to notify
-       * later */
-      sdl_renderer->pending_resize_notify = TRUE;
+       * immediately notifying we queue an idle callback */
+      if (!sdl_renderer->resize_notify_idle)
+        {
+          sdl_renderer->resize_notify_idle =
+            _cogl_poll_renderer_add_idle (renderer,
+                                          flush_pending_resize_notify_idle,
+                                          context,
+                                          NULL);
+        }
 
       return COGL_FILTER_CONTINUE;
     }
@@ -378,31 +398,6 @@ _cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen,
                                            sdl_display->video_mode_flags);
 }
 
-static void
-_cogl_winsys_poll_dispatch (CoglRenderer *renderer,
-                            const CoglPollFD *poll_fds,
-                            int n_poll_fds)
-{
-  CoglRendererSdl *sdl_renderer = renderer->winsys;
-
-  /* FIXME: instead of requiring event dispatching which is handled at
-   * the CoglRenderer level to have to know about CoglContext we
-   * should have a generalized way of queuing an idle function */
-  if (renderer->context &&
-      sdl_renderer->pending_resize_notify)
-    {
-      CoglContext *context = renderer->context;
-      CoglDisplaySdl *sdl_display = context->display->winsys;
-      CoglOnscreen *onscreen = sdl_display->onscreen;
-
-      g_return_if_fail (onscreen != NULL);
-
-      _cogl_onscreen_notify_resize (onscreen);
-
-      sdl_renderer->pending_resize_notify = FALSE;
-    }
-}
-
 const CoglWinsysVtable *
 _cogl_winsys_sdl_get_vtable (void)
 {
@@ -436,8 +431,6 @@ _cogl_winsys_sdl_get_vtable (void)
       vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility;
       vtable.onscreen_set_resizable = _cogl_winsys_onscreen_set_resizable;
 
-      vtable.poll_dispatch = _cogl_winsys_poll_dispatch;
-
       vtable_inited = TRUE;
     }
 
diff --git a/cogl/winsys/cogl-winsys-sdl2.c b/cogl/winsys/cogl-winsys-sdl2.c
index 24ba3b2..8515d6b 100644
--- a/cogl/winsys/cogl-winsys-sdl2.c
+++ b/cogl/winsys/cogl-winsys-sdl2.c
@@ -38,6 +38,7 @@
 #include "cogl-onscreen-private.h"
 #include "cogl-winsys-sdl-private.h"
 #include "cogl-error-private.h"
+#include "cogl-poll-private.h"
 #include "cogl-sdl.h"
 
 typedef struct _CoglContextSdl2
@@ -47,7 +48,7 @@ typedef struct _CoglContextSdl2
 
 typedef struct _CoglRendererSdl2
 {
-  CoglBool pending_resize_notify;
+  CoglClosure *resize_notify_idle;
 } CoglRendererSdl2;
 
 typedef struct _CoglDisplaySdl2
@@ -265,6 +266,42 @@ error:
   return FALSE;
 }
 
+static void
+flush_pending_notifications_cb (void *data,
+                                void *user_data)
+{
+  CoglFramebuffer *framebuffer = data;
+
+  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
+    {
+      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
+      CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys;
+
+      if (sdl_onscreen->pending_resize_notify)
+        {
+          _cogl_onscreen_notify_resize (onscreen);
+          sdl_onscreen->pending_resize_notify = FALSE;
+        }
+    }
+}
+
+static void
+flush_pending_resize_notifications_idle (void *user_data)
+{
+  CoglContext *context = user_data;
+  CoglRenderer *renderer = context->display->renderer;
+  CoglRendererSdl2 *sdl_renderer = renderer->winsys;
+
+  /* This needs to be disconnected before invoking the callbacks in
+   * case the callbacks cause it to be queued again */
+  cogl_closure_disconnect (sdl_renderer->resize_notify_idle);
+  sdl_renderer->resize_notify_idle = NULL;
+
+  g_list_foreach (context->framebuffers,
+                  flush_pending_notifications_cb,
+                  NULL);
+}
+
 static CoglFilterReturn
 sdl_event_filter_cb (SDL_Event *event, void *data)
 {
@@ -273,7 +310,8 @@ sdl_event_filter_cb (SDL_Event *event, void *data)
     {
       CoglContext *context = data;
       CoglDisplay *display = context->display;
-      CoglRendererSdl2 *sdl_renderer = display->renderer->winsys;
+      CoglRenderer *renderer = display->renderer;
+      CoglRendererSdl2 *sdl_renderer = renderer->winsys;
       float width = event->window.data1;
       float height = event->window.data2;
       CoglFramebuffer *framebuffer;
@@ -292,13 +330,19 @@ sdl_event_filter_cb (SDL_Event *event, void *data)
 
       _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
 
-      sdl_onscreen = COGL_ONSCREEN (framebuffer)->winsys;
-
       /* We only want to notify that a resize happened when the
        * application calls cogl_context_dispatch so instead of
-       * immediately notifying we'll set a flag to remember to notify
-       * later */
-      sdl_renderer->pending_resize_notify = TRUE;
+       * immediately notifying we queue an idle callback */
+      if (!sdl_renderer->resize_notify_idle)
+        {
+          sdl_renderer->resize_notify_idle =
+            _cogl_poll_renderer_add_idle (renderer,
+                                          flush_pending_resize_notifications_idle,
+                                          context,
+                                          NULL);
+        }
+
+      sdl_onscreen = COGL_ONSCREEN (framebuffer)->winsys;
       sdl_onscreen->pending_resize_notify = TRUE;
 
       return COGL_FILTER_CONTINUE;
@@ -486,47 +530,6 @@ _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
     SDL_HideWindow (sdl_onscreen->window);
 }
 
-static void
-flush_pending_notifications_cb (void *data,
-                                void *user_data)
-{
-  CoglFramebuffer *framebuffer = data;
-
-  if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN)
-    {
-      CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
-      CoglOnscreenSdl2 *sdl_onscreen = onscreen->winsys;
-
-      if (sdl_onscreen->pending_resize_notify)
-        {
-          _cogl_onscreen_notify_resize (onscreen);
-          sdl_onscreen->pending_resize_notify = FALSE;
-        }
-    }
-}
-
-static void
-_cogl_winsys_poll_dispatch (CoglRenderer *renderer,
-                            const CoglPollFD *poll_fds,
-                            int n_poll_fds)
-{
-  CoglRendererSdl2 *sdl_renderer = renderer->winsys;
-
-  /* FIXME: instead of requiring event dispatching which is handled at
-   * the CoglRenderer level to have to know about CoglContext we
-   * should have a generalized way of queuing an idle function */
-  if (renderer->context &&
-      sdl_renderer->pending_resize_notify)
-    {
-      CoglContext *context = renderer->context;
-
-      g_list_foreach (context->framebuffers,
-                      flush_pending_notifications_cb,
-                      NULL);
-      sdl_renderer->pending_resize_notify = FALSE;
-    }
-}
-
 SDL_Window *
 cogl_sdl_onscreen_get_window (CoglOnscreen *onscreen)
 {
@@ -574,8 +577,6 @@ _cogl_winsys_sdl_get_vtable (void)
         _cogl_winsys_onscreen_update_swap_throttled;
       vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility;
 
-      vtable.poll_dispatch = _cogl_winsys_poll_dispatch;
-
       vtable_inited = TRUE;
     }
 
-- 
1.8.2.1



More information about the Cogl mailing list