diff --git a/cogl/cogl-glx-renderer-private.h b/cogl/cogl-glx-renderer-private.h
index 65fa8d950..b70a78ea7 100644
--- a/cogl/cogl-glx-renderer-private.h
+++ b/cogl/cogl-glx-renderer-private.h
@@ -69,6 +69,10 @@ typedef struct _CoglGLXRenderer
void *
(* glXGetProcAddress) (const GLubyte *procName);
+ int
+ (* glXQueryDrawable) (Display *dpy, GLXDrawable drawable,
+ int attribute, unsigned int *value);
+
/* Function pointers for GLX specific extensions */
#define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d, e, f, g)
diff --git a/cogl/cogl-onscreen.c b/cogl/cogl-onscreen.c
index 3c1e148de..1e2243081 100644
--- a/cogl/cogl-onscreen.c
+++ b/cogl/cogl-onscreen.c
@@ -188,6 +188,22 @@ cogl_onscreen_swap_region (CoglOnscreen *onscreen,
COGL_BUFFER_BIT_STENCIL);
}
+int
+cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ const CoglWinsysVtable *winsys;
+
+ _COGL_RETURN_VAL_IF_FAIL (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN, 0);
+
+ winsys = _cogl_framebuffer_get_winsys (framebuffer);
+
+ if (!winsys->onscreen_get_buffer_age)
+ return 0;
+
+ return winsys->onscreen_get_buffer_age (onscreen);
+}
+
#ifdef COGL_HAS_X11_SUPPORT
void
cogl_x11_onscreen_set_foreign_window_xid (CoglOnscreen *onscreen,
diff --git a/cogl/cogl-onscreen.h b/cogl/cogl-onscreen.h
index 726052eb9..5ae97e234 100644
--- a/cogl/cogl-onscreen.h
+++ b/cogl/cogl-onscreen.h
@@ -307,6 +307,54 @@ cogl_onscreen_hide (CoglOnscreen *onscreen);
void
cogl_onscreen_swap_buffers (CoglOnscreen *onscreen);
+
+/**
+ * cogl_onscreen_get_buffer_age:
+ * @onscreen: A #CoglOnscreen framebuffer
+ *
+ * Gets the current age of the buffer contents.
+ *
+ * This function allows applications to query the age of the current
+ * back buffer contents for a #CoglOnscreen as the number of frames
+ * elapsed since the contents were most recently defined.
+ *
+ * These age values exposes enough information to applications about
+ * how Cogl internally manages back buffers to allow applications to
+ * re-use the contents of old frames and minimize how much must be
+ * redrawn for the next frame.
+ *
+ * The back buffer contents can either be reported as invalid (has an
+ * age of 0) or it may be reported to be the same contents as from n
+ * frames prior to the current frame.
+ *
+ * The queried value remains valid until the next buffer swap.
+ *
+ * One caveat is that under X11 the buffer age does not reflect
+ * changes to buffer contents caused by the window systems. X11
+ * applications must track Expose events to determine what buffer
+ * regions need to additionally be repaired each frame.
+ *
+ * The recommended way to take advantage of this buffer age api is to
+ * build up a circular buffer of length 3 for tracking damage regions
+ * over the last 3 frames and when starting a new frame look at the
+ * age of the buffer and combine the damage regions for the current
+ * frame with the damage regions of previous @age frames so you know
+ * everything that must be redrawn to update the old contents for the
+ * new frame.
+ *
+ * If the system doesn't not support being able to track the age
+ * of back buffers then this function will always return 0 which
+ * implies that the contents are undefined.
+ *
+ * Return value: The age of the buffer contents or 0 when the buffer
+ * contents are undefined.
+ *
+ * Since: 1.14
+ * Stability: stable
+ */
+int
+cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen);
+
/**
* cogl_onscreen_swap_region:
* @onscreen: A #CoglOnscreen framebuffer
diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h
index 78640e597..f1f281da2 100644
--- a/cogl/cogl-types.h
+++ b/cogl/cogl-types.h
@@ -829,6 +829,9 @@ typedef enum _CoglWinsysFeature
* only needs to be throttled to the framerate */
COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED,
+ /* Avaiable if the age of the back buffer can be queried */
+ COGL_WINSYS_FEATURE_BUFFER_AGE,
+
COGL_WINSYS_FEATURE_N_FEATURES
} CoglWinsysFeature;
diff --git a/cogl/winsys/cogl-winsys-glx-feature-functions.h b/cogl/winsys/cogl-winsys-glx-feature-functions.h
index 44d453217..677ac0ecd 100644
--- a/cogl/winsys/cogl-winsys-glx-feature-functions.h
+++ b/cogl/winsys/cogl-winsys-glx-feature-functions.h
@@ -170,3 +170,11 @@ COGL_WINSYS_FEATURE_FUNCTION (GLXContext, glXCreateContextAttribs,
Bool direct,
const int *attrib_list))
COGL_WINSYS_FEATURE_END ()
+
+COGL_WINSYS_FEATURE_BEGIN (255, 255,
+ buffer_age,
+ "EXT\0",
+ "buffer_age\0",
+ 0,
+ COGL_WINSYS_FEATURE_BUFFER_AGE)
+COGL_WINSYS_FEATURE_END ()
diff --git a/cogl/winsys/cogl-winsys-glx.c b/cogl/winsys/cogl-winsys-glx.c
index 97e4c60b5..22078cc4f 100644
--- a/cogl/winsys/cogl-winsys-glx.c
+++ b/cogl/winsys/cogl-winsys-glx.c
@@ -280,7 +280,9 @@ resolve_core_glx_functions (CoglRenderer *renderer,
(!g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddress",
(void **) &glx_renderer->glXGetProcAddress) &&
!g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddressARB",
- (void **) &glx_renderer->glXGetProcAddress)))
+ (void **) &glx_renderer->glXGetProcAddress)) ||
+ !g_module_symbol (glx_renderer->libgl_module, "glXQueryDrawable",
+ (void **) &glx_renderer->glXQueryDrawable))
{
_cogl_set_error (error, COGL_WINSYS_ERROR,
COGL_WINSYS_ERROR_INIT,
@@ -1189,6 +1191,30 @@ _cogl_winsys_get_vsync_counter (CoglContext *ctx)
return video_sync_count;
}
+#ifndef GLX_BACK_BUFFER_AGE_EXT
+#define GLX_BACK_BUFFER_AGE_EXT 0x20F4
+#endif
+
+static int
+_cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen)
+{
+ CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen);
+ CoglContext *context = framebuffer->context;
+ CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer);
+ CoglGLXRenderer *glx_renderer = context->display->renderer->winsys;
+ CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
+ CoglOnscreenXlib *xlib_onscreen = onscreen->winsys;
+ GLXDrawable drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin;
+ unsigned int age;
+
+ if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE))
+ return 0;
+
+ glx_renderer->glXQueryDrawable (xlib_renderer->xdpy, drawable, GLX_BACK_BUFFER_AGE_EXT, &age);
+
+ return age;
+}
+
static void
_cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen,
const int *user_rectangles,
@@ -2165,6 +2191,7 @@ static CoglWinsysVtable _cogl_winsys_vtable =
.onscreen_bind = _cogl_winsys_onscreen_bind,
.onscreen_swap_buffers = _cogl_winsys_onscreen_swap_buffers,
.onscreen_swap_region = _cogl_winsys_onscreen_swap_region,
+ .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age,
.onscreen_update_swap_throttled =
_cogl_winsys_onscreen_update_swap_throttled,
.onscreen_x11_get_window_xid =
diff --git a/cogl/winsys/cogl-winsys-private.h b/cogl/winsys/cogl-winsys-private.h
index 80686ef5f..dafbada8a 100644
--- a/cogl/winsys/cogl-winsys-private.h
+++ b/cogl/winsys/cogl-winsys-private.h
@@ -127,6 +127,9 @@ typedef struct _CoglWinsysVtable
void
(*onscreen_set_resizable) (CoglOnscreen *onscreen, CoglBool resizable);
+ int
+ (*onscreen_get_buffer_age) (CoglOnscreen *onscreen);
+
#ifdef COGL_HAS_EGL_SUPPORT
EGLDisplay
(*context_egl_get_egl_display) (CoglContext *context);