IMG_framebuffer_downsample

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.