blob: cbdc492b90d7c147397498cbca592c6b852c01ac [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
{
class SRGBTextureTest : public ANGLETest
{
protected:
SRGBTextureTest()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void SetUp() override
{
ANGLETest::SetUp();
const std::string vs =
"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";
const std::string fs =
"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(vs, fs);
ASSERT_NE(0u, mProgram);
mTextureLocation = glGetUniformLocation(mProgram, "tex");
ASSERT_NE(-1, mTextureLocation);
}
void TearDown() override
{
glDeleteProgram(mProgram);
ANGLETest::TearDown();
}
GLuint mProgram = 0;
GLint mTextureLocation = -1;
};
TEST_P(SRGBTextureTest, SRGBValidation)
{
bool supported = extensionEnabled("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, 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_P(SRGBTextureTest, SRGBAValidation)
{
bool supported = extensionEnabled("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, 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 (getClientMajorVersion() == 2)
{
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
EXPECT_GL_NO_ERROR();
}
}
else
{
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
glDeleteTextures(1, &tex);
}
TEST_P(SRGBTextureTest, SRGBARenderbuffer)
{
bool supported = extensionEnabled("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 = extensionEnabled("GL_EXT_texture_sRGB_decode");
if (hasSRGBDecode)
{
bool hasSRGBTextures = extensionEnabled("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.
if (IsOzone() && IsIntel() && IsOpenGLES())
{
std::cout << "Test skipped on Ozone Intel." << std::endl;
return;
}
if (!extensionEnabled("GL_EXT_texture_sRGB_decode"))
{
std::cout << "Test skipped because GL_EXT_texture_sRGB_decode is not available."
<< std::endl;
return;
}
GLColor linearColor(64, 127, 191, 255);
GLColor srgbColor(13, 54, 133, 255);
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, 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 decode using the sampler parameter
TEST_P(SRGBTextureTest, SRGBDecodeSamplerParameter)
{
if (!extensionEnabled("GL_EXT_texture_sRGB_decode") || getClientMajorVersion() < 3)
{
std::cout << "Test skipped because GL_EXT_texture_sRGB_decode or ES3 is not available."
<< std::endl;
return;
}
GLColor linearColor(64, 127, 191, 255);
GLColor srgbColor(13, 54, 133, 255);
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex.get());
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT, 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);
}
// Test that mipmaps are generated correctly for sRGB textures
TEST_P(SRGBTextureTest, GenerateMipmaps)
{
if (getClientMajorVersion() < 3)
{
std::cout << "Test skipped because ES3 is not available." << std::endl;
return;
}
if (IsOpenGL() && (IsIntel() || IsAMD()))
{
std::cout << "Test skipped on Intel and AMD OpenGL drivers." << std::endl;
return;
}
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(SRGBTextureTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL(),
ES2_OPENGLES(),
ES3_OPENGLES());
} // namespace