Add support for EXT_sRGB.

BUG=angle:672

Change-Id: I001ff3dde7a39e545a535a399c02f3a6d91634c8
Reviewed-on: https://chromium-review.googlesource.com/203460
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libGLESv2/Caps.cpp b/src/libGLESv2/Caps.cpp
index 42aa70a..088e502 100644
--- a/src/libGLESv2/Caps.cpp
+++ b/src/libGLESv2/Caps.cpp
@@ -125,6 +125,7 @@
     InsertExtensionString("GL_EXT_texture_compression_dxt1",   2,    0,     textureCompressionDXT1,   clientVersion, &extensionStrings);
     InsertExtensionString("GL_ANGLE_texture_compression_dxt3", 2,    0,     textureCompressionDXT3,   clientVersion, &extensionStrings);
     InsertExtensionString("GL_ANGLE_texture_compression_dxt5", 2,    0,     textureCompressionDXT5,   clientVersion, &extensionStrings);
+    InsertExtensionString("GL_EXT_sRGB",                       2,    0,     sRGB,                     clientVersion, &extensionStrings); // FIXME: Don't advertise in ES3
     InsertExtensionString("GL_ANGLE_depth_texture",            2,    0,     depthTextures,            clientVersion, &extensionStrings);
     InsertExtensionString("GL_EXT_texture_storage",            2,    0,     textureStorage,           clientVersion, &extensionStrings);
     InsertExtensionString("GL_OES_texture_npot",               2,    0,     textureNPOT,              clientVersion, &extensionStrings);
@@ -282,6 +283,20 @@
     return GetFormatSupport(textureCaps, requiredFormats, true, false, false);
 }
 
+// Check for GL_ANGLE_texture_compression_dxt5
+static bool DetermineSRGBTextureSupport(const TextureCapsMap &textureCaps)
+{
+    std::vector<GLenum> requiredFilterFormats;
+    requiredFilterFormats.push_back(GL_SRGB8);
+    requiredFilterFormats.push_back(GL_SRGB8_ALPHA8);
+
+    std::vector<GLenum> requiredRenderFormats;
+    requiredRenderFormats.push_back(GL_SRGB8_ALPHA8);
+
+    return GetFormatSupport(textureCaps, requiredFilterFormats, true, false, false) &&
+           GetFormatSupport(textureCaps, requiredRenderFormats, false, true, false);
+}
+
 // Check for GL_ANGLE_depth_texture
 static bool DetermineDepthTextureSupport(const TextureCapsMap &textureCaps)
 {
@@ -320,6 +335,7 @@
     textureCompressionDXT1 = DetermineDXT1TextureSupport(textureCaps);
     textureCompressionDXT3 = DetermineDXT3TextureSupport(textureCaps);
     textureCompressionDXT5 = DetermineDXT5TextureSupport(textureCaps);
+    sRGB = DetermineSRGBTextureSupport(textureCaps);
     depthTextures = DetermineDepthTextureSupport(textureCaps);
     colorBufferFloat = DetermineColorBufferFloatSupport(textureCaps);
 }
diff --git a/src/libGLESv2/Caps.h b/src/libGLESv2/Caps.h
index 4ad7b9d..7fd5443 100644
--- a/src/libGLESv2/Caps.h
+++ b/src/libGLESv2/Caps.h
@@ -119,6 +119,11 @@
     bool textureCompressionDXT3;
     bool textureCompressionDXT5;
 
+    // GL_EXT_sRGB
+    // Implies that TextureCaps for GL_SRGB8_ALPHA8 and GL_SRGB8 exist
+    // TODO: Don't advertise this extension in ES3
+    bool sRGB;
+
     // GL_ANGLE_depth_texture
     bool depthTextures;
 
diff --git a/src/libGLESv2/formatutils.cpp b/src/libGLESv2/formatutils.cpp
index 8f249b5..ed6e05f 100644
--- a/src/libGLESv2/formatutils.cpp
+++ b/src/libGLESv2/formatutils.cpp
@@ -80,6 +80,9 @@
     InsertFormatMapping(&map, GL_RGBA,                            GL_FLOAT,                          GL_RGBA32F_EXT,                     WriteColor<R32G32B32A32F, GLfloat>);
     InsertFormatMapping(&map, GL_RGBA,                            GL_HALF_FLOAT_OES,                 GL_RGBA16F_EXT,                     WriteColor<R16G16B16A16F, GLfloat>);
 
