[Cogl] [PATCH v3] Add a callback to get dirty events from a CoglOnscreen

Neil Roberts neil at linux.intel.com
Tue May 28 08:39:17 PDT 2013


If I land the patch to make the Wayland winsys delay setting the
surface type until the onscreen is first shown then the previous
version of this patch will send the dirty event at the wrong time
because the buffers that are attached before the surface type is set
are effectively ignored.

Here is a third version of the patch that makes the Wayland winsys set
the private feature for dirty events so that it can send them only
when the onscreen is shown instead of when it is allocated.

The default behaviour for when the winsys doesn't support the private
feature is left as sending the event when the onscreen is allocated
because that probably makes more sense for winsys's like KMS and EGL
null.

Regards,
- Neil

-- >8 --

This adds a callback that can be registered with
cogl_onscreen_add_dirty_callback which will get called whenever the
window system determines that the contents of the window is dirty and
needs to be redrawn. Under the two X-based winsys's, this is reported
off the back of the Expose events, under SDL it is reported from
SDL_VIDEOEXPOSE or SDL_WINDOWEVENT_EXPOSED and under Windows from the
WM_PAINT messages. The Wayland winsys doesn't really have the concept
of dirtying the buffer but in order to allow applications to work the
same way on all platforms it will emit the event when the surface is
first shown and whenever it is resized.

There is a private feature flag to specify whether dirty events are
supported. If the winsys does not set this then Cogl will simulate
dirty events by emitting one when the window is first allocated and
when it is resized. The only winsys's that don't set this flag are
things like KMS or the EGL null winsys where there is no windowing
system and showing and hiding the onscreen doesn't really make any
sense. In that case Cogl can assume the buffer will only become dirty
once when it is first allocated.
---
 cogl/cogl-context-private.h            |   3 +-
 cogl/cogl-context.c                    |   3 +-
 cogl/cogl-framebuffer.c                |   9 +-
 cogl/cogl-onscreen-private.h           |  23 +++-
 cogl/cogl-onscreen.c                   | 186 +++++++++++++++++++++++----------
 cogl/cogl-onscreen.h                   | 113 +++++++++++++++++++-
 cogl/cogl-private.h                    |   9 +-
 cogl/winsys/cogl-winsys-egl-wayland.c  |  13 +++
 cogl/winsys/cogl-winsys-egl-x11.c      |  25 ++++-
 cogl/winsys/cogl-winsys-glx.c          |  26 ++++-
 cogl/winsys/cogl-winsys-sdl.c          |  37 +++++--
 cogl/winsys/cogl-winsys-sdl2.c         |  70 +++++++++----
 cogl/winsys/cogl-winsys-wgl.c          |  28 +++++
 doc/reference/cogl2/cogl2-sections.txt |   7 ++
 14 files changed, 459 insertions(+), 93 deletions(-)

diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index cf41c88..3512486 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2007,2008,2009 Intel Corporation.
+ * Copyright (C) 2007,2008,2009,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -174,6 +174,7 @@ struct _CoglContext
   CoglOffscreenAllocateFlags last_offscreen_allocate_flags;
 
   CoglOnscreenEventList onscreen_events_queue;
+  CoglOnscreenQueuedDirtyList onscreen_dirty_queue;
   CoglClosure *onscreen_dispatch_idle;
 
   CoglGLES2Context *current_gles2_context;
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index e4714be..13fb585 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2007,2008,2009 Intel Corporation.
+ * Copyright (C) 2007,2008,2009,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -288,6 +288,7 @@ cogl_context_new (CoglDisplay *display,
   context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL;
 
   COGL_TAILQ_INIT (&context->onscreen_events_queue);
+  COGL_TAILQ_INIT (&context->onscreen_dirty_queue);
 
   g_queue_init (&context->gles2_context_stack);
 
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index a917af8..5fd0d23 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -643,6 +643,7 @@ cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
 {
   CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
   const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer);
+  CoglContext *ctx = framebuffer->context;
 
   if (framebuffer->allocated)
     return TRUE;
@@ -660,10 +661,16 @@ cogl_framebuffer_allocate (CoglFramebuffer *framebuffer,
 
       if (!winsys->onscreen_init (onscreen, error))
         return FALSE;
+
+      /* If the winsys doesn't support dirty events then we'll report
+       * one on allocation so that if the application only paints in
+       * response to dirty events then it will at least paint once to
+       * start */
+      if (!(ctx->private_feature_flags & COGL_PRIVATE_FEATURE_DIRTY_EVENTS))
+        _cogl_onscreen_queue_full_dirty (onscreen);
     }
   else
     {
-      CoglContext *ctx = framebuffer->context;
       CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer);
 
       if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
diff --git a/cogl/cogl-onscreen-private.h b/cogl/cogl-onscreen-private.h
index 4c2ce94..e960dc3 100644
--- a/cogl/cogl-onscreen-private.h
+++ b/cogl/cogl-onscreen-private.h
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -48,6 +48,18 @@ struct _CoglOnscreenEvent
   CoglFrameEvent type;
 };
 
