summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Bragg <robert@sixbynine.org>2012-02-12 11:30:06 +0000
committerRobert Bragg <robert@sixbynine.org>2012-02-29 20:04:13 +0000
commit2351ad4c7b7c9a6c0bc250edabee89bf22ad2584 (patch)
tree0ffcb23ac99c6825dd22ab941748d366a4848de8
parent236e85a2453714378b4a5727a793d9a072acb504 (diff)
egl: Implement EGL_EXT_buffer_age extensionwip/egl-extensions
This provides an implementation of the EGL_EXT_buffer_age draft extension which allows applications to reliably determine how back buffers are being re-used so they can safely perform incremental updates of the buffer's contents without needing to use a blit to present. Signed-off-by: Robert Bragg <robert@sixbynine.org>
-rw-r--r--docs/EXT_buffer_age.spec172
-rw-r--r--include/EGL/eglext.h5
-rw-r--r--src/egl/drivers/dri2/egl_dri2.h1
-rw-r--r--src/egl/drivers/dri2/platform_wayland.c71
-rw-r--r--src/egl/main/egldisplay.h1
-rw-r--r--src/egl/main/eglmisc.c1
6 files changed, 233 insertions, 18 deletions
diff --git a/docs/EXT_buffer_age.spec b/docs/EXT_buffer_age.spec
new file mode 100644
index 0000000000..8da2538717
--- /dev/null
+++ b/docs/EXT_buffer_age.spec
@@ -0,0 +1,172 @@
+EGL_EXT_buffer_age
+
+ EXT_buffer_age
+
+Name Strings
+
+ EGL_EXT_buffer_age
+
+Notice
+
+ Copyright 2011,2012 Intel Cooperation. All rights reserved.
+
+Contributors
+
+ Robert Bragg
+ Neil Roberts
+ Tapani Pälli
+ Kristian Høgsberg
+
+Contacts
+
+ Robert Bragg, Intel (robert.bragg 'at' intel.com)
+
+Status
+
+ Draft
+
+Version
+
+ 5 - Feb 12, 2012
+
+Number
+
+ TBD
+
+Dependencies
+
+ Requires EGL 1.4
+
+ This extension is written against the wording of the EGL 1.4
+ Specification.
+
+ This extension refers to the EGL_EXT_start_frame extension
+
+Overview
+
+ The aim of this extension is to expose enough information to
+ applications about how the driver manages the set of front and
+ back buffers associated with a given surface to allow applications
+ to re-use the contents of old frames and minimize how much must be
+ redrawn for the next frame.
+
+ There are lots of different ways for a driver to manage these
+ buffers, from double buffering, different styles of triple
+ buffering and even n-buffering or simply single buffer rendering.
+ We also need to consider that power management events or memory
+ pressure events might also result in some of the buffers not
+ currently in-use being freed.
+
+ This extension lets you query the age of the back buffer contents
+ for an EGL surface as the number of frames elapsed since the
+ contents were originally defined. The back buffer can either be
+ reported as invalid (has an age of 0) or it may be reported to
+ contain the contents from n frames prior to this new frame.
+
+ For many use-cases this extension can provide an efficient
+ alternative to using the EGL_BUFFER_PRESERVED swap behaviour. The
+ EGL_BUFFER_PRESERVED swap behaviour adds a direct dependency for
+ any frame n on frame n - 1 which can affect the pipelining of
+ multiple frames but also implies a costly copy-back of data to
+ initialize the back-buffer at the start of each frame.
+
+ For example if you consider a double buffered application drawing
+ a small spinning icon, but everything else in the scene is static.
+ If we know that 2 buffers are continuously being recycled each
+ time eglSwapBuffers is called then even though 100s of frames may
+ need to be drawn to animate the icon it can be seen that the two
+ buffers are remaining unchanged except within the bounds of the
+ icon. In this scenario ideally the application would simply
+ perform an incremental update of the old buffer instead of
+ redundantly redrawing all the static parts of the scene. The
+ problem up until now though has been that EGL doesn't report how
+ buffers may be recycled so it wasn't safe for applications to try
+ and reuse their contents. Now applications can keep track of all
+ the regions that have changed over the last n frames and by
+ knowing the age of the buffer they know how to efficiently repair
+ buffers that are re-cycled instead of redrawing the entire scene.
+
+New Procedures and Functions
+
+ None
+
+New Tokens
+
+ EGL_BUFFER_AGE_EXT TBD
+
+Additions to Section 3.5 of the EGL 1.4 Specification (Rendering Surfaces)
+
+ Add the following to the table of "Queryable surface attributes
+ and types":
+
+ +----------------------+---------+-----------------------------+
+ | Attribute | Type | Description |
+ +----------------------+---------+-----------------------------+
+ | EGL_BUFFER_AGE_EXT | Integer | Age of back-buffer contents |
+ +----------------------+---------+-----------------------------+
+ Table aaa: Queryable surface attributes and types.
+
+
+ Add the following text to the subsection titled "Surface
+ Attributes" in the description for eglQuerySurface
+
+ Querying EGL_BUFFER_AGE_EXT returns the age of the
+ color contents of the current back-buffer as the number of
+ frames elapsed since it was defined. For example with a single
+ buffered surface the age would usually be 1. With a double
+ buffered surface the age would usually be 2. An age of 0 means
+ that the contents have only just been initialized and the
+ contents are undefined.
+ Since this attribute depends on the progression of frames
+ and for it to really be useful it should be queried at the
+ start of a frame before rendering anything, applications
+ should usually call eglStartFrameEGL() to explicitly start a
+ new frame before querying the back-buffer age.
+ If the EGL driver decides to free un-used back-buffers
+ when the system is under memory pressure or in response to
+ power-management events then EGL will return an age of 0 when
+ it allocates a new buffer at the start of a new frame.
+ Applications can use this age to safely rely on the
+ contents of old back-buffers to potentially reduce the amount
+ of redrawing they do each frame.
+ If the EGL_BUFFER_PRESERVED swap behaviour is in use then
+ it can be assumed that the age will always be 1. It is
+ recommended where possible though that the
+ EGL_BUFFER_PRESERVED swap behaviour not be used since it can
+ have severe performance consequences. Keeping track of the
+ buffer age instead and the regions that have changed over the
+ last 2 or 3 frames can often replace the need for using
+ EGL_BUFFER_PRESERVED without adding a dependency for each
+ frame on the previous frame which is what usually make
+ EGL_BUFFER_PRESERVED in-efficient.
+
+Dependencies on OpenGL ES
+
+ None
+
+Dependencies on OpenVG
+
+ None
+
+Issues
+
+ What are the semantics if EGL_BUFFER_PRESERVED is in use
+
+ RESOLVED: The age will always be 1 in this case. More
+ clarification about this was added along with the recommendation
+ to use the buffer age to reuse buffers instead of
+ EGL_BUFFER_PRESERVED when possible to avoid the in-efficiencies of
+ introducing a dependency for each frame on the previous frame.
+
+Revision History
+
+ Version 1, 25/07/2011
+ - First draft
+ Version 2, 03/08/2011
+ - Clarified semantics for using EGL_BUFFER_PRESERVED
+ Version 3, 01/09/2011
+ - Fixed a prototype inconsistency
+ Version 3, 03/11/2011
+ - Split out the buffer age parts from EGL_INTEL_start_frame
+ Version 4, 12/02/2012
+ - Rename to EGL_EXT_buffer_age
diff --git a/include/EGL/eglext.h b/include/EGL/eglext.h
index 0d8a732e79..bea789842f 100644
--- a/include/EGL/eglext.h
+++ b/include/EGL/eglext.h
@@ -344,6 +344,11 @@ EGLAPI EGLBoolean EGLAPIENTRY eglStartFrameEXT(EGLDisplay dpy, EGLSurface surfac
typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTARTFRAMEEXTPROC) (EGLDisplay dpy, EGLSurface surface);
#endif /* EGL_EXT_start_frame */
+#ifndef EGL_EXT_buffer_age
+#define EGL_EXT_buffer_age 1
+#define EGL_BUFFER_AGE_EXT 0x31D6
+#endif /* EGL_EXT_buffer_age */
+
#include <EGL/eglmesaext.h>
#ifdef __cplusplus
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index 84ea0b694c..da3299bfa3 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -168,6 +168,7 @@ struct dri2_egl_surface
struct wl_egl_pixmap *wl_pix;
struct wl_buffer *wl_drm_buffer[WL_BUFFER_COUNT];
int wl_buffer_lock[WL_BUFFER_COUNT];
+ int buffer_age[WL_BUFFER_COUNT];
int dx;
int dy;
__DRIbuffer *dri_buffers[__DRI_BUFFER_COUNT];
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index e96c2486a0..f20293fea4 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -90,13 +90,14 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
_eglError(EGL_BAD_ALLOC, "dri2_create_surface");
return NULL;
}
-
+
if (!_eglInitSurface(&dri2_surf->base, disp, type, conf, attrib_list))
goto cleanup_surf;
for (i = 0; i < WL_BUFFER_COUNT; ++i) {
dri2_surf->wl_drm_buffer[i] = NULL;
dri2_surf->wl_buffer_lock[i] = 0;
+ dri2_surf->buffer_age[i] = 0;
}
for (i = 0; i < __DRI_BUFFER_COUNT; ++i)
@@ -129,14 +130,14 @@ dri2_create_surface(_EGLDriver *drv, _EGLDisplay *disp, EGLint type,
dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] = dri2_buf->dri_buffer;
}
break;
- default:
+ default:
goto cleanup_surf;
}
- dri2_surf->dri_drawable =
+ dri2_surf->dri_drawable =
(*dri2_dpy->dri2->createNewDrawable) (dri2_dpy->dri_screen,
type == EGL_WINDOW_BIT ?
- dri2_conf->dri_double_config :
+ dri2_conf->dri_double_config :
dri2_conf->dri_single_config,
dri2_surf);
if (dri2_surf->dri_drawable == NULL) {
@@ -175,6 +176,21 @@ dri2_create_pixmap_surface(_EGLDriver *drv, _EGLDisplay *disp,
pixmap, attrib_list);
}
+static EGLBoolean
+dri2_query_surface(_EGLDriver *drv, _EGLDisplay *dpy, _EGLSurface *surface,
+ EGLint attribute, EGLint *value)
+{
+ struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
+
+ switch (attribute) {
+ case EGL_BUFFER_AGE_EXT:
+ *value = dri2_surf->buffer_age[WL_BUFFER_BACK];
+ return EGL_TRUE;
+ default:
+ return _eglQuerySurface (drv, dpy, surface, attribute, value);
+ }
+}
+
/**
* Called via eglDestroySurface(), drv->API.DestroySurface().
*/
@@ -223,7 +239,7 @@ dri2_wl_egl_pixmap_destroy(struct wl_egl_pixmap *egl_pixmap)
dri2_buf->dri_buffer);
free(dri2_buf);
-
+
egl_pixmap->driver_private = NULL;
egl_pixmap->destroy = NULL;
}
@@ -257,7 +273,7 @@ dri2_process_back_buffer(struct dri2_egl_surface *dri2_surf, unsigned format)
/* allocate a front buffer for our double-buffered window*/
if (dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] != NULL)
break;
- dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] =
+ dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT] =
dri2_dpy->dri2->allocateBuffer(dri2_dpy->dri_screen,
__DRI_BUFFER_FRONT_LEFT, format,
dri2_surf->base.Width, dri2_surf->base.Height);
@@ -302,7 +318,7 @@ dri2_release_pending_buffer(void *data,
/* FIXME: print internal error */
if (!dri2_surf->pending_buffer)
return;
-
+
dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
dri2_surf->pending_buffer);
dri2_surf->pending_buffer = NULL;
@@ -326,6 +342,7 @@ dri2_release_buffers(struct dri2_egl_surface *dri2_surf)
dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
dri2_surf->third_buffer);
dri2_surf->third_buffer = NULL;
+ dri2_surf->buffer_age[WL_BUFFER_THIRD] = 0;
}
for (i = 0; i < __DRI_BUFFER_COUNT; ++i) {
@@ -338,7 +355,11 @@ dri2_release_buffers(struct dri2_egl_surface *dri2_surf)
callback = wl_display_sync(dri2_dpy->wl_dpy);
wl_callback_add_listener(callback,
&release_buffer_listener, dri2_surf);
+ dri2_surf->buffer_age[WL_BUFFER_FRONT] = 0;
break;
+ case __DRI_BUFFER_BACK_LEFT:
+ dri2_surf->buffer_age[WL_BUFFER_BACK] = 0;
+ /* fall through */
default:
dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
dri2_surf->dri_buffers[i]);
@@ -357,6 +378,14 @@ pointer_swap(const void **p1, const void **p2)
*p2 = tmp;
}
+static inline void
+int_swap(int *i1, int *i2)
+{
+ int tmp = *i1;
+ *i1 = *i2;
+ *i2 = tmp;
+}
+
static void
destroy_third_buffer(struct dri2_egl_surface *dri2_surf)
{
@@ -369,6 +398,7 @@ destroy_third_buffer(struct dri2_egl_surface *dri2_surf)
dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
dri2_surf->third_buffer);
dri2_surf->third_buffer = NULL;
+ dri2_surf->buffer_age[WL_BUFFER_THIRD] = 0;
if (dri2_surf->wl_drm_buffer[WL_BUFFER_THIRD])
wl_buffer_destroy(dri2_surf->wl_drm_buffer[WL_BUFFER_THIRD]);
@@ -380,12 +410,9 @@ static void
swap_wl_buffers(struct dri2_egl_surface *dri2_surf,
enum wayland_buffer_type a, enum wayland_buffer_type b)
{
- int tmp;
+ int_swap(&dri2_surf->wl_buffer_lock[a], &dri2_surf->wl_buffer_lock[b]);
+ int_swap(&dri2_surf->buffer_age[a], &dri2_surf->buffer_age[b]);
- tmp = dri2_surf->wl_buffer_lock[a];
- dri2_surf->wl_buffer_lock[a] = dri2_surf->wl_buffer_lock[b];
- dri2_surf->wl_buffer_lock[b] = tmp;
-
pointer_swap((const void **) &dri2_surf->wl_drm_buffer[a],
(const void **) &dri2_surf->wl_drm_buffer[b]);
}
@@ -431,7 +458,7 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
int i;
if (dri2_surf->base.Type == EGL_WINDOW_BIT &&
- (dri2_surf->base.Width != dri2_surf->wl_win->width ||
+ (dri2_surf->base.Width != dri2_surf->wl_win->width ||
dri2_surf->base.Height != dri2_surf->wl_win->height)) {
dri2_release_buffers(dri2_surf);
@@ -463,7 +490,7 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
attachments[i], attachments[i+1],
dri2_surf->base.Width, dri2_surf->base.Height);
- if (!dri2_surf->dri_buffers[attachments[i]])
+ if (!dri2_surf->dri_buffers[attachments[i]])
continue;
if (attachments[i] == __DRI_BUFFER_FRONT_LEFT)
@@ -591,9 +618,9 @@ dri2_swap_buffers_with_damage(_EGLDriver *drv,
(const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT],
(const void **) &dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]);
- dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT]->attachment =
+ dri2_surf->dri_buffers[__DRI_BUFFER_FRONT_LEFT]->attachment =
__DRI_BUFFER_FRONT_LEFT;
- dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]->attachment =
+ dri2_surf->dri_buffers[__DRI_BUFFER_BACK_LEFT]->attachment =
__DRI_BUFFER_BACK_LEFT;
swap_wl_buffers(dri2_surf, WL_BUFFER_FRONT, WL_BUFFER_BACK);
@@ -621,6 +648,11 @@ dri2_swap_buffers_with_damage(_EGLDriver *drv,
wl_surface_damage(dri2_surf->wl_win->surface, rect[0], rect[1],
rect[2], rect[3]);
}
+
+ for (i = 0; i < WL_BUFFER_COUNT; i++)
+ if (dri2_surf->buffer_age[i] != 0)
+ dri2_surf->buffer_age[i]++;
+ dri2_surf->buffer_age[WL_BUFFER_FRONT] = 1;
}
_EGLContext *ctx;
@@ -818,6 +850,7 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
drv->API.CreateWindowSurface = dri2_create_window_surface;
drv->API.CreatePixmapSurface = dri2_create_pixmap_surface;
+ drv->API.QuerySurface = dri2_query_surface;
drv->API.DestroySurface = dri2_destroy_surface;
drv->API.SwapBuffers = dri2_swap_buffers;
drv->API.SwapBuffersWithDamageEXT = dri2_swap_buffers_with_damage;
@@ -873,7 +906,7 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
dri2_dpy->dri2_loader_extension.flushFrontBuffer = dri2_flush_front_buffer;
dri2_dpy->dri2_loader_extension.getBuffersWithFormat =
dri2_get_buffers_with_format;
-
+
dri2_dpy->extensions[0] = &dri2_dpy->dri2_loader_extension.base;
dri2_dpy->extensions[1] = &image_lookup_extension.base;
dri2_dpy->extensions[2] = &use_invalidate.base;
@@ -898,6 +931,8 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
disp->Extensions.EXT_swap_buffers_with_damage = EGL_TRUE;
+ disp->Extensions.EXT_buffer_age = EGL_TRUE;
+
/* we're supporting EGL 1.4 */
disp->VersionMajor = 1;
disp->VersionMinor = 4;
@@ -915,6 +950,6 @@ dri2_initialize_wayland(_EGLDriver *drv, _EGLDisplay *disp)
wl_drm_destroy(dri2_dpy->wl_drm);
cleanup_dpy:
free(dri2_dpy);
-
+
return EGL_FALSE;
}
diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
index 4fd1ed8738..c975382b12 100644
--- a/src/egl/main/egldisplay.h
+++ b/src/egl/main/egldisplay.h
@@ -116,6 +116,7 @@ struct _egl_extensions
EGLBoolean EXT_swap_buffers_with_damage;
EGLBoolean EXT_start_frame;
+ EGLBoolean EXT_buffer_age;
};
diff --git a/src/egl/main/eglmisc.c b/src/egl/main/eglmisc.c
index 2e33ae0e69..d3c2123ca8 100644
--- a/src/egl/main/eglmisc.c
+++ b/src/egl/main/eglmisc.c
@@ -120,6 +120,7 @@ _eglUpdateExtensionsString(_EGLDisplay *dpy)
_EGL_CHECK_EXTENSION(EXT_swap_buffers_with_damage);
_EGL_CHECK_EXTENSION(EXT_start_frame);
+ _EGL_CHECK_EXTENSION(EXT_buffer_age);
#undef _EGL_CHECK_EXTENSION
}