diff options
author | Robert Bragg <robert@sixbynine.org> | 2012-02-12 11:30:06 +0000 |
---|---|---|
committer | Robert Bragg <robert@sixbynine.org> | 2012-02-29 20:04:13 +0000 |
commit | 2351ad4c7b7c9a6c0bc250edabee89bf22ad2584 (patch) | |
tree | 0ffcb23ac99c6825dd22ab941748d366a4848de8 | |
parent | 236e85a2453714378b4a5727a793d9a072acb504 (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.spec | 172 | ||||
-rw-r--r-- | include/EGL/eglext.h | 5 | ||||
-rw-r--r-- | src/egl/drivers/dri2/egl_dri2.h | 1 | ||||
-rw-r--r-- | src/egl/drivers/dri2/platform_wayland.c | 71 | ||||
-rw-r--r-- | src/egl/main/egldisplay.h | 1 | ||||
-rw-r--r-- | src/egl/main/eglmisc.c | 1 |
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 } |