+typedef struct _CoglOnscreenQueuedDirty CoglOnscreenQueuedDirty;
+
+COGL_TAILQ_HEAD (CoglOnscreenQueuedDirtyList, CoglOnscreenQueuedDirty);
+
+struct _CoglOnscreenQueuedDirty
+{
+  COGL_TAILQ_ENTRY (CoglOnscreenQueuedDirty) list_node;
+
+  CoglOnscreen *onscreen;
+  CoglOnscreenDirtyInfo info;
+};
+
 struct _CoglOnscreen
 {
   CoglFramebuffer  _parent;
@@ -73,6 +85,8 @@ struct _CoglOnscreen
   CoglBool resizable;
   CoglClosureList resize_closures;
 
+  CoglClosureList dirty_closures;
+
   int64_t frame_counter;
   int64_t swap_frame_counter; /* frame counter at last all to
                                * cogl_onscreen_swap_region() or
@@ -96,6 +110,11 @@ void
 _cogl_onscreen_notify_resize (CoglOnscreen *onscreen);
 
 void
-_cogl_dispatch_onscreen_events (CoglContext *context);
+_cogl_onscreen_queue_dirty (CoglOnscreen *onscreen,
+                            const CoglOnscreenDirtyInfo *info);
+
+
+void
+_cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen);
 
 #endif /* __COGL_ONSCREEN_PRIVATE_H */
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index 9f4a215..f686b14 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -49,6 +49,7 @@ _cogl_onscreen_init_from_template (CoglOnscreen *onscreen,
 
   COGL_LIST_INIT (&onscreen->frame_closures);
   COGL_LIST_INIT (&onscreen->resize_closures);
+  COGL_LIST_INIT (&onscreen->dirty_closures);
 
   framebuffer->config = onscreen_template->config;
   cogl_object_ref (framebuffer->config.swap_chain);
@@ -92,6 +93,7 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
 
   _cogl_closure_list_disconnect_all (&onscreen->resize_closures);
   _cogl_closure_list_disconnect_all (&onscreen->frame_closures);
+  _cogl_closure_list_disconnect_all (&onscreen->dirty_closures);
 
   while ((frame_info = g_queue_pop_tail (&onscreen->pending_frame_infos)))
     cogl_object_unref (frame_info);
@@ -107,6 +109,111 @@ _cogl_onscreen_free (CoglOnscreen *onscreen)
 }
 
 static void
+notify_event (CoglOnscreen *onscreen,
+              CoglFrameEvent event,
+              CoglFrameInfo *info)
+{
+  _cogl_closure_list_invoke (&onscreen->frame_closures,
+                             CoglFrameCallback,
+                             onscreen, event, info);
+}
+
+static void
+_cogl_dispatch_onscreen_cb (CoglContext *context)
+{
+  CoglOnscreenEvent *event, *tmp;
+  CoglOnscreenEventList queue;
+
+  /* Dispatching the event callback may cause another frame to be
+   * drawn which in may cause another event to be queued immediately.
+   * To make sure this loop will only dispatch one set of events we'll
+   * steal the queue and iterate that separately */
+  COGL_TAILQ_INIT (&queue);
+  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,
+                           tmp)
+    {
+      CoglOnscreen *onscreen = event->onscreen;
+      CoglFrameInfo *info = event->info;
+
+      notify_event (onscreen, event->type, info);
+
+      cogl_object_unref (onscreen);
+      cogl_object_unref (info);
+
+      g_slice_free (CoglOnscreenEvent, event);
+    }
+
+  while (!COGL_TAILQ_EMPTY (&context->onscreen_dirty_queue))
+    {
+      CoglOnscreenQueuedDirty *qe =
+        COGL_TAILQ_FIRST (&context->onscreen_dirty_queue);
+
+      COGL_TAILQ_REMOVE (&context->onscreen_dirty_queue, qe, list_node);
+
+      _cogl_closure_list_invoke (&qe->onscreen->dirty_closures,
+                                 CoglOnscreenDirtyCallback,
+                                 qe->onscreen,
+                                 &qe->info);
+
+      cogl_object_unref (qe->onscreen);
+
+      g_slice_free (CoglOnscreenQueuedDirty, qe);
+    }
+}
+
+static void
+_cogl_onscreen_queue_dispatch_idle (CoglOnscreen *onscreen)
+{
+  CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context;
+
+  if (!ctx->onscreen_dispatch_idle)
+    {
+      ctx->onscreen_dispatch_idle =
+        _cogl_poll_renderer_add_idle (ctx->display->renderer,
+                                      (CoglIdleCallback)
+                                      _cogl_dispatch_onscreen_cb,
+                                      ctx,
+                                      NULL);
+    }
+}
+
+void
+_cogl_onscreen_queue_dirty (CoglOnscreen *onscreen,
+                            const CoglOnscreenDirtyInfo *info)
+{
+  CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context;
+  CoglOnscreenQueuedDirty *qe = g_slice_new (CoglOnscreenQueuedDirty);
+
+  qe->onscreen = cogl_object_ref (onscreen);
+  qe->info = *info;
+  COGL_TAILQ_INSERT_TAIL (&ctx->onscreen_dirty_queue, qe, list_node);
+
+  _cogl_onscreen_queue_dispatch_idle (onscreen);
+}
+
+void
+_cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen)
+{
+  CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+  CoglOnscreenDirtyInfo info;
+
+  info.x = 0;
+  info.y = 0;
+  info.width = framebuffer->width;
+  info.height = framebuffer->height;
+
+  _cogl_onscreen_queue_dirty (onscreen, &info);
+}
+
+static void
 _cogl_onscreen_queue_event (CoglOnscreen *onscreen,
                             CoglFrameEvent type,
                             CoglFrameInfo *info)
