Name

    IMG_framebuffer_downsample

Name Strings

    GL_IMG_framebuffer_downsample

Contributors

    Tobias Hector, Imagination Technologies (tobias.hector 'at' imgtec.com)

Contact

    Tobias Hector (tobias.hector 'at' imgtec.com)

Status

    Complete

Version

    Last Modified Date: August 20, 2015
    Revision: 11

Number

    OpenGL ES Extension #255

Dependencies

    OpenGL ES 2.0 or OES_framebuffer_object are required.

    This extension is written against the OpenGL ES 3.0.4 Specification
    (August 27, 2014).

    This extension has interactions with GL_EXT_multisampled_render_to_texture.

    This extension has interactions with OpenGL ES 3.1.

    This extension has interactions with GL_EXT_color_buffer_float.

    This extension has interactions with GL_EXT_color_buffer_half_float.

Overview

    This extension introduces the ability to attach color buffers to a
    framebuffer that are at a lower resolution than the framebuffer itself, with
    the GPU automatically downsampling the color attachment to fit.

    This can be useful for various post-process rendering techniques where it is
    desirable to generate downsampled images in an efficient manner, or for a
    lower resolution post-process technique.

    This extension exposes at least a 2 x 2 downscale. Other downsampling modes
    may be exposed on the system and this can be queried.

IP Status

    No known IP claims.

New Procedures and Functions

    void FramebufferTexture2DDownsampleIMG(
            enum target, enum attachment,
            enum textarget, uint texture,
            int level, int xscale, int yscale);

    void FramebufferTextureLayerDownsampleIMG(
            enum target, enum attachment,
            uint texture, int level,
            int layer, int xscale, int yscale);

New Tokens

    Returned by CheckFramebufferStatus:

        FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG 0x913C

    Accepted by the <pname> parameter of GetBooleanv, GetIntegerv, GetFloatv,
    GetInteger64v, and GetInternalFormativ:

        NUM_DOWNSAMPLE_SCALES_IMG                             0x913D

    Accepted by the <target> parameter of GetIntegerv, GetInteger64v,
    GetIntegeri_v, GetInteger64i_v and GetInternalFormativ:

        DOWNSAMPLE_SCALES_IMG                                 0x913E

    Accepted by the <pname> parameter of GetFramebufferAttachmentParameteriv:

        FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG              0x913F

