| // |
| // 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 |