+    InsertFormatMapping(&map, GL_SRGB_EXT,                        GL_UNSIGNED_BYTE,                  GL_SRGB8,                           WriteColor<R8G8B8, GLfloat>        );
+    InsertFormatMapping(&map, GL_SRGB_ALPHA_EXT,                  GL_UNSIGNED_BYTE,                  GL_SRGB8_ALPHA8,                    WriteColor<R8G8B8A8, GLfloat>      );
+
     InsertFormatMapping(&map, GL_BGRA_EXT,                        GL_UNSIGNED_BYTE,                  GL_BGRA8_EXT,                       WriteColor<B8G8R8A8, GLfloat>     );
     InsertFormatMapping(&map, GL_BGRA_EXT,                        GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT, GL_BGRA4_ANGLEX,                    WriteColor<B4G4R4A4, GLfloat>     );
     InsertFormatMapping(&map, GL_BGRA_EXT,                        GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT, GL_BGR5_A1_ANGLEX,                  WriteColor<B5G5R5A1, GLfloat>     );
@@ -175,6 +178,9 @@
     InsertFormatMapping(&map, GL_BGRA_EXT,           GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT, GL_BGRA4_ANGLEX,           WriteColor<B4G4R4A4, GLfloat>     );
     InsertFormatMapping(&map, GL_BGRA_EXT,           GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT, GL_BGR5_A1_ANGLEX,         WriteColor<B5G5R5A1, GLfloat>     );
 
+    InsertFormatMapping(&map, GL_SRGB_EXT,           GL_UNSIGNED_BYTE,                  GL_SRGB8,                  WriteColor<R8G8B8, GLfloat>       );
+    InsertFormatMapping(&map, GL_SRGB_ALPHA_EXT,     GL_UNSIGNED_BYTE,                  GL_SRGB8_ALPHA8,           WriteColor<R8G8B8A8, GLfloat>     );
+
     InsertFormatMapping(&map, GL_COMPRESSED_RGB_S3TC_DXT1_EXT,    GL_UNSIGNED_BYTE,     GL_COMPRESSED_RGB_S3TC_DXT1_EXT,    NULL                     );
     InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,   GL_UNSIGNED_BYTE,     GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,   NULL                     );
     InsertFormatMapping(&map, GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, GL_UNSIGNED_BYTE,     GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE, NULL                     );
@@ -307,6 +313,8 @@
     set.insert(FormatInfo(GL_LUMINANCE_ALPHA,    GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE                 ));
     set.insert(FormatInfo(GL_LUMINANCE,          GL_LUMINANCE,       GL_UNSIGNED_BYTE                 ));
     set.insert(FormatInfo(GL_ALPHA,              GL_ALPHA,           GL_UNSIGNED_BYTE                 ));
+    set.insert(FormatInfo(GL_SRGB_ALPHA_EXT,     GL_SRGB_ALPHA_EXT,  GL_UNSIGNED_BYTE                 ));
+    set.insert(FormatInfo(GL_SRGB_EXT,           GL_SRGB_EXT,        GL_UNSIGNED_BYTE                 ));
 
     // Depth stencil formats
     set.insert(FormatInfo(GL_DEPTH_COMPONENT16,  GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT                ));
@@ -316,6 +324,10 @@
     set.insert(FormatInfo(GL_DEPTH24_STENCIL8,   GL_DEPTH_STENCIL,   GL_UNSIGNED_INT_24_8             ));
     set.insert(FormatInfo(GL_DEPTH32F_STENCIL8,  GL_DEPTH_STENCIL,   GL_FLOAT_32_UNSIGNED_INT_24_8_REV));
 
+    // From GL_EXT_sRGB
+    set.insert(FormatInfo(GL_SRGB8_ALPHA8_EXT,   GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE                  ));
+    set.insert(FormatInfo(GL_SRGB8,              GL_SRGB_EXT,       GL_UNSIGNED_BYTE                  ));
+
     // From GL_OES_texture_float
     set.insert(FormatInfo(GL_LUMINANCE_ALPHA,    GL_LUMINANCE_ALPHA, GL_FLOAT                         ));
     set.insert(FormatInfo(GL_LUMINANCE,          GL_LUMINANCE,       GL_FLOAT                         ));