Additions to Chapter 4 of the OpenGL ES 3.0 Specification:

    Modify figure 4.1, "Per-Fragment Operations.", to add an additional box
    "Downscaling" after "Additional Multisample Fragment Operations".

    Add a new section 4.1.11, "Downscaling":

        If no multisampling was performed, and downscaling is enabled, fragment
        outputs may be optionally downscaled in a similar way to how multiple
        samples are resolved. If the value of FRAMEBUFFER_ATTACHMENT_TEXTURE_-
        SCALE_IMG is not {1,1}, fragment values are written to an intermediate
        buffer. After all other fragment operations have completed, they are
        then combined to a produce a single color value, and that value is
        written into the corresponding color buffer selected by DrawBuffers. An
        implementation may defer the writing of color buffers until a later
        time, but the state of the framebuffer must behave as if the color
        buffers were updated as each fragment is processed. The method of
        combination is not specified. If the framebuffer contains sRGB values,
        then it is recommended that an average of samples is computed in a
        linearized space, as for blending (see section 4.1.7). Otherwise, a
        simple average computed independently for each color component is
        recommended.

    Add the following to Section 4.4.2 "Attaching Images to Framebuffer Objects"
    after the paragraph describing FramebufferTexture2D:

        The command

            void FramebufferTexture2DDownsampleIMG(
                    enum target, enum attachment,
                    enum textarget, uint texture,
                    int level, uint xscale, uint yscale);

        allows a rendering into the image of a texture object that has a lower
        resolution than the framebuffer.

        target, textarget, texture, and level correspond to the same
        parameters for FramebufferTexture2D and have the same restrictions.

        attachment corresponds to the same parameter for FramebufferTexture2D,
        but must be COLOR_ATTACHMENTn.

        xscale and yscale are multiplied by texture's width and height,
        respectively, to produce the effective size of the attachment when
        rendering. For example, a texture width of 128 with an xscale of 2 would
        produce a color attachment with the effective width of 256. xscale and
        yscale must be one of the value pairs in DOWNSAMPLE_SCALES_IMG. If the
        xscale and yscale value pair is not available on the implementation,
        then the error INVALID_VALUE is generated.

        The implementation allocates an implicit color buffer for the same
        internalformat as the specified texture, and widths and heights from the
        specified texture level, multiplied by xscale and yscale. This buffer is
        used as the target for rendering instead of the specified texture level.
        The buffer is associated with the attachment and gets deleted after the
        attachment is broken.

        When the texture level is used as a source or destination for any
        operation, the attachment is flushed, or when the attachment is broken,
        an implicit downsample of the color data from the color buffer to the
        texture level may be performed. After such a downsample, the contents
        of the color buffer become undefined.

        The operations which may cause a resolve include:
            * Drawing with the texture bound to an active texture unit
            * ReadPixels or CopyTex[Sub]Image* while the texture is
              attached to the framebuffer
            * CopyTex[Sub]Image*, Tex[Sub]Image*,
              CompressedTex[Sub]Image* with the specified level as
              destination
            * GenerateMipmap
            * Flush or Finish while the texture is attached to the
              framebuffer
            * BindFramebuffer while the texture is attached to the currently
              bound framebuffer.

        Whether each of the above cause a resolve or not is implementation-
        dependent.

    Add the following to the sub-section "Attaching Texture Images to a
    Framebuffer" after the paragraph describing FramebufferTextureLayer:

        The command

            void FramebufferTextureLayerDownsampleIMG(
                    enum target, enum attachment,
                    uint texture, int level,
                    int layer, uint xscale, uint yscale);

        allows a rendering into a single layer of a texture object that has a
        lower resolution than the framebuffer. It operates like a combination of
        FramebufferTexture2DDownsampleIMG and FramebufferTextureLayer; it allows
        the developer to set scaling values and attaches a single layer of a
        three-dimensional or two-dimensional array texture level.

        target, attachment, level, xscale and yscale correspond to the same
        parameters for FramebufferTexture2DDownsampleIMG and have the same
        restrictions.

        texture can only be a two-dimensional array texture, but otherwise has
        the same restrictions as it does for FramebufferTextureLayer.

        layer corresponds to the same parameter for FramebufferTextureLayer and
        has the same restrictions.

    In the sub-section "Effects of Attaching a Texture Image", change the bullet
    list to the following:

        * The value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is set to TEXTURE.
        * The value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME is set to texture.
        * The value of FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL is set to level.
        * If FramebufferTexture2D is called and texture is a cube map texture,
          then the value of FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE is set
          to textarget; otherwise it is set to the default (NONE).
        * If FramebufferTextureLayer is called, then the value of FRAMEBUFFER_-
          ATTACHMENT_TEXTURE_LAYER is set to layer; otherwise it is set to zero.
        * If FramebufferTexture2DDownsampleIMG or
          FramebufferTextureLayerDownsampleIMG is called, then the value of
          FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG is set to {xscale, yscale};
          otherwise it is set to {1, 1}.

    In section 4.4.4 "Framebuffer Completeness", add the following bullet to the
    end of the list in subsection "Framebuffer Attachment Completeness":


        * If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE and the
          value of FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG is supported by the
          internal format of the attachment (see GetInternalFormativ in section
          6.1.15).

    In section 4.4.4 "Framebuffer Completeness", add the following bullet to the
    end of the list in subsection "Whole Framebuffer Completeness":

        * The value of FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG for all
          attachments is {1,1}, or if not, the value of TEXTURE_SAMPLES_EXT or
          RENDERBUFFER_SAMPLES for all attachments is zero.
          FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG

Additions to Chapter 6 of the OpenGL ES 3.0 Specification:

    Add the following bullet point to the list in Section 6.1.13 "Framebuffer
    Object Queries" describing valid pname values when FRAMEBUFFER_ATTACHMENT_-
    OBJECT_TYPE is TEXTURE:

        * If pname is FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG, then params will
          contain two integer values - the downsample scale pair for that
          attachment.

    Change the paragraph in Section 6.1.15 "Internal Format Queries" describing
    valid target values to:

        target indicates the usage of the internalformat, and must be one of
    RENDERBUFFER, TEXTURE_2D, TEXTURE_CUBE_MAP or TEXTURE_2D_ARRAY.

    Add the following paragraphs to Section 6.1.15 "Internal Format Queries" to
    the paragraphs describing valid pname values:

        If pname is NUM_DOWNSAMPLE_SCALES_IMG, the number of downscales that
    would be returned by querying DOWNSAMPLE_SCALES_IMG is returned in params.
        If pname is DOWNSAMPLE_SCALES_IMG, the available downscale pairs for the
    format are written into params.
        Formats that don't support downsampling will still return one valid
    downsample scale pair - {1,1}. A value of one for NUM_DOWNSAMPLE_SCALES_IMG
    will always mean no downscaling available, as {1,1} must be supported by
    every format. Targets that don't support downscaling (e.g. RENDERBUFFER)
    will return no downsample scale pairs.