@@ -121,15 +228,7 @@ _cogl_onscreen_queue_event (CoglOnscreen *onscreen,
 
   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);
-    }
+  _cogl_onscreen_queue_dispatch_idle (onscreen);
 }
 
 void
@@ -395,50 +494,6 @@ cogl_onscreen_hide (CoglOnscreen *onscreen)
     }
 }
 
-static void
-notify_event (CoglOnscreen *onscreen,
-              CoglFrameEvent event,
-              CoglFrameInfo *info)
-{
-  _cogl_closure_list_invoke (&onscreen->frame_closures,
-                             CoglFrameCallback,
-                             onscreen, event, info);
-}
-
-void
-_cogl_dispatch_onscreen_events (CoglContext *context)
-{
-  CoglOnscreenEvent *event, *tmp;
-  CoglOnscreenEventList queue;
-
-  /* Dispatching the event callback may cause another frame to be
-   * drawn which in may cause another event to be queued immediately.
-   * To make sure this loop will only dispatch one set of events we'll
-   * steal the queue and iterate that separately */
-  COGL_TAILQ_INIT (&queue);
-  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,
-                           tmp)
-    {
-      CoglOnscreen *onscreen = event->onscreen;
-      CoglFrameInfo *info = event->info;
-
-      notify_event (onscreen, event->type, info);
-
-      cogl_object_unref (onscreen);
-      cogl_object_unref (info);
-
-      g_slice_free (CoglOnscreenEvent, event);
-    }
-}
-
 void
 _cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info)
 {
@@ -474,6 +529,10 @@ _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer,
   framebuffer->height = height;
 
   cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height);
+
+  if (!(framebuffer->context->private_feature_flags &
+        COGL_PRIVATE_FEATURE_DIRTY_EVENTS))
+    _cogl_onscreen_queue_full_dirty (COGL_ONSCREEN (framebuffer));
 }
 
 void
@@ -523,6 +582,27 @@ cogl_onscreen_remove_resize_callback (CoglOnscreen *onscreen,
   _cogl_closure_disconnect (closure);
 }
 
