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);