Interactions with OpenGL ES 2.0

    In section 4.4.5 of the OpenGL ES 2.0 Specification "Framebuffer
    Completeness", subsection "Framebuffer Attachment Completeness", replace:

        * All attached images have the same width and height.
          FRAMEBUFFER_INCOMPLETE_DIMENSIONS

    with:

        * All attached images have the same value of width * xscale and
          height * yscale.
          FRAMEBUFFER_INCOMPLETE_DIMENSIONS

Interactions with OpenGL ES 3.1

    If OpenGL ES 3.1 is supported, replace TEXTURE_SAMPLES_EXT with TEXTURE_-
    SAMPLES, and add TEXTURE_2D_MULTISAMPLE to the list of valid targets for
    GetInternalFormativ.

Interactions with EXT_multisampled_render_to_texture

    If EXT_multisampled_render_to_texture is not supported:
        - ignore references to TEXTURE_SAMPLES_EXT
        - the sample counts returned by GetInternalFormativ with a target of
          TEXTURE* will be the sample values available to be used with
          FramebufferTexture2DMultisampleEXT

Dependencies on OpenGL ES 3.0

    If OpenGL ES 3.0 or higher is not supported, ignore references to
    glFramebufferTextureLayerDownsample and glGetIntegeri_v.

Interactions with EXT_color_buffer_float and EXT_color_buffer_half_float

    If either of these extensions are supported, it is not guaranteed that
    downscale of these formats is supported, but it may be - users will have to
    check with the GetInternalFormat query.

    This equally applies to any other additional render formats provided by
    extension.

Errors

    The error INVALID_VALUE is generated if FramebufferTextureLayerDownsampleIMG
    or FramebufferTexture2DDownsampleIMG are are called with an <xscale> and
    <yscale> value pair that isn't reported by DOWNSAMPLE_SCALES_IMG.

    The error INVALID_ENUM is generated if FramebufferTextureLayerDownsampleIMG
    or FramebufferTexture2DDownsampleIMG are called with an <attachment> that is
    not COLOR_ATTACHMENTn.

New State

    Add to Table 6.14 "Framebuffer (state per attachment point)"

                                                        Initial
    Get Value         Type        Get Command           Value   Description     Sec.
    ----------------- ----------- --------------------- ------- --------------- ----
    FRAMEBUFFER_-     2 x Z+      GetFramebuffer-       {1,1}   Framebuffer     4.4.2
    ATTACHMENT_-                  AttachmentParameteriv         texture scale
    TEXTURE_SCALE_IMG

New Implementation Dependent State

    Add to Table 6.35 "Framebuffer Dependent Values"
                                                     Minimum
    Get Value         Type        Get Command        Value   Description     Sec.
    ----------------- ----------- ------------------ ------- --------------- ----
    NUM_DOWNSAMPLE_-  2 x Z+      GetIntegerv        2       Number of       4.4.2
    SCALES_IMG                                               scale value
                                                             pairs available

    DOWNSAMPLE_-      1* x 2 x Z+ GetIntegeri_v      **      Scale value     4.4.2
    SCALES_IMG                                               pairs available


    ** At least {1,1} and {2,2} must be supported as a minimum to support this extension.