+CoglOnscreenDirtyClosure *
+cogl_onscreen_add_dirty_callback (CoglOnscreen *onscreen,
+                                  CoglOnscreenDirtyCallback callback,
+                                  void *user_data,
+                                  CoglUserDataDestroyCallback destroy)
+{
+  return _cogl_closure_list_add (&onscreen->dirty_closures,
+                                 callback,
+                                 user_data,
+                                 destroy);
+}
+
+void
+cogl_onscreen_remove_dirty_callback (CoglOnscreen *onscreen,
+                                     CoglOnscreenDirtyClosure *closure)
+{
+  _COGL_RETURN_IF_FAIL (closure);
+
+  _cogl_closure_disconnect (closure);
+}
+
 int64_t
 cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen)
 {
diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h
index 2646d40..d9582fa 100644
--- a/cogl/cogl-onscreen.h
+++ b/cogl/cogl-onscreen.h
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2011,2012 Intel Corporation.
+ * Copyright (C) 2011,2012,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -787,6 +787,117 @@ cogl_onscreen_remove_resize_callback (CoglOnscreen *onscreen,
                                       CoglOnscreenResizeClosure *closure);
 
 /**
+ * CoglOnscreenDirtyInfo:
+ * @x: Left edge of the dirty rectangle
+ * @y: Top edge of the dirty rectangle, measured from the top of the window
+ * @width: Width of the dirty rectangle
+ * @height: Height of the dirty rectangle
+ *
+ * A structure passed to callbacks registered using
+ * cogl_onscreen_add_dirty_callback(). The members describe a
+ * rectangle within the onscreen buffer that should be redrawn.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+typedef struct _CoglOnscreenDirtyInfo CoglOnscreenDirtyInfo;
+
+struct _CoglOnscreenDirtyInfo
+{
+  int x, y;
+  int width, height;
+};
+
+/**
+ * CoglOnscreenDirtyCallback:
+ * @onscreen: The onscreen that the frame is associated with
+ * @info: A #CoglOnscreenDirtyInfo struct containing the details of the
+ *   dirty area
+ * @user_data: The user pointer passed to
+ *             cogl_onscreen_add_frame_callback()
+ *
+ * Is a callback that can be registered via
+ * cogl_onscreen_add_dirty_callback() to be called when the windowing
+ * system determines that a region of the onscreen window has been
+ * lost and the application should redraw it.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+typedef void (*CoglOnscreenDirtyCallback) (CoglOnscreen *onscreen,
+                                           const CoglOnscreenDirtyInfo *info,
+                                           void *user_data);
+
+/**
+ * CoglOnscreenDirtyClosure:
+ *
+ * An opaque type that tracks a #CoglOnscreenDirtyCallback and associated
+ * user data. A #CoglOnscreenDirtyClosure pointer will be returned from
+ * cogl_onscreen_add_dirty_callback() and it allows you to remove a
+ * callback later using cogl_onscreen_remove_dirty_callback().
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+typedef struct _CoglClosure CoglOnscreenDirtyClosure;
+
+/**
+ * cogl_onscreen_add_dirty_callback:
+ * @onscreen: A #CoglOnscreen framebuffer
+ * @callback: A callback function to call for dirty events
+ * @user_data: A private pointer to be passed to @callback
+ * @destroy: An optional callback to destroy @user_data when the
+ *           @callback is removed or @onscreen is freed.
+ *
+ * Installs a @callback function that will be called whenever the
+ * window system has lost the contents of a region of the onscreen
+ * buffer and the application should redraw it to repair the buffer.
+ * For example this may happen in a window system without a compositor
+ * if a window that was previously covering up the onscreen window has
+ * been moved causing a region of the onscreen to be exposed.
+ *
+ * The @callback will be passed a #CoglOnscreenDirtyInfo struct which
+ * decribes a rectangle containing the newly dirtied region. Note that
+ * this may be called multiple times to describe a non-rectangular
+ * region composed of multiple smaller rectangles.
+ *
+ * The dirty events are separate from %COGL_FRAME_EVENT_SYNC events so
+ * the application should also listen for this event before rendering
+ * the dirty region to ensure that the framebuffer is actually ready
+ * for rendering.
+ *
+ * Return value: a #CoglOnscreenDirtyClosure pointer that can be used to
+ *               remove the callback and associated @user_data later.
+ * Since: 1.16
+ * Stability: unstable
+ */
+CoglOnscreenDirtyClosure *
+cogl_onscreen_add_dirty_callback (CoglOnscreen *onscreen,
+                                  CoglOnscreenDirtyCallback callback,
+                                  void *user_data,
+                                  CoglUserDataDestroyCallback destroy);
+
+/**
+ * cogl_onscreen_remove_dirty_callback:
+ * @onscreen: A #CoglOnscreen
+ * @closure: A #CoglOnscreenDirtyClosure returned from
+ *           cogl_onscreen_add_dirty_callback()
+ *
+ * Removes a callback and associated user data that were previously
+ * registered using cogl_onscreen_add_dirty_callback().
+ *
+ * If a destroy callback was passed to
+ * cogl_onscreen_add_dirty_callback() to destroy the user data then
+ * this will also get called.
+ *
+ * Since: 1.16
+ * Stability: unstable
+ */
+void
+cogl_onscreen_remove_dirty_callback (CoglOnscreen *onscreen,
+                                     CoglOnscreenDirtyClosure *closure);
+
+/**
  * cogl_is_onscreen:
  * @object: A #CoglObject pointer
  *
diff --git a/cogl/cogl-private.h b/cogl/cogl-private.h
index 08ee775..cbe8ca2 100644
--- a/cogl/cogl-private.h
+++ b/cogl/cogl-private.h
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2010 Intel Corporation.
+ * Copyright (C) 2010,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -57,7 +57,12 @@ typedef enum
   COGL_PRIVATE_FEATURE_ALPHA_TEXTURES = 1L<<21,
   COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE = 1L<<22,
   COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL = 1L<<23,
-  COGL_PRIVATE_FEATURE_ARBFP = 1L<<24
+  COGL_PRIVATE_FEATURE_ARBFP = 1L<<24,
+
+  /* If this is set then the winsys is responsible for queueing dirty
+   * events. Otherwise a dirty event will be queued when the onscreen
+   * is first allocated or when it is shown or resized */
+  COGL_PRIVATE_FEATURE_DIRTY_EVENTS = 1L<<25
 } CoglPrivateFeatureFlags;
 
 /* Sometimes when evaluating pipelines, either during comparisons or
diff --git a/cogl/winsys/cogl-winsys-egl-wayland.c b/cogl/winsys/cogl-winsys-egl-wayland.c
index 8735a8c..515a13e 100644
--- a/cogl/winsys/cogl-winsys-egl-wayland.c
+++ b/cogl/winsys/cogl-winsys-egl-wayland.c
@@ -321,6 +321,16 @@ _cogl_winsys_egl_context_init (CoglContext *context,
                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
                   TRUE);
 
+  /* We'll manually handle queueing dirty events when the surface is
+   * first shown or when it is resized. Note that this is slightly
+   * different from the emulated behaviour that CoglFramebuffer would
+   * provide if we didn't set this flag because we want to emit the
+   * event on show instead of on allocation. The Wayland protocol
+   * delays setting the surface type until the next buffer is attached
+   * so attaching a buffer before setting the type would not cause
+   * anything to be displayed */
+  context->private_feature_flags |= COGL_PRIVATE_FEATURE_DIRTY_EVENTS;
+
   return TRUE;
 }
 