@@ -712,6 +724,8 @@
     map.insert(InternalFormatInfoPair(GL_RGB_INTEGER,     InternalFormatInfo::UnsizedFormat(GL_RGB_INTEGER,     AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_RGBA_INTEGER,    InternalFormatInfo::UnsizedFormat(GL_RGBA_INTEGER,    AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_BGRA_EXT,        InternalFormatInfo::UnsizedFormat(GL_BGRA_EXT,        AlwaysSupported)));
+    map.insert(InternalFormatInfoPair(GL_SRGB_EXT,        InternalFormatInfo::UnsizedFormat(GL_RGB,             CheckSupport<&Extensions::sRGB>)));
+    map.insert(InternalFormatInfoPair(GL_SRGB_ALPHA_EXT,  InternalFormatInfo::UnsizedFormat(GL_RGBA,            CheckSupport<&Extensions::sRGB>)));
 
     // Compressed formats, From ES 3.0.1 spec, table 3.16
     //                               | Internal format                             |                                    |W |H | BS |CC| Format                                      | Type            | SRGB | Supported          |
@@ -761,6 +775,10 @@
     map.insert(InternalFormatInfoPair(GL_BGRA4_ANGLEX,         InternalFormatInfo::RGBAFormat( 4,  4,  4,  4, 0, GL_BGRA_EXT,      GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_NORMALIZED, false, CheckSupport<&Extensions::textureFormatBGRA8888>)));
     map.insert(InternalFormatInfoPair(GL_BGR5_A1_ANGLEX,       InternalFormatInfo::RGBAFormat( 5,  5,  5,  1, 0, GL_BGRA_EXT,      GL_UNSIGNED_SHORT_5_5_5_1, GL_UNSIGNED_NORMALIZED, false, CheckSupport<&Extensions::textureFormatBGRA8888>)));
 
+    // From GL_EXT_sRGB
+    map.insert(InternalFormatInfoPair(GL_SRGB8,                InternalFormatInfo::RGBAFormat( 8,  8,  8,  0, 0, GL_RGB,           GL_UNSIGNED_BYTE,           GL_UNSIGNED_NORMALIZED, true,  CheckSupport<&Extensions::sRGB>)));
+    map.insert(InternalFormatInfoPair(GL_SRGB8_ALPHA8,         InternalFormatInfo::RGBAFormat( 8,  8,  8,  8, 0, GL_RGBA,          GL_UNSIGNED_BYTE,           GL_UNSIGNED_NORMALIZED, true,  CheckSupport<&Extensions::sRGB>)));
+
     // Floating point formats have to query the renderer for support
     //                               | Internal format        |                              | R | G | B | A |S | Format          | Type                     | Comp    | SRGB | Supported                                                         |
     //                               |                        |                              |   |   |   |   |  |                 |                          | type    |      |                                                                   |
@@ -793,6 +811,8 @@
     map.insert(InternalFormatInfoPair(GL_BGRA_EXT,             InternalFormatInfo::UnsizedFormat(GL_BGRA_EXT,          CheckSupport<&Extensions::textureFormatBGRA8888>)));
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT,      InternalFormatInfo::UnsizedFormat(GL_DEPTH_COMPONENT,   AlwaysSupported                                 )));
     map.insert(InternalFormatInfoPair(GL_DEPTH_STENCIL_OES,    InternalFormatInfo::UnsizedFormat(GL_DEPTH_STENCIL_OES, CheckSupport<&Extensions::packedDepthStencil>   )));
+    map.insert(InternalFormatInfoPair(GL_SRGB_EXT,             InternalFormatInfo::UnsizedFormat(GL_RGB,               CheckSupport<&Extensions::sRGB>                 )));
+    map.insert(InternalFormatInfoPair(GL_SRGB_ALPHA_EXT,       InternalFormatInfo::UnsizedFormat(GL_RGBA,              CheckSupport<&Extensions::sRGB>                 )));
 
     // Luminance alpha formats from GL_EXT_texture_storage
     //                               | Internal format          |                              | L | A | Format             | Type            | Component type        | Supported                                                              |