Example

    GLint xDownscale = 1;
    GLint yDownscale = 1;

    // Select a downscale amount if possible
    if (extension_is_supported("GL_IMG_framebuffer_downsample")
    {
        // Query the number of available scales
        GLint numScales;
        glGetIntegerv(GL_NUM_DOWNSAMPLE_SCALES_IMG, &numScales);

        // 2 scale modes are supported as minimum, so only need to check for
        // better than 2x2 if more modes are exposed.
        if (numScales > 2)
        {
            // Try to select most aggressive scaling.
            GLint bestScale = 1;
            GLint tempScale[2];
            GLint i;
            for (i = 0; i < numScales; ++i)
            {
                glGetIntegeri_v(GL_DOWNSAMPLE_SCALES_IMG, i, tempScale);

                // If the scaling is more aggressive, update our x/y scale values.
                if (tempScale[0] * tempScale[1] > bestScale)
                {
                    xDownscale = tempScale[0];
                    yDownscale = tempScale[1];
                }
            }
        }
        else
        {
            xDownscale = 2;
            yDownscale = 2;
        }
    }

    // Create depth texture. Depth and stencil buffers must be full size
    GLuint depthTexture;
    glGenTextures(1, &depthTexture);
    glBindTexture(GL_TEXTURE_2D, depthTexture);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, width, height);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    // Create a full size RGBA texture with single mipmap level
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexStorage2D(GL_TEXTURE_2D, 0, GL_RGBA4, width, height);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    // Scale the width and height appropriately.
    GLint scaledWidth = width / xDownscale;
    GLint scaledHeight = height / yDownscale;

    // Create a reduced size RGBA texture with single mipmap level
    GLuint scaledTexture;
    glGenTextures(1, &scaledTexture);
    glBindTexture(GL_TEXTURE_2D, scaledTexture);
    glTexStorage2D(GL_TEXTURE_2D, 0, GL_RGBA4, scaledWidth, scaledHeight);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    // Create framebuffer object, attach textures
    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
        GL_TEXTURE_2D, depthTexture);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
        GL_TEXTURE_2D, texture, 0);
    glFramebufferTexture2DDownsampleIMG(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
        GL_TEXTURE_2D, scaledTexture, 0, xDownscale, yDownscale);

    // Handle unsupported cases
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    {
        ...
    }

    // Draw to the texture
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    ...

    // Discard the depth renderbuffer contents if possible/available
    if (extension_supported("GL_EXT_discard_framebuffer"))
    {
        GLenum discard_attachments[] = { GL_DEPTH_ATTACHMENT };
        glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discard_attachments);
    }

    /*
        Draw to the default framebuffer using the textures with various post
        processing effects.
    */
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, scaledTexture);
    ...

Issues

    1) Should renderbuffers be resolvable in this way too?

       RESOLVED

       No, renderbuffers are considered somewhat legacy and thus will
       not be supported by this extension.

    2) Should any scale values other than {1,1} be mandated as minimum?

       RESOLVED

       Yes, {2,2} will also be required. Implementations may support additional
       values though, so a query is also added for other values.

    3) What formats support downscaling?

       RESOLVED

       Formats that are guaranteed color-renderable by the core ES 3.1
       specification, excluding integer and signed integer formats, support all
       available downscale modes. Other formats only support {1,1} (no
       downscaling).

    4) What should happen if an application calls GetInternalFormativ with a
       target of TEXTURE* (not TEXTURE_2D_MULTISAMPLE)?

       RESOLVED

       For standard OpenGL ES, NUM_SAMPLE_COUNTS should be zero. However, if
       EXT_multisampled_render_to_texture is supported, valid configurations
       for FramebufferTexture2DMultisampleEXT should be returned here.

Revision History

    Revision 1, 2014/08/27
      - First draft

    Revision 2, 2015/03/16
      - Mandated {2,2} as a required downsample scale.
      - Coupled x and y scale values into pairs

    Revision 3, 2015/03/19
      - Moved framebuffer completeness information to correct (whole framebuffer
        completeness) section, and corrected wording.
      - Added note about minimum support in the overview.

    Revision 4, 2015/03/19
      - Added a specific revision of the OpenGL ES 3.0 specification
      - Added an error that only COLOR_ATTACHMENTn can be used as an attachment
        point

    Revision 5, 2015/06/02
      - Added internalformat query capability, so that formats can opt into
        downscaling support
      - Added section on downscaling to per-fragment operations.
      - Added issue about what formats support downscaling.
      - Restricted layer downscaling to 2D array textures.

    Revision 6, 2015/06/03
      - Fixed typo in incomplete framebuffer condition
      - Added a bullet point to describe the FRAMEBUFFER_ATTACHMENT_TEXTURE_-
        SCALE_IMG parameter to GetFramebufferAttachmentiv
      - Clarified targets to GetInternalFormativ

    Revision 7, 2015/06/17
      - Clarified interactions with EXT_color_buffer_float and
        EXT_color_buffer_half_float

    Revision 8, 2015/06/22
      - Added NUM_DOWNSAMPLE_SCALES_IMG as a parameter to GetInternalFormativ
      - Added framebuffer attachment incomplete message and removed error when
        scale pair isn't supported by textures' internalformat, as
        internalformat is not necessarily known at attachment time.

    Revision 9, 2015/08/19
      - Assigned enum values

    Revision 10, 2015/08/20
      - Allowed DOWNSAMPLE_SCALES_IMG to be used with GetIntegerv/GetInteger64v

    Revision 11, 2015/12/18
      - Fixed example - "tempScale" is an array so doesn't need to be dereferenced.
