blob: 68fd9f7165ff237ed3c86a42be0b2a24dd9819c7 [file] [log] [blame]
//
// Copyright 2015 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
namespace angle
{
// These two colors are equivelent in different colorspaces
constexpr GLColor kLinearColor(64, 127, 191, 255);
constexpr GLColor kNonlinearColor(13, 54, 133, 255);
class SRGBTextureTest : public ANGLETest
{
protected:
SRGBTextureTest()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void testSetUp() override
{
constexpr char kVS[] =
"precision highp float;\n"
"attribute vec4 position;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position.xy, 0.0, 1.0);\n"
" texcoord = (position.xy * 0.5) + 0.5;\n"
"}\n";
constexpr char kFS[] =
"precision highp float;\n"
"uniform sampler2D tex;\n"
"varying vec2 texcoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
"}\n";
mProgram = CompileProgram(kVS, kFS);
ASSERT_NE(0u, mProgram);
mTextureLocation = glGetUniformLocation(mProgram, "tex");
ASSERT_NE(-1, mTextureLocation);
}
void testTearDown() override { glDeleteProgram(mProgram); }
GLenum getSRGBA8TextureInternalFormat() const
{
return getClientMajorVersion() >= 3 ? GL_SRGB8_ALPHA8 : GL_SRGB_ALPHA_EXT;
}
GLenum getSRGBA8TextureFormat() const
{
return getClientMajorVersion() >= 3 ? GL_RGBA : GL_SRGB_ALPHA_EXT;
}
GLenum getSRGB8TextureInternalFormat() const
{
return getClientMajorVersion() >= 3 ? GL_SRGB8 : GL_SRGB_EXT;
}
GLenum getSRGB8TextureFormat() const
{
return getClientMajorVersion() >= 3 ? GL_RGB : GL_SRGB_EXT;
}
GLuint mProgram = 0;
GLint mTextureLocation = -1;
};
class SRGBTextureTestES3 : public SRGBTextureTest
{};
// GenerateMipmaps should generate INVALID_OPERATION in ES 2.0 / WebGL 1.0 with EXT_sRGB.
// https://bugs.chromium.org/p/chromium/issues/detail?id=769989
TEST_P(SRGBTextureTest, SRGBValidation)
{
// TODO(fjhenigman): Figure out why this fails on Ozone Intel.
ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
bool supported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 3;
GLuint tex = 0;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte pixel[3] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, getSRGB8TextureInternalFormat(), 1, 1, 0,
getSRGB8TextureFormat(), GL_UNSIGNED_BYTE, pixel);
if (supported)
{
EXPECT_GL_NO_ERROR();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSRGB8TextureFormat(), GL_UNSIGNED_BYTE,
pixel);
EXPECT_GL_NO_ERROR();
// Mipmap generation always generates errors for SRGB unsized in ES2 or SRGB8 sized in ES3.
glGenerateMipmap(GL_TEXTURE_2D);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
glDeleteTextures(1, &tex);
}
TEST_P(SRGBTextureTest, SRGBAValidation)
{
// TODO(fjhenigman): Figure out why this fails on Ozone Intel.
ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
bool supported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 3;
GLuint tex = 0;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte pixel[4] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, pixel);
if (supported)
{
EXPECT_GL_NO_ERROR();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE,
pixel);
EXPECT_GL_NO_ERROR();
glGenerateMipmap(GL_TEXTURE_2D);
if (getClientMajorVersion() < 3)
{
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
EXPECT_GL_NO_ERROR();
}
}
else
{
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
glDeleteTextures(1, &tex);
}
// Test that sized SRGBA formats allow generating mipmaps
TEST_P(SRGBTextureTestES3, SRGBASizedValidation)
{
// TODO(fjhenigman): Figure out why this fails on Ozone Intel.
ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte pixel[4] = {0};
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, pixel);
EXPECT_GL_NO_ERROR();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
EXPECT_GL_NO_ERROR();
glGenerateMipmap(GL_TEXTURE_2D);
EXPECT_GL_NO_ERROR();
}
TEST_P(SRGBTextureTest, SRGBARenderbuffer)
{
bool supported = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() == 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);
}
// Verify that if the srgb decode extension is available, srgb textures are too
TEST_P(SRGBTextureTest, SRGBDecodeExtensionAvailability)
{
bool hasSRGBDecode = IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode");
if (hasSRGBDecode)
{
bool hasSRGBTextures = IsGLExtensionEnabled("GL_EXT_sRGB") || getClientMajorVersion() >= 3;
EXPECT_TRUE(hasSRGBTextures);
}
}
// Test basic functionality of SRGB decode using the texture parameter
TEST_P(SRGBTextureTest, SRGBDecodeTextureParameter)
{
// TODO(fjhenigman): Figure out why this fails on Ozone Intel.
ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel() && IsOpenGLES());
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test basic functionality of SRGB override using the texture parameter
TEST_P(SRGBTextureTest, SRGBOverrideTextureParameter)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLenum internalFormat = getClientMajorVersion() >= 3 ? GL_RGBA8 : GL_RGBA;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
&linearColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
}
// Test that all supported formats can be overridden
TEST_P(SRGBTextureTestES3, SRGBOverrideFormats)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
constexpr GLenum possibleFormats[] = {GL_RGB8,
GL_RGBA8,
GL_COMPRESSED_RGB8_ETC2,
GL_COMPRESSED_RGBA8_ETC2_EAC,
GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2,
GL_COMPRESSED_RGBA_ASTC_4x4,
GL_COMPRESSED_RGBA_ASTC_5x4,
GL_COMPRESSED_RGBA_ASTC_5x5,
GL_COMPRESSED_RGBA_ASTC_6x5,
GL_COMPRESSED_RGBA_ASTC_6x6,
GL_COMPRESSED_RGBA_ASTC_8x5,
GL_COMPRESSED_RGBA_ASTC_8x6,
GL_COMPRESSED_RGBA_ASTC_8x8,
GL_COMPRESSED_RGBA_ASTC_10x5,
GL_COMPRESSED_RGBA_ASTC_10x6,
GL_COMPRESSED_RGBA_ASTC_10x8,
GL_COMPRESSED_RGBA_ASTC_10x10,
GL_COMPRESSED_RGBA_ASTC_12x10,
GL_COMPRESSED_RGBA_ASTC_12x12,
GL_COMPRESSED_RGB_S3TC_DXT1_EXT,
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
GL_R8,
GL_RG8,
GL_COMPRESSED_RGBA_BPTC_UNORM_EXT};
for (GLenum format : possibleFormats)
{
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexStorage2D(GL_TEXTURE_2D, 1, format, 1, 1);
GLenum error = glGetError();
if (error == GL_INVALID_ENUM)
{
// Format is not supported, we don't require the sRGB counterpart to be supported either
continue;
}
else
{
ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), error);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_NO_ERROR();
// Discard result, we are only checking that we don't try to reinterpret to an unsupported
// format
}
}
// Test interaction between sRGB_override and sampler objects
TEST_P(SRGBTextureTestES3, SRGBOverrideTextureParameterWithSampler)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLenum internalFormat = getClientMajorVersion() >= 3 ? GL_RGBA8 : GL_RGBA;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
&linearColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that SRGB override is a noop when used on a nonlinear texture format
// EXT_texture_format_sRGB_override spec says:
// "If the internal format is not one of the above formats, then
// the value of TEXTURE_FORMAT_SRGB_OVERRIDE_EXT is ignored."
TEST_P(SRGBTextureTestES3, SRGBOverrideTextureParameterNoop)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_NONE);
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
}
// Test basic functionality of SRGB decode using the sampler parameter
TEST_P(SRGBTextureTestES3, SRGBDecodeSamplerParameter)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Toggle between GL_DECODE_EXT and GL_SKIP_DECODE_EXT of sampler parameter
// GL_TEXTURE_SRGB_DECODE_EXT
TEST_P(SRGBTextureTestES3, SRGBDecodeSamplerParameterToggle)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
for (int i = 0; i < 4; i++)
{
// Toggle betwee decode and skip decode and verify pixel value
GLint decode = ((i & 1) == 0) ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT;
angle::GLColor &expectedColor = ((i & 1) == 0) ? srgbColor : linearColor;
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, decode);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColor, 1.0);
}
}
// Test that sampler state overrides texture state for srgb decode
TEST_P(SRGBTextureTestES3, SRGBDecodeTextureAndSamplerParameter)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"));
GLColor linearColor = kLinearColor;
GLColor srgbColor = kNonlinearColor;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, getSRGBA8TextureInternalFormat(), 1, 1, 0,
getSRGBA8TextureFormat(), GL_UNSIGNED_BYTE, &linearColor);
ASSERT_GL_NO_ERROR();
GLSampler sampler;
glBindSampler(0, sampler.get());
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that srgb decode state takes priority over srgb override state
TEST_P(SRGBTextureTestES3, SRGBDecodeOverridePriority)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_override"));
GLColor linearColor = kLinearColor;
GLenum internalFormat = getClientMajorVersion() >= 3 ? GL_RGBA8 : GL_RGBA;
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
&linearColor);
ASSERT_GL_NO_ERROR();
glUseProgram(mProgram);
glUniform1i(mTextureLocation, 0);
glDisable(GL_DEPTH_TEST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT, GL_SRGB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
drawQuad(mProgram, "position", 0.5f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, linearColor, 1.0);
}
// Test that mipmaps are generated correctly for sRGB textures
TEST_P(SRGBTextureTestES3, GenerateMipmaps)
{
ANGLE_SKIP_TEST_IF(IsOpenGL() && ((IsIntel() && IsOSX()) || IsAMD()));
// http://anglebug.com/5108
ANGLE_SKIP_TEST_IF(IsMetal());
auto createAndReadBackTexture = [this](GLenum internalFormat, const GLColor &color) {
constexpr GLsizei width = 128;
constexpr GLsizei height = 128;
std::array<GLColor, width * height> buf;
std::fill(buf.begin(), buf.end(), color);
// Set up-left region of the texture as red color.
// In order to make sure bi-linear interpolation operates on different colors, red region
// is 1 pixel smaller than a quarter of the full texture on each side.
constexpr GLsizei redWidth = width / 2 - 1;
constexpr GLsizei redHeight = height / 2 - 1;
std::array<GLColor, redWidth * redHeight> redBuf;
std::fill(redBuf.begin(), redBuf.end(), GLColor::red);
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
buf.data());
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, redWidth, redHeight, GL_RGBA, GL_UNSIGNED_BYTE,
redBuf.data());
glGenerateMipmap(GL_TEXTURE_2D);
constexpr GLsizei drawWidth = 32;
constexpr GLsizei drawHeight = 32;
glViewport(0, 0, drawWidth, drawHeight);
drawQuad(mProgram, "position", 0.5f);
std::array<GLColor, drawWidth * drawHeight> result;
glReadPixels(0, 0, drawWidth, drawHeight, GL_RGBA, GL_UNSIGNED_BYTE, result.data());
EXPECT_GL_NO_ERROR();
return result;
};
GLColor srgbaColor(0, 63, 127, 255);
auto srgbaReadback = createAndReadBackTexture(GL_SRGB8_ALPHA8, srgbaColor);
GLColor linearColor(0, 13, 54, 255);
auto rgbaReadback = createAndReadBackTexture(GL_RGBA8, linearColor);
ASSERT_EQ(srgbaReadback.size(), rgbaReadback.size());
for (size_t i = 0; i < srgbaReadback.size(); i++)
{
constexpr double tolerence = 7.0;
EXPECT_COLOR_NEAR(srgbaReadback[i], rgbaReadback[i], tolerence);
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(SRGBTextureTest);
ANGLE_INSTANTIATE_TEST_ES3(SRGBTextureTestES3);
} // namespace angle