diff --git a/src/libGLESv2/libGLESv2.cpp b/src/libGLESv2/libGLESv2.cpp
index 791332b..0c08ac8 100644
--- a/src/libGLESv2/libGLESv2.cpp
+++ b/src/libGLESv2/libGLESv2.cpp
@@ -2016,6 +2016,13 @@
                 return gl::error(GL_INVALID_OPERATION);
             }
 
+            // GL_EXT_sRGB does not support mipmap generation on sRGB textures
+            if (context->getClientVersion() == 2 &&
+                gl::GetColorEncoding(internalFormat, context->getClientVersion()) == GL_SRGB)
+            {
+                return gl::error(GL_INVALID_OPERATION);
+            }
+
             // Non-power of 2 ES2 check
             if (!context->getCaps().extensions.textureNPOT && (!gl::isPow2(texture->getBaseLevelWidth()) || !gl::isPow2(texture->getBaseLevelHeight())))
             {
@@ -2552,6 +2559,12 @@
               case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
               case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
                 break;
+              case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
+                if (clientVersion < 3 && !context->getCaps().extensions.sRGB)
+                {
+                    return gl::error(GL_INVALID_ENUM);
+                }
+                break;
               case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
               case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
               case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
@@ -2559,12 +2572,12 @@
               case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
               case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
               case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
-              case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
               case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
-                if (clientVersion >= 3)
+                if (clientVersion < 3)
                 {
-                    break;
+                    return gl::error(GL_INVALID_ENUM);
                 }
+                break;
               default:
                 return gl::error(GL_INVALID_ENUM);
             }
diff --git a/src/libGLESv2/renderer/Renderer.h b/src/libGLESv2/renderer/Renderer.h
index cb20bac..b50513c 100644
--- a/src/libGLESv2/renderer/Renderer.h
+++ b/src/libGLESv2/renderer/Renderer.h
@@ -169,6 +169,7 @@
     virtual bool getPostSubBufferSupport() const = 0;
     virtual int getMaxRecommendedElementsIndices() const = 0;
     virtual int getMaxRecommendedElementsVertices() const = 0;
+    virtual bool getSRGBTextureSupport() const = 0;
 
     virtual int getMajorShaderModel() const = 0;
     virtual float getMaxPointSize() const = 0;
diff --git a/src/libGLESv2/renderer/d3d11/Renderer11.cpp b/src/libGLESv2/renderer/d3d11/Renderer11.cpp
index a58822f..11033cb 100644
--- a/src/libGLESv2/renderer/d3d11/Renderer11.cpp
+++ b/src/libGLESv2/renderer/d3d11/Renderer11.cpp
@@ -2037,6 +2037,11 @@
     return std::numeric_limits<GLint>::max();
 }
 