@@ -433,6 +443,8 @@ flush_pending_resize (CoglOnscreen *onscreen)
                                             wayland_onscreen->pending_width,
                                             wayland_onscreen->pending_height);
 
+      _cogl_onscreen_queue_full_dirty (onscreen);
+
       wayland_onscreen->pending_dx = 0;
       wayland_onscreen->pending_dy = 0;
       wayland_onscreen->has_pending = FALSE;
@@ -482,6 +494,7 @@ _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen,
     {
       wl_shell_surface_set_toplevel (wayland_onscreen->wayland_shell_surface);
       wayland_onscreen->shell_surface_type_set = TRUE;
+      _cogl_onscreen_queue_full_dirty (onscreen);
     }
 
   /* FIXME: We should also do something here to hide the surface when
diff --git a/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/winsys/cogl-winsys-egl-x11.c
index 5bc1d9f..24e45bf 100644
--- a/cogl/winsys/cogl-winsys-egl-x11.c
+++ b/cogl/winsys/cogl-winsys-egl-x11.c
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -45,7 +45,7 @@
 #include "cogl-error-private.h"
 #include "cogl-poll-private.h"
 
-#define COGL_ONSCREEN_X11_EVENT_MASK StructureNotifyMask
+#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask)
 
 static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable;
 
@@ -173,6 +173,23 @@ event_filter_cb (XEvent *xevent, void *data)
                      xevent->xconfigure.width,
                      xevent->xconfigure.height);
     }
+  else if (xevent->type == Expose)
+    {
+      CoglOnscreen *onscreen =
+        find_onscreen_for_xid (context, xevent->xexpose.window);
+
+      if (onscreen)
+        {
+          CoglOnscreenDirtyInfo info;
+
+          info.x = xevent->xexpose.x;
+          info.y = xevent->xexpose.y;
+          info.width = xevent->xexpose.width;
+          info.height = xevent->xexpose.height;
+
+          _cogl_onscreen_queue_dirty (onscreen, &info);
+        }
+    }
 
   return COGL_FILTER_CONTINUE;
 }
@@ -302,6 +319,10 @@ _cogl_winsys_egl_context_init (CoglContext *context,
                   COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN,
                   TRUE);
 
+  /* We'll manually handle queueing dirty events in response to
+   * Expose events from X */
+  context->private_feature_flags |= COGL_PRIVATE_FEATURE_DIRTY_EVENTS;
+
   return TRUE;
 }
 
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index d800cdd..5d436ee 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -64,7 +64,7 @@
 #include <GL/glx.h>
 #include <X11/Xlib.h>
 
