From d0944b8fbd332083e02163a4bf14a3b338af0e54 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 14 May 2013 13:39:48 +0100 Subject: [PATCH] Add a callback to get dirty events from a CoglOnscreen 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. Reviewed-by: Robert Bragg (cherry picked from commit 85c5a9ba419b2247bd768284c79ee69164a0c098) Conflicts: cogl/cogl-private.h --- 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 | 8 +- 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 +++ .../cogl-2.0-experimental-sections.txt | 7 + 14 files changed, 458 insertions(+), 93 deletions(-) diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h index 1444cb739..d73928ea3 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 @@ -197,6 +197,7 @@ struct _CoglContext int next_swap_callback_id; 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 a5dde52c4..070f2d907 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 @@ -313,6 +313,7 @@ cogl_context_new (CoglDisplay *display, g_hash_table_new (g_direct_hash, g_direct_equal); 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 d65823503..d9ec88d18 100644 --- a/cogl/cogl-framebuffer.c +++ b/cogl/cogl-framebuffer.c @@ -684,6 +684,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; @@ -701,10 +702,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 a6425fc76..5dcf96770 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 @@ -99,6 +113,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 e0546b128..ce533e711 100644 --- a/cogl/cogl-onscreen.c +++ b/cogl/cogl-onscreen.c @@ -50,6 +50,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); @@ -121,6 +122,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); @@ -138,6 +140,111 @@ _cogl_onscreen_free (CoglOnscreen *onscreen) g_free (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, @@ -153,15 +260,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 @@ -501,50 +600,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) { @@ -580,6 +635,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 @@ -629,6 +688,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 6ce1f5140..168d60c33 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 @@ -814,6 +814,117 @@ void 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 b62be3354..c17e86531 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,11 @@ 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_OES_EGL_SYNC = 1L<<24 + COGL_PRIVATE_FEATURE_OES_EGL_SYNC = 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 50075500e..35a3cc1a1 100644 --- a/cogl/winsys/cogl-winsys-egl-wayland.c +++ b/cogl/winsys/cogl-winsys-egl-wayland.c @@ -325,6 +325,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; } @@ -437,6 +447,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; @@ -486,6 +498,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 bdb375900..ba6f500a6 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; } @@ -303,6 +320,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 849844a8f..8395ed842 100644 --- a/cogl/winsys/cogl-winsys-glx.c +++ b/cogl/winsys/cogl-winsys-glx.c @@ -63,7 +63,7 @@ #include #include -#define COGL_ONSCREEN_X11_EVENT_MASK StructureNotifyMask +#define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask) #define MAX_GLX_CONFIG_ATTRIBS 30 typedef struct _CoglContextGLX @@ -557,6 +557,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; } @@ -835,6 +855,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 8af11ac3c..75b7edd8c 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 @@ -246,25 +246,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 @@ -281,6 +283,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; } @@ -298,6 +313,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 5595f461f..58666cbdd 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 b0b1b9a8f..6e789d77a 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; } @@ -682,6 +706,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/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt index e97ea21a6..be5b99a29 100644 --- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt +++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt @@ -611,6 +611,13 @@ CoglFrameClosure cogl_onscreen_add_frame_callback cogl_onscreen_remove_frame_callback + +CoglOnscreenDirtyInfo +CoglOnscreenDirtyCallback +CoglOnscreenDirtyClosure +cogl_onscreen_add_dirty_callback +cogl_onscreen_remove_dirty_callback + CoglOnscreenResizeCallback CoglOnscreenResizeClosure