+bool Renderer11::getSRGBTextureSupport() const
+{
+    return true;
+}
+
 int Renderer11::getMajorShaderModel() const
 {
     switch (mFeatureLevel)
diff --git a/src/libGLESv2/renderer/d3d11/Renderer11.h b/src/libGLESv2/renderer/d3d11/Renderer11.h
index e940599..490e190 100644
--- a/src/libGLESv2/renderer/d3d11/Renderer11.h
+++ b/src/libGLESv2/renderer/d3d11/Renderer11.h
@@ -123,6 +123,7 @@
     virtual bool getPostSubBufferSupport() const;
     virtual int getMaxRecommendedElementsIndices() const;
     virtual int getMaxRecommendedElementsVertices() const;
+    virtual bool getSRGBTextureSupport() const;
 
     virtual int getMajorShaderModel() const;
     virtual float getMaxPointSize() const;
diff --git a/src/libGLESv2/renderer/d3d11/formatutils11.cpp b/src/libGLESv2/renderer/d3d11/formatutils11.cpp
index 6252cce..0ad37a8 100644
--- a/src/libGLESv2/renderer/d3d11/formatutils11.cpp
+++ b/src/libGLESv2/renderer/d3d11/formatutils11.cpp
@@ -406,6 +406,10 @@
     map.insert(D3D11ES2FormatPair(GL_BGRA4_ANGLEX,                    D3D11ES2FormatInfo(DXGI_FORMAT_B8G8R8A8_UNORM,     DXGI_FORMAT_B8G8R8A8_UNORM,        DXGI_FORMAT_B8G8R8A8_UNORM,     DXGI_FORMAT_UNKNOWN,           loadRGBA4444DataToRGBA                   )));
     map.insert(D3D11ES2FormatPair(GL_BGR5_A1_ANGLEX,                  D3D11ES2FormatInfo(DXGI_FORMAT_B8G8R8A8_UNORM,     DXGI_FORMAT_B8G8R8A8_UNORM,        DXGI_FORMAT_B8G8R8A8_UNORM,     DXGI_FORMAT_UNKNOWN,           loadRGBA5551DataToRGBA                   )));
 
+    // From GL_EXT_sRGB
+    map.insert(D3D11ES2FormatPair(GL_SRGB8,                           D3D11ES2FormatInfo(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN,             DXGI_FORMAT_UNKNOWN,           loadToNative3To4<GLubyte, 0xFF>          )));
+    map.insert(D3D11ES2FormatPair(GL_SRGB8_ALPHA8,                    D3D11ES2FormatInfo(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_FORMAT_UNKNOWN,           loadToNative<GLubyte, 4>                 )));
+
     // From GL_EXT_texture_rg
     map.insert(D3D11ES2FormatPair(GL_R8_EXT,                          D3D11ES2FormatInfo(DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_R8_UNORM,              DXGI_FORMAT_R8_UNORM,           DXGI_FORMAT_UNKNOWN,           loadToNative<GLubyte, 1>                 )));
     map.insert(D3D11ES2FormatPair(GL_R32F_EXT,                        D3D11ES2FormatInfo(DXGI_FORMAT_R32_FLOAT,          DXGI_FORMAT_R32_FLOAT,             DXGI_FORMAT_R32_FLOAT,          DXGI_FORMAT_UNKNOWN,           loadToNative<GLfloat, 1>                 )));
diff --git a/src/libGLESv2/renderer/d3d9/Renderer9.cpp b/src/libGLESv2/renderer/d3d9/Renderer9.cpp
index 05916d8..46b7a72 100644
--- a/src/libGLESv2/renderer/d3d9/Renderer9.cpp
+++ b/src/libGLESv2/renderer/d3d9/Renderer9.cpp
@@ -2362,6 +2362,11 @@
     return 0;
 }
 
+bool Renderer9::getSRGBTextureSupport() const
+{
+    return false;
+}
+
 int Renderer9::getMajorShaderModel() const
 {
     return D3DSHADER_VERSION_MAJOR(mDeviceCaps.PixelShaderVersion);
diff --git a/src/libGLESv2/renderer/d3d9/Renderer9.h b/src/libGLESv2/renderer/d3d9/Renderer9.h
index 71fc69f..d7c32f2 100644
--- a/src/libGLESv2/renderer/d3d9/Renderer9.h
+++ b/src/libGLESv2/renderer/d3d9/Renderer9.h
@@ -124,6 +124,7 @@
     virtual bool getPostSubBufferSupport() const;
     virtual int getMaxRecommendedElementsIndices() const;
     virtual int getMaxRecommendedElementsVertices() const;
+    virtual bool getSRGBTextureSupport() const;
 
     virtual int getMajorShaderModel() const;
     virtual float getMaxPointSize() const;
diff --git a/src/libGLESv2/renderer/d3d9/formatutils9.cpp b/src/libGLESv2/renderer/d3d9/formatutils9.cpp
index 7eb25d8..752f196 100644
--- a/src/libGLESv2/renderer/d3d9/formatutils9.cpp
+++ b/src/libGLESv2/renderer/d3d9/formatutils9.cpp
@@ -740,7 +740,6 @@
     }
     else
     {
-        UNREACHABLE();
         return D3DFMT_UNKNOWN;
     }
 }
@@ -754,7 +753,6 @@
     }
     else
     {
-        UNREACHABLE();
         return D3DFMT_UNKNOWN;
     }
 }
diff --git a/src/libGLESv2/validationES2.cpp b/src/libGLESv2/validationES2.cpp
index 9c54ee9..f2ba5da 100644
--- a/src/libGLESv2/validationES2.cpp
+++ b/src/libGLESv2/validationES2.cpp
@@ -341,6 +341,20 @@
                 return gl::error(GL_INVALID_OPERATION, false);
             }
             break;