-#define COGL_ONSCREEN_X11_EVENT_MASK StructureNotifyMask
+#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask)
 #define MAX_GLX_CONFIG_ATTRIBS 30
 
 typedef struct _CoglContextGLX
@@ -555,6 +555,26 @@ glx_event_filter_cb (XEvent *xevent, void *data)
     }
 #endif /* GLX_INTEL_swap_event */
 
+  if (xevent->type == Expose)
+    {
+      CoglOnscreen *onscreen =
+        find_onscreen_for_xid (context, xevent->xexpose.window);
+
+      if (onscreen)
+        {
+          CoglOnscreenDirtyInfo info;
+
+          info.x = xevent->xexpose.x;
+          info.y = xevent->xexpose.y;
+          info.width = xevent->xexpose.width;
+          info.height = xevent->xexpose.height;
+
+          _cogl_onscreen_queue_dirty (onscreen, &info);
+        }
+
+      return COGL_FILTER_CONTINUE;
+    }
+
   return COGL_FILTER_CONTINUE;
 }
 
@@ -824,6 +844,10 @@ update_winsys_features (CoglContext *context, CoglError **error)
                       TRUE);
     }
 
+  /* We'll manually handle queueing dirty events in response to
+   * Expose events from X */
+  context->private_feature_flags |= COGL_PRIVATE_FEATURE_DIRTY_EVENTS;
+
   return TRUE;
 }
 
diff --git a/cogl/winsys/cogl-winsys-sdl.c b/cogl/winsys/cogl-winsys-sdl.c
index ed8212e..14a55c8 100644
--- a/cogl/winsys/cogl-winsys-sdl.c
+++ b/cogl/winsys/cogl-winsys-sdl.c
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2011 Intel Corporation.
+ * Copyright (C) 2011,2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -230,25 +230,27 @@ flush_pending_resize_notification_idle (void *user_data)
 static CoglFilterReturn
 sdl_event_filter_cb (SDL_Event *event, void *data)
 {
+  CoglContext *context = data;
+  CoglDisplay *display = context->display;
+  CoglDisplaySdl *sdl_display = display->winsys;
+  CoglFramebuffer *framebuffer;
+
+  if (!sdl_display->onscreen)
+    return COGL_FILTER_CONTINUE;
+
+  framebuffer = COGL_FRAMEBUFFER (sdl_display->onscreen);
+
   if (event->type == SDL_VIDEORESIZE)
     {
-      CoglContext *context = data;
-      CoglDisplay *display = context->display;
-      CoglDisplaySdl *sdl_display = display->winsys;
       CoglRenderer *renderer = display->renderer;
       CoglRendererSdl *sdl_renderer = renderer->winsys;
       float width = event->resize.w;
       float height = event->resize.h;
-      CoglFramebuffer *framebuffer;
-
-      if (!sdl_display->onscreen)
-        return COGL_FILTER_CONTINUE;
 
       sdl_display->surface = SDL_SetVideoMode (width, height,
                                                0, /* bitsperpixel */
                                                sdl_display->video_mode_flags);
 
-      framebuffer = COGL_FRAMEBUFFER (sdl_display->onscreen);
       _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
 
       /* We only want to notify that a resize happened when the
@@ -265,6 +267,19 @@ sdl_event_filter_cb (SDL_Event *event, void *data)
 
       return COGL_FILTER_CONTINUE;
     }
+  else if (event->type == SDL_VIDEOEXPOSE)
+    {
+      CoglOnscreenDirtyInfo info;
+
+      /* Sadly SDL doesn't seem to report the rectangle of the expose
+       * event so we'll just queue the whole window */
+      info.x = 0;
+      info.y = 0;
+      info.width = framebuffer->width;
+      info.height = framebuffer->height;
+
+      _cogl_onscreen_queue_dirty (COGL_ONSCREEN (framebuffer), &info);
+    }
 
   return COGL_FILTER_CONTINUE;
 }
