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 14, April 24, 2025

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 <attribute> 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 <surface> is set to the area described by <n_rects> and
    <rects> if all of the following conditions are met:

    * <surface> is the current draw surface of the calling thread
    * <surface> is a postable surface
    * There have been no client API commands which result with rendering to
      <surface> since eglSwapBuffers was last called with <surface>, or since
      <surface> was created in case eglSwapBuffers has not yet been called with
      <surface>.
    * The surface's swap behavior is EGL_BUFFER_DESTROYED

    <n_rects> specifies the number of rectangles comprising the damage region.
    <rects> is a pointer to a list of values describing the rectangles. The list
    should consist of <n_rects> 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 <n_rects> is zero, <rects> is ignored and the damage region is set to the
    full dimensions of the surface.

    If <n_rects> is not zero but the rectangles in <rects> describe a region of
    zero area after clamping, the damage region is set to the empty region.

    If <rects> contains more than (4 * <n_rects>) values, the remaining values
    are ignored. If <rects> contains fewer than (4 * <n_rects>) 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 <surface> have been
    issued since eglSwapBuffers was last called with <surface>, 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 <surface> is not a postable surface, an EGL_BAD_MATCH error is
      generated
    * If <surface> 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 <surface> is not
      EGL_BUFFER_DESTROYED, an EGL_BAD_MATCH error is generated
    * If eglSetDamageRegionKHR has already been called on <surface> since the
      most recent frame boundary, an EGL_BAD_ACCESS error is generated
    * If the EGL_BUFFER_AGE_KHR attribute of <surface> 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 <attribute> is EGL_BUFFER_AGE_KHR and <surface> 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
    <n_rects>, 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.

11) What is the behavior if n_rects is < 0 ?

    RESOLVED: This case is undefined.


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
    Version 14, 24/04/2025
     - Add Issue number 11 and resolution