+          case GL_SRGB_EXT:
+          case GL_SRGB_ALPHA_EXT:
+            if (!context->getCaps().extensions.sRGB)
+            {
+                return gl::error(GL_INVALID_ENUM, false);
+            }
+            switch (type)
+            {
+              case GL_UNSIGNED_BYTE:
+                break;
+              default:
+                return gl::error(GL_INVALID_OPERATION, false);
+            }
+            break;
           case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:  // error cases for compressed textures are handled below
           case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
diff --git a/tests/angle_tests/SRGBTextureTest.cpp b/tests/angle_tests/SRGBTextureTest.cpp
new file mode 100644
index 0000000..090b2c2
--- /dev/null
+++ b/tests/angle_tests/SRGBTextureTest.cpp
@@ -0,0 +1,133 @@
+#include "ANGLETest.h"
+
+class SRGBTextureTest : public ANGLETest
+{
+protected:
+    SRGBTextureTest()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+    }
+
+    virtual void SetUp()
+    {
+        ANGLETest::SetUp();
+    }
+
+    virtual void TearDown()
+    {
+        ANGLETest::TearDown();
+    }
+};
+
+TEST_F(SRGBTextureTest, srgb_validation)
+{
+    bool supported = extensionEnabled("GL_EXT_sRGB") || getClientVersion() == 3;
+
+    GLuint tex = 0;
+    glGenTextures(1, &tex);
+    glBindTexture(GL_TEXTURE_2D, tex);
+
+    GLubyte pixel[3] = { 0 };
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, 1, 1, 0, GL_SRGB, GL_UNSIGNED_BYTE, pixel);
+    if (supported)
+    {
+        EXPECT_GL_NO_ERROR();
+
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_SRGB, GL_UNSIGNED_BYTE, pixel);
+        EXPECT_GL_NO_ERROR();
+
+        glGenerateMipmap(GL_TEXTURE_2D);
+        EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+    }
+    else
+    {
+        EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    }
+
+    glDeleteTextures(1, &tex);
+}
+
+TEST_F(SRGBTextureTest, srgba_validation)
+{
+    bool supported = extensionEnabled("GL_EXT_sRGB") || getClientVersion() == 3;
+
+    GLuint tex = 0;
+    glGenTextures(1, &tex);
+    glBindTexture(GL_TEXTURE_2D, tex);
+
+    GLubyte pixel[4] = { 0 };
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, pixel);
+    if (supported)
+    {
+        EXPECT_GL_NO_ERROR();
+
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, pixel);
+        EXPECT_GL_NO_ERROR();
+
+        glGenerateMipmap(GL_TEXTURE_2D);
+        if (getClientVersion() == 2)
+        {
+            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+        }
+        else
+        {
+            EXPECT_GL_NO_ERROR();
+        }
+    }
+    else
+    {
+        EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    }
+
+    glDeleteTextures(1, &tex);
+}
+
+TEST_F(SRGBTextureTest, srgba_renderbuffer)
+{
+    bool supported = extensionEnabled("GL_EXT_sRGB") || getClientVersion() == 3;
+
+    GLuint rbo = 0;
+    glGenRenderbuffers(1, &rbo);
+    glBindRenderbuffer(GL_RENDERBUFFER, rbo);
+
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8_ALPHA8_EXT, 1, 1);
+    if (supported)
+    {
+        EXPECT_GL_NO_ERROR();
+    }
+    else
+    {
+        EXPECT_GL_ERROR(GL_INVALID_ENUM);
+
+        // Make sure the rbo has a size for future tests
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, 1, 1);
+        EXPECT_GL_NO_ERROR();
+    }
+
+    GLuint fbo = 0;
+    glGenFramebuffers(1, &fbo);
+    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
+    EXPECT_GL_NO_ERROR();
+
+    GLint colorEncoding = 0;
+    glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                                          GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT, &colorEncoding);
+    if (supported)
+    {
+        EXPECT_GL_NO_ERROR();
+        EXPECT_EQ(GL_SRGB_EXT, colorEncoding);
+    }
+    else
+    {
+        EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    }
+
+    glDeleteFramebuffers(1, &fbo);
+    glDeleteRenderbuffers(1, &rbo);
+}