@@ -282,6 +297,10 @@ _cogl_winsys_context_init (CoglContext *context, CoglError **error)
                                     (CoglNativeFilterFunc)sdl_event_filter_cb,
                                     context);
 
+  /* We'll manually handle queueing dirty events in response to
+   * SDL_VIDEOEXPOSE events */
+  context->private_feature_flags |= COGL_PRIVATE_FEATURE_DIRTY_EVENTS;
+
   return _cogl_context_update_features (context, error);
 }
 
diff --git a/cogl/winsys/cogl-winsys-sdl2.c b/cogl/winsys/cogl-winsys-sdl2.c
index 5595f46..58666cb 100644
--- a/cogl/winsys/cogl-winsys-sdl2.c
+++ b/cogl/winsys/cogl-winsys-sdl2.c
@@ -3,7 +3,7 @@
  *
  * An object oriented GL/GLES Abstraction/Utility Layer
  *
- * Copyright (C) 2011, 2012 Intel Corporation.
+ * Copyright (C) 2011, 2012, 2013 Intel Corporation.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -303,30 +303,30 @@ flush_pending_resize_notifications_idle (void *user_data)
 }
 
 static CoglFilterReturn
-sdl_event_filter_cb (SDL_Event *event, void *data)
+sdl_window_event_filter (SDL_WindowEvent *event,
+                         CoglContext *context)
 {
-  if (event->type == SDL_WINDOWEVENT &&
-      event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+  SDL_Window *window;
+  CoglFramebuffer *framebuffer;
+
+  window = SDL_GetWindowFromID (event->windowID);
+
+  if (window == NULL)
+    return COGL_FILTER_CONTINUE;
+
+  framebuffer = SDL_GetWindowData (window, COGL_SDL_WINDOW_DATA_KEY);
+
+  if (framebuffer == NULL || framebuffer->context != context)
+    return COGL_FILTER_CONTINUE;
+
+  if (event->event == SDL_WINDOWEVENT_SIZE_CHANGED)
     {
-      CoglContext *context = data;
       CoglDisplay *display = context->display;
       CoglRenderer *renderer = display->renderer;
       CoglRendererSdl2 *sdl_renderer = renderer->winsys;
-      float width = event->window.data1;
-      float height = event->window.data2;
-      CoglFramebuffer *framebuffer;
+      float width = event->data1;
+      float height = event->data2;
       CoglOnscreenSdl2 *sdl_onscreen;
-      SDL_Window *window;
-
-      window = SDL_GetWindowFromID (event->window.windowID);
-
-      if (window == NULL)
-        return COGL_FILTER_CONTINUE;
-
-      framebuffer = SDL_GetWindowData (window, COGL_SDL_WINDOW_DATA_KEY);
-
-      if (framebuffer == NULL || framebuffer->context != context)
-        return COGL_FILTER_CONTINUE;
 
       _cogl_framebuffer_winsys_update_size (framebuffer, width, height);
 
@@ -344,13 +344,39 @@ sdl_event_filter_cb (SDL_Event *event, void *data)
 
       sdl_onscreen = COGL_ONSCREEN (framebuffer)->winsys;
       sdl_onscreen->pending_resize_notify = TRUE;
+    }
+  else if (event->event == SDL_WINDOWEVENT_EXPOSED)
+    {
+      CoglOnscreenDirtyInfo info;
 
-      return COGL_FILTER_CONTINUE;
+      /* Sadly SDL doesn't seem to report the rectangle of the expose
+       * event so we'll just queue the whole window */
+      info.x = 0;
+      info.y = 0;
+      info.width = framebuffer->width;
+      info.height = framebuffer->height;
+
+      _cogl_onscreen_queue_dirty (COGL_ONSCREEN (framebuffer), &info);
     }
 
   return COGL_FILTER_CONTINUE;
 }
 
+static CoglFilterReturn
+sdl_event_filter_cb (SDL_Event *event, void *data)
+{
+  CoglContext *context = data;
+
+  switch (event->type)
+    {
+    case SDL_WINDOWEVENT:
+      return sdl_window_event_filter (&event->window, context);
+
+    default:
+      return COGL_FILTER_CONTINUE;
+    }
+}
+
 static CoglBool
 _cogl_winsys_context_init (CoglContext *context, CoglError **error)
 {
@@ -370,6 +396,10 @@ _cogl_winsys_context_init (CoglContext *context, CoglError **error)
                     COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE,
                     TRUE);
 
+  /* We'll manually handle queueing dirty events in response to
+   * SDL_WINDOWEVENT_EXPOSED events */
+  context->private_feature_flags |= COGL_PRIVATE_FEATURE_DIRTY_EVENTS;
+
   _cogl_renderer_add_native_filter (renderer,
                                     (CoglNativeFilterFunc) sdl_event_filter_cb,
                                     context);
diff --git a/cogl/winsys/cogl-winsys-wgl.c b/cogl/winsys/cogl-winsys-wgl.c
index ecaf346..1e7fe4c 100644
--- a/cogl/winsys/cogl-winsys-wgl.c
+++ b/cogl/winsys/cogl-winsys-wgl.c
@@ -235,6 +235,30 @@ win32_event_filter_cb (MSG *msg, void *data)
             }
         }
     }
