[Cogl] [PATCH 2/2] wayland: Send COGL_FRAME_EVENT_SYNC based on the frame callback

Neil Roberts neil at linux.intel.com
Tue Jul 9 11:26:31 PDT 2013


Instead of queuing the frame sync event immediately after a swap, the
Wayland winsys now installs a frame callback and queues the event when
Wayland reports that the frame is complete. It also reports the
COGL_FRAME_EVENT_COMPLETE event at the same time because there is no
more information we can give. The presentation time on the
CoglFrameInfo is filled in from the timestamp in the callback
function.

This patch is a bit of a divergence from how the events are handled in
the GLX winsys. Instead of installing its own idle function, the
_cogl_onscreen_queue_event() function has now been made non-static so
that it can be used by the Wayland winsys. The frame callback now just
queues an event using that. The pending_frame_infos queue on the
CoglOnscreen isn't used and instead the CoglFrameInfo is immediately
popped off the queue so that it can be stored as part of the closure
data when the frame callback is set up. That way it would use the
right frame info even if somehow the Wayland callbacks were invoked in
the wrong order and the code is a bit simpler.
---
 cogl/cogl-onscreen-private.h          |  5 +++
 cogl/cogl-onscreen.c                  |  2 +-
 cogl/winsys/cogl-winsys-egl-wayland.c | 84 +++++++++++++++++++++++++++++++++++
 3 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/cogl/cogl-onscreen-private.h b/cogl/cogl-onscreen-private.h
index 1fd62ea..7314c86 100644
--- a/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl-onscreen-private.h
@@ -93,6 +93,11 @@ _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer,
                                       int width, int height);
 
 void
+_cogl_onscreen_queue_event (CoglOnscreen *onscreen,
+                            CoglFrameEvent type,
+                            CoglFrameInfo *info);
+
+void
 _cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info);
 
 void
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index c7492c8..3782a21 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -209,7 +209,7 @@ _cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen)
   _cogl_onscreen_queue_dirty (onscreen, &info);
 }
 
-static void
+void
 _cogl_onscreen_queue_event (CoglOnscreen *onscreen,
                             CoglFrameEvent type,
                             CoglFrameInfo *info)
diff --git a/cogl/winsys/cogl-winsys-egl-wayland.c b/cogl/winsys/cogl-winsys-egl-wayland.c
index 075c258..2d20382 100644
--- a/cogl/winsys/cogl-winsys-egl-wayland.c
+++ b/cogl/winsys/cogl-winsys-egl-wayland.c
@@ -41,6 +41,7 @@
 #include "cogl-wayland-renderer.h"
 #include "cogl-error-private.h"
 #include "cogl-poll-private.h"
+#include "cogl-frame-info-private.h"
 
 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
 
@@ -77,8 +78,18 @@ typedef struct _CoglOnscreenWayland
   CoglBool has_pending;
 
   CoglBool shell_surface_type_set;
+
+  CoglList frame_callbacks;
 } CoglOnscreenWayland;
 
+typedef struct
+{
+  CoglList link;
+  CoglFrameInfo *frame_info;
+  struct wl_callback *callback;
+  CoglOnscreen *onscreen;
+} FrameCallbackData;
+
 static void
 registry_handle_global_cb (void *data,
                            struct wl_registry *registry,
@@ -403,6 +414,12 @@ _cogl_winsys_egl_context_init (CoglContext *context,
   COGL_FLAGS_SET (context->winsys_features,
                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
                   TRUE);
+  COGL_FLAGS_SET (context->winsys_features,
+                  COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT,
+                  TRUE);
+  COGL_FLAGS_SET (context->features,
+                  COGL_FEATURE_ID_PRESENTATION_TIME,
+                  TRUE);
 
   /* We'll manually handle queueing dirty events when the surface is
    * first shown or when it is resized. Note that this is slightly
@@ -433,6 +450,8 @@ _cogl_winsys_egl_onscreen_init (CoglOnscreen *onscreen,
   wayland_onscreen = g_slice_new0 (CoglOnscreenWayland);
   egl_onscreen->platform = wayland_onscreen;
 
+  _cogl_list_init (&wayland_onscreen->frame_callbacks);
+
   if (onscreen->foreign_surface)
     wayland_onscreen->wayland_surface = onscreen->foreign_surface;
   else
@@ -476,10 +495,26 @@ _cogl_winsys_egl_onscreen_init (CoglOnscreen *onscreen,
 }
 
 static void
+free_frame_callback_data (FrameCallbackData *callback_data)
+{
+  cogl_object_unref (callback_data->frame_info);
+  wl_callback_destroy (callback_data->callback);
+  _cogl_list_remove (&callback_data->link);
+  g_slice_free (FrameCallbackData, callback_data);
+}
+
+static void
 _cogl_winsys_egl_onscreen_deinit (CoglOnscreen *onscreen)
 {
   CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
   CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
+  FrameCallbackData *frame_callback_data, *tmp;
+
+  _cogl_list_for_each_safe (frame_callback_data,
+                            tmp,
+                            &wayland_onscreen->frame_callbacks,
+                            link)
+    free_frame_callback_data (frame_callback_data);
 
   if (wayland_onscreen->wayland_egl_native_window)
     {
@@ -535,12 +570,61 @@ flush_pending_resize (CoglOnscreen *onscreen)
 }
 
 static void
+frame_cb (void *data,
+          struct wl_callback *callback,
+          uint32_t time)
+{
+  FrameCallbackData *callback_data = data;
+  CoglFrameInfo *info = callback_data->frame_info;
+  CoglOnscreen *onscreen = callback_data->onscreen;
+
+  g_assert (callback_data->callback == callback);
+
+  info->presentation_time = time * (int64_t) 1000000;
+
+  _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info);
+  _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info);
+
+  free_frame_callback_data (callback_data);
+}
+
+static const struct wl_callback_listener
+frame_listener =
+{
+  frame_cb
+};
+
+static void
 _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
                                                 const int *rectangles,
                                                 int n_rectangles)
 {
+  CoglOnscreenEGL *egl_onscreen = onscreen->winsys;
+  CoglOnscreenWayland *wayland_onscreen = egl_onscreen->platform;
+  FrameCallbackData *frame_callback_data = g_slice_new (FrameCallbackData);
+
   flush_pending_resize (onscreen);
 
+  /* Before calling the winsys function,
+   * cogl_onscreen_swap_buffers_with_damage() will have pushed the
+   * frame info object onto the end of the pending frames. We can grab
+   * it out of the queue now because we don't care about the order and
+   * we will just directly queue the event corresponding to the exact
+   * frame that Wayland reports as completed. This will steal the
+   * reference */
+  frame_callback_data->frame_info =
+    g_queue_pop_tail (&onscreen->pending_frame_infos);
+  frame_callback_data->onscreen = onscreen;
+
+  frame_callback_data->callback =
+    wl_surface_frame (wayland_onscreen->wayland_surface);
+  wl_callback_add_listener (frame_callback_data->callback,
+                            &frame_listener,
+                            frame_callback_data);
+
+  _cogl_list_insert (&wayland_onscreen->frame_callbacks,
+                     &frame_callback_data->link);
+
   parent_vtable->onscreen_swap_buffers_with_damage (onscreen,
                                                     rectangles,
                                                     n_rectangles);
-- 
1.7.11.3.g3c3efa5



More information about the Cogl mailing list