Name KHR_partial_update Name Strings EGL_KHR_partial_update Contributors Ray Smith Tom Cooksey James Jones Chad Versace Jesse Hall Contact Ray Smith, ARM (Raymond.Smith 'at' arm.com) IP Status No known claims. Notice Copyright (c) 2014 The Khronos Group Inc. Copyright terms at http://www.khronos.org/registry/speccopyright.html Status Complete. Approved by the EGL Working Group on September 17, 2014. Approved by the Khronos Board of Promoters on November 7, 2014. Version Version 12, September 12, 2014 Number EGL Extension #83 Extension Type EGL display extension Dependencies EGL 1.4 or later is required. Written based on the EGL 1.5 specification (March 12, 2014). The behavior of part of this extension is different depending on whether the EGL_EXT_buffer_age extension is also present. This extension trivially interacts with EGL_KHR_swap_buffers_with_damage and EGL_EXT_swap_buffers_with_damage. This extension is worded against the KHR version, but the interactions with the EXT version are identical. New Procedures and Functions EGLBoolean eglSetDamageRegionKHR(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects); New Tokens Accepted in the parameter of eglQuerySurface: EGL_BUFFER_AGE_KHR 0x313D Overview The aim of this extension is to allow efficient partial updates for postable surfaces. It allows implementations to completely avoid processing areas of the surface which have not changed between frames, allowing increased efficiency. It does so by providing information and guarantees about the content of the current back buffer which allow the application to "repair" only areas that have become out of date since the particular back buffer was last used. The information provided is in the form of the "age" of the buffer, that is, how many frames ago it was last used as the back buffer for the surface. If the application tracks what changes it has made to the surface since this back buffer was last used, it can bring the entire back buffer up to date by only re-rendering the areas it knows to be out of date. Use of this extension provides a more efficient alternative to EGL_BUFFER_PRESERVED swap behaviour. EGL_BUFFER_PRESERVED typically implies an expensive full-frame copy at the beginning of the frame, as well as a dependency on the previous frame. Usage of this extension avoids both and requires only the necessary updates to a back buffer to be made. Terminology This extension and the EGL_KHR_swap_buffers_with_damage extension both use the word "damage" for subtly but significantly different purposes: "Surface damage" is what the EGL_KHR_swap_buffers_with_damage extension is concerned with. This is the area of the *surface* that changes between frames for that surface. It concerns the differences between two buffers - the current back buffer and the current front buffer. It is useful only to the consumer. "Buffer damage" is what the EGL_KHR_partial_update extension is concerned with. This is the area of a particular buffer that has changed since that same buffer was last used. As it only concerns changes to a single buffer, there is no dependency on the next or previous frames or any other buffer. It therefore cannot be used to infer anything about changes to the surface, which requires linking one frame or buffer to another. Buffer damage is therefore only useful to the producer. Following are examples of the two different damage types. Note that the final surface content is the same in both cases, but the damaged areas differ according to the type of damage being discussed. Surface damage example (EGL_KHR_swap_buffers_with_damage) The surface damage for frame n is the difference between frame n and frame (n-1), and represents the area that a compositor must recompose. Frame 0 Frame 1 Frame 2 Frame 3 Frame 4 +---------+ +---------+ +---------+ +---------+ +---------+ | | |#########| |#########| |#########| |#########| | | | | |#########| |#########| |#########| Final surface | | | | | | |#########| |#########| content | | | | | | | | |#########| +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ +---------+ |@@@@@@@@@| |@@@@@@@@@| | | | | | | |@@@@@@@@@| | | |@@@@@@@@@| | | | | Surface damage |@@@@@@@@@| | | | | |@@@@@@@@@| | | |@@@@@@@@@| | | | | | | |@@@@@@@@@| +---------+ +---------+ +---------+ +---------+ +---------+ Buffer damage example (EGL_KHR_partial_update) The buffer damage for a frame is the area changed since that same buffer was last used. If the buffer has not been used before, the buffer damage is the entire area of the buffer. The buffer marked with an 'X' in the top left corner is the buffer that is being used for that frame. This is the buffer to which the buffer age and the buffer damage relate. Note that this example shows a double buffered surface - the actual number of buffers could be different and variable throughout the lifetime of the surface. The age *must* therefore be queried for every frame. Frame 0 Frame 1 Frame 2 Frame 3 Frame 4 +---------+ +---------+ +---------+ +---------+ +---------+ | | |#########| |#########| |#########| |#########| | | | | |#########| |#########| |#########| Final surface | | | | | | |#########| |#########| content | | | | | | | | |#########| +---------+ +---------+ +---------+ +---------+ +---------+ X---------+ +---------+ X---------+ +---------+ X---------+ | | | | |#########| |#########| |#########| | | | | |#########| |#########| |#########| Buffer 1 content | | | | | | | | |#########| | | | | | | | | |#########| +---------+ +---------+ +---------+ +---------+ +---------+ X---------+ +---------+ X---------+ +---------+ |#########| |#########| |#########| |#########| | | | | |#########| |#########| Buffer 2 content | | | | |#########| |#########| | | | | | | | | +---------+ +---------+ +---------+ +---------+ 0 0 2 2 2 Buffer age +---------+ +---------+ +---------+ +---------+ +---------+ |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@| | | | | |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@| | | Buffer damage |@@@@@@@@@| |@@@@@@@@@| | | |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@| |@@@@@@@@@| | | | | |@@@@@@@@@| +---------+ +---------+ +---------+ +---------+ +---------+ Add a new section entitled "Partial updates to postable surfaces" to section 3.5: The "damage region" defines the area of the buffer to which all rendering commands must be restricted. It applies only for surfaces which can be posted, as described in section 3.10, and only when the swap behavior is EGL_BUFFER_DESTROYED. The contents of the buffer outside of the damage region may always be relied upon to contain the same content as the last time they were defined for the current back buffer. See section 3.5.6 for how to query when the current back buffer was last used, and therefore what those contents are. If EGL_EXT_buffer_age is supported, the contents of the buffer inside the damage region may also be relied upon to contain the same content as the last time they were defined for the current back buffer. If EGL_EXT_buffer_age is not supported, the contents of the buffer inside the damage region are always undefined after calling eglSwapBuffers. Setting the damage region appropriately can be used to efficiently update only the necessary areas inbetween frames. After posting the back buffer, the damage region is set to the full dimensions of the surface. The damage region can only be changed by the application before any client API commands that draw to the surface have been made. After this, the damage region is frozen until the back buffer is posted again. Use the command EGLBoolean eglSetDamageRegionKHR( EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects) to set the damage region. The damage region for is set to the area described by and if all of the following conditions are met: * is the current draw surface of the calling thread * is a postable surface * There have been no client API commands which result with rendering to since eglSwapBuffers was last called with , or since was created in case eglSwapBuffers has not yet been called with . * The surface's swap behavior is EGL_BUFFER_DESTROYED specifies the number of rectangles comprising the damage region. is a pointer to a list of values describing the rectangles. The list should consist of groups of four values, with each group representing a single rectangle in surface coordinates in the form {x, y, width, height}. Coordinates are specified relative to the lower left corner of the surface. It is not necessary to avoid overlaps of the specified rectangles. Rectangles that lie (partially) outside of the current surface dimensions (as queryable via the EGL_WIDTH and EGL_HEIGHT attributes) will be clamped to the current surface dimensions. If is zero, is ignored and the damage region is set to the full dimensions of the surface. If is not zero but the rectangles in describe a region of zero area after clamping, the damage region is set to the empty region. If contains more than (4 * ) values, the remaining values are ignored. If contains fewer than (4 * ) values, the behavior is undefined, up to and including program termination. At all times, any client API rendering which falls outside of the damage region results in undefined framebuffer contents for the entire framebuffer. It is the client's responsibility to ensure that rendering is confined to the current damage area. If any client API commands resulting in rendering to have been issued since eglSwapBuffers was last called with , or since the surface was created in case eglSwapBuffers has not yet been called on it, attempting to set the damage region will result in undefined framebuffer contents for the entire framebuffer. Errors ------ eglSetDamageRegionKHR returns EGL_FALSE on failure: * If is not a postable surface, an EGL_BAD_MATCH error is generated * If is not the current draw surface for the calling thread, an EGL_BAD_MATCH error is generated * If the value of EGL_SWAP_BEHAVIOR for is not EGL_BUFFER_DESTROYED, an EGL_BAD_MATCH error is generated * If eglSetDamageRegionKHR has already been called on since the most recent frame boundary, an EGL_BAD_ACCESS error is generated * If the EGL_BUFFER_AGE_KHR attribute of has not been queried since the most recent frame boundary, an EGL_BAD_ACCESS error is generated Add before the final paragraph in section 3.5.6 "Surface Attributes": Querying EGL_BUFFER_AGE_KHR returns the age of the color contents of the current back buffer as the number of frames elapsed since it was most recently defined. Under certain conditions described below, applications can, in conjunction with the surface's damage region (see section 3.5.1), use this age to safely rely on the contents of old back buffers to reduce the amount of redrawing they do each frame. To query the age of a surface, it must be the current draw surface for the calling thread. Function name -------------------- eglSwapBuffers eglSwapBuffersWithDamageKHR Table 3.X, Frame Boundary Functions Buffers' ages are initialized to 0 at buffer creation time. When a frame boundary is reached, the following occurs before any exchanging or copying of color buffers: * The current back buffer's age is set to 1. * Any other color buffers' ages are incremented by 1 if their age was previously greater than 0. For example, with a double buffered surface and an implementation that swaps via buffer exchanges, the age would usually be 2. With a triple buffered surface the age would usually be 3. An age of 1 usually means the previous swap was implemented as a copy. An age of 0 means the buffer has only just been initialized and the contents are undefined. Single buffered surfaces have no frame boundaries and therefore always have an age of 0. Where specified in terms of the current damage region (see section 3.5.6), the relevant part of a buffer's content is considered defined when the buffer's age is a value greater than 0. Frame boundaries are the only events that can set a buffer's age to a positive value. Once EGL_BUFFER_AGE_KHR has been queried then it can be assumed that the age will remain valid until the next frame boundary. EGL implementations are permitted, but not required, to reset the buffer age in response to pixel ownership test changes for any pixels within the drawable, or if new pixels are added to or removed from the drawable, i.e., the drawable is resized. A reset of this nature does not affect the age of content for pixels that pass the pixel ownership test before and after the event that caused the reset. In other words, applications can assume that no event will invalidate the content of pixels that continuously pass the pixel ownership test between when the buffer age was queried and the following frame boundary. It is up to applications to track pixel ownership using data collected from relevant window system events, such as configuration and expose events on the X11 platform. EGL_BUFFER_AGE_KHR state is a property of the EGL surface that owns the buffers and lives in the address space of the application. That is, if an EGL surface has been created from a native window or pixmap that may be shared between processes, the buffer age is not guaranteed to be synchronized across the processes. Binding and unbinding a surface to and from one or more contexts in the same address space will not affect the ages of any buffers in that surface. Add to the list of errors for eglQuerySurface at the end of section 3.5.6 "Surface Attributes": If is EGL_BUFFER_AGE_KHR and is not the current draw surface for the calling thread, an EGL_BAD_SURFACE error is generated. Add to the end of section 3.10.1.1 "Native Window Resizing": If eglSetDamageRegionKHR has been called with anything other than zero for , a surface resize will cause the damage region to become undefined. This will effectively cause the entire framebuffer content to become undefined until the next frame. Dependencies on EGL_KHR_swap_buffers_with_damage If EGL_KHR_swap_buffers_with_damage is not supported, all references to eglSwapBuffersWithDamageKHR are removed. Issues 1) What should happen if the client renders outside of the damage area? RESOLVED: The entire framebuffer content will be undefined. DISCUSSION: The definedness of different parts of the buffer varies across implementations, making it hard to define, and providing any more specific information may encourage improper and non-portable use of this extension. 2) How does this interact with EGL_EXT_buffer_age? RESOLVED: The initial content of the damage area differs depending on whether EGL_EXT_buffer_age is present or not, making this extension fully backwards compatible with EGL_EXT_buffer_age, while not depending on it. 3) How does this interact with EGL_KHR_swap_buffers_with_damage? RESOLVED: It does not interact materially with EGL_KHR_swap_buffers_with_damage, except for the trivial interaction with eglSwapBuffersWithDamageKHR being a frame boundary function if the extension is also supported. DISCUSSION: This extension only provides a way to efficiently update the back buffer for a surface. It does not have any effect on the subsequent posting of that buffer. For maximum efficiency, applications should use both EGL_KHR_partial_update and EGL_KHR_swap_buffers_with_damage simultaneously. 4) How does this interact with EGL_BUFFER_PRESERVED? RESOLVED: It is an error to call eglSetDamageRegionKHR with a surface with EGL_BUFFER_PRESERVED swap behavior. However, it is not an error to query the age of the buffer in this case. DISCUSSION: A layered extension will be proposed to guarantee that the age of a buffer is always 1 after the first frame for a surface. This will provide similar (but not identical) semantics to EGL_BUFFER_PRESERVED for applications that need it. 5) How does surface resizing affect the damage region? RESOLVED: The damage region becomes undefined if a surface resize occurs after it has been set to anything except the full buffer. Because rendering outside the damage area results in undefined framebuffer contents, this effectively means that the entire framebuffer content becomes undefined until the next frame. 6) What happens if the damage region is set after any client rendering commands? OPTION 1: An error is returned. Detecting this condition is non-trivial in some implementations. OPTION 2: The entire framebuffer contents become undefined. RESOLVED: Option 2. 7) Should the entire region be provided in advance of any rendering, or should each region be supplied immediately before the rendering commands for that region, and multiple regions can be defined per frame? RESOLVED: The entire region must be provided in advance of any rendering. 8) What should be the behavior if eglSetDamageRegionKHR is called multiple times before the first rendering command? RESOLVED: This is an error. The entire region must be provided during a single call, with no overwrite or modify behavior needed. 9) Is it allowed to set the damage region when the buffer age has not been queried? RESOLVED: This is an error. This could only make sense when the damage region is the entire buffer, which it is initially anyway. Otherwise the undamaged area needs to be defined to an age that the application doesn't know about. It's not clear that this would ever be useful to the application, because it can't know at this point which areas it needs to update. 10) What is the behavior if, after clamping, the damage region is empty? RESOLVED: The damage region is set to empty. Revision History Version 1, 28/01/2014 - Initial draft Version 2, 05/02/2014 - Removed clip behavior, replaced with undefined framebuffer contents if client renders outside of given damage region - Renamed to EGL_KHR_partial_update from EGL_KHR_frame_clip - Added detailed parameter descriptions and error conditions - Added dependency on GL_XXX_damage_region - Defined interactions with EGL_EXT_buffer_age Version 3, 04/03/2014 - Removed dependency on GL_XXX_damage_region - Changed error on defining damage region after drawcalls to be undefined rendering results instead - Redefined interactions with EGL_EXT_buffer_age to allow both to exist Version 4, 20/03/2014 - Modified language to allow use with EGLStream producer surfaces - Clarified that surface must be the current *draw* surface - Changed n_rects=0 behavior to set the damage region to the entire surface - Clarified that rendering outside the damage region results in the entire framebuffer becoming undefined Version 5, 20/03/2014 - Updated to be based on EGL 1.5 spec Version 6, 23/04/2014 -Added the pixel ownership logic from EGL_EXT_buffer_age -Ported over the detailed description of buffer age from EGL_EXT_buffer_age -Added a "New Functions" and "New Tokens" section. -Added dependencies on EGL_EXT_swap_buffers_with_damage Version 7, 20/05/2014 - Removing a couple of now-obsolete sentences - An age of 1 *usually* means the previous swap was implemented as a copy. - Reworded "For the purposes of buffer age tracking..." to reference the conditions under which the different parts of the buffer are actually defined, which depend on the damage region Version 8, 20/05/2014 - Added issues list Version 9, 12/08/2014 - Removed outdated modification to "Posting to a Window" - Changed names and order of rects/n_rects to match EGL_EXT_swap_buffers_with_damage - Resolved issue 3 on EGL_EXT_swap_buffers_with_damage interactions - Resolved issue 4 on EGL_BUFFER_PRESERVED swap behavior - Resolved issue 5 on surface resize behavior - Resolved issue 7 on multiple calls to eglSetDamageRegionKHR - Added issue 8 and suggested resolution - Added issue 9 and suggested resolution - Added issue 10 and suggested resolution Version 10, 19/08/2014 - Added section on terminology and damage types Version 11, 10/09/2014 - Resolved outstanding issues Version 12, 12/09/2014 - Added the restriction that you can only query the age of a surface while it is the current draw surface. Version 13, 18/09/2015 - Marked as a Display extension - Changed remaining references to EGL_EXT_swap_buffers_with_damage to EGL_KHR_swap_buffers_with_damage