+  else if (msg->message == WM_PAINT)
+    {
+      CoglOnscreen *onscreen =
+        find_onscreen_for_hwnd (context, msg->hwnd);
+      RECT rect;
+
+      if (onscreen && GetUpdateRect (msg->hwnd, &rect, FALSE))
+        {
+          CoglOnscreenDirtyInfo info;
+
+          /* Apparently this removes the dirty region from the window
+           * so that it won't be included in the next WM_PAINT
+           * message. This is also what SDL does to emit dirty
+           * events */
+          ValidateRect (msg->hwnd, &rect);
+
+          info.x = rect.left;
+          info.y = rect.top;
+          info.width = rect.right - rect.left;
+          info.height = rect.bottom - rect.top;
+
+          _cogl_onscreen_queue_dirty (onscreen, &info);
+        }
+    }
 
   return COGL_FILTER_CONTINUE;
 }
@@ -680,6 +704,10 @@ update_winsys_features (CoglContext *context, CoglError **error)
       g_strfreev (split_extensions);
     }
 
+  /* We'll manually handle queueing dirty events in response to
+   * WM_PAINT messages */
+  context->private_feature_flags |= COGL_PRIVATE_FEATURE_DIRTY_EVENTS;
+
   return TRUE;
 }
 
diff --git a/doc/reference/cogl2/cogl2-sections.txt b/doc/reference/cogl2/cogl2-sections.txt
index 59f40bd..7b00c04 100644
--- a/doc/reference/cogl2/cogl2-sections.txt
+++ b/doc/reference/cogl2/cogl2-sections.txt
@@ -556,6 +556,13 @@ cogl_onscreen_add_frame_callback
 cogl_onscreen_remove_frame_callback
 
 <SUBSECTION>
+CoglOnscreenDirtyInfo
+CoglOnscreenDirtyCallback
+CoglOnscreenDirtyClosure
+cogl_onscreen_add_dirty_callback
+cogl_onscreen_remove_dirty_callback
+
+<SUBSECTION>
 CoglOnscreenResizeCallback
 CoglOnscreenResizeClosure
 cogl_onscreen_add_resize_callback
-- 
1.7.11.3.g3c3efa5



More information about the Cogl mailing list