| // |
| // 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 <vector> |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| class IncompleteTextureTest : public ANGLETest |
| { |
| protected: |
| IncompleteTextureTest() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void testSetUp() override |
| { |
| constexpr char kVS[] = R"(precision highp float; |
| attribute vec4 position; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_Position = position; |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| |
| constexpr char kFS[] = R"(precision highp float; |
| uniform sampler2D tex; |
| varying vec2 texcoord; |
| |
| void main() |
| { |
| gl_FragColor = texture2D(tex, texcoord); |
| })"; |
| |
| mProgram = CompileProgram(kVS, kFS); |
| if (mProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| mTextureUniformLocation = glGetUniformLocation(mProgram, "tex"); |
| } |
| |
| void testTearDown() override { glDeleteProgram(mProgram); } |
| |
| GLuint mProgram; |
| GLint mTextureUniformLocation; |
| }; |
| |
| class IncompleteTextureTestES3 : public ANGLETest |
| { |
| protected: |
| IncompleteTextureTestES3() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| public: |
| void setupFramebuffer(const GLenum sizedInternalFormat) |
| { |
| glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); |
| glRenderbufferStorage(GL_RENDERBUFFER, sizedInternalFormat, getWindowWidth(), |
| getWindowHeight()); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| mRenderbuffer); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| private: |
| GLRenderbuffer mRenderbuffer; |
| GLFramebuffer mFramebuffer; |
| }; |
| |
| class IncompleteTextureTestES31 : public ANGLETest |
| { |
| protected: |
| IncompleteTextureTestES31() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| }; |
| |
| // Test rendering with an incomplete texture. |
| TEST_P(IncompleteTextureTest, IncompleteTexture2D) |
| { |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| |
| glUseProgram(mProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| constexpr GLsizei kTextureSize = 2; |
| std::vector<GLColor> textureData(kTextureSize * kTextureSize, GLColor::red); |
| |
| // Make a complete texture. |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, textureData.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| |
| // Should be complete - expect red. |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red) << "complete texture should be red"; |
| |
| // Make texture incomplete. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
| |
| // Should be incomplete - expect black. |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black) << "incomplete texture should be black"; |
| |
| // Make texture complete by defining the second mip. |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, kTextureSize >> 1, kTextureSize >> 1, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, textureData.data()); |
| |
| // Should be complete - expect red. |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red) << "mip-complete texture should be red"; |
| } |
| |
| // Tests redefining a texture with half the size works as expected. |
| TEST_P(IncompleteTextureTest, UpdateTexture) |
| { |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| |
| glUseProgram(mProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| constexpr GLsizei redTextureSize = 64; |
| std::vector<GLColor> redTextureData(redTextureSize * redTextureSize, GLColor::red); |
| for (GLint mip = 0; mip < 7; ++mip) |
| { |
| const GLsizei mipSize = redTextureSize >> mip; |
| |
| glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, mipSize, mipSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| redTextureData.data()); |
| } |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| constexpr GLsizei greenTextureSize = 32; |
| std::vector<GLColor> greenTextureData(greenTextureSize * greenTextureSize, GLColor::green); |
| |
| for (GLint mip = 0; mip < 6; ++mip) |
| { |
| const GLsizei mipSize = greenTextureSize >> mip; |
| |
| glTexSubImage2D(GL_TEXTURE_2D, mip, mipSize, mipSize, mipSize, mipSize, GL_RGBA, |
| GL_UNSIGNED_BYTE, greenTextureData.data()); |
| } |
| |
| drawQuad(mProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - greenTextureSize, getWindowHeight() - greenTextureSize, |
| GLColor::green); |
| } |
| |
| // Tests that incomplete textures don't get initialized with the unpack buffer contents. |
| TEST_P(IncompleteTextureTestES3, UnpackBufferBound) |
| { |
| std::vector<GLColor> red(16, GLColor::red); |
| |
| GLBuffer unpackBuffer; |
| glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer); |
| glBufferData(GL_PIXEL_UNPACK_BUFFER, red.size() * sizeof(GLColor), red.data(), GL_STATIC_DRAW); |
| |
| draw2DTexturedQuad(0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| } |
| |
| // Tests that the incomplete multisample texture has the correct alpha value. |
| TEST_P(IncompleteTextureTestES31, MultisampleTexture) |
| { |
| constexpr char kVS[] = R"(#version 310 es |
| in vec2 position; |
| out vec2 texCoord; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| texCoord = (position * 0.5) + 0.5; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 310 es |
| precision mediump float; |
| in vec2 texCoord; |
| out vec4 color; |
| uniform mediump sampler2DMS tex; |
| void main() |
| { |
| ivec2 texSize = textureSize(tex); |
| ivec2 texel = ivec2(vec2(texSize) * texCoord); |
| color = texelFetch(tex, texel, 0); |
| })"; |
| |
| glClearColor(1.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // The zero texture will be incomplete by default. |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| drawQuad(program, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black); |
| } |
| |
| // This mirrors a scenario seen in GFXBench Car Chase where a |
| // default CUBE_MAP_ARRAY texture is used without being setup. |
| // Its ends up sampling from an incomplete texture. |
| TEST_P(IncompleteTextureTestES31, IncompleteTextureCubeMapArray) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_cube_map_array")); |
| |
| constexpr char kVS[] = |
| R"(#version 310 es |
| precision mediump float; |
| in vec3 pos; |
| void main() { |
| gl_Position = vec4(pos, 1.0); |
| })"; |
| |
| constexpr char kFS[] = |
| R"(#version 310 es |
| #extension GL_EXT_texture_cube_map_array : enable |
| precision mediump float; |
| out vec4 color; |
| uniform lowp samplerCubeArray uTex; |
| void main(){ |
| vec4 outColor = vec4(0.0); |
| |
| // Pull a color from each cube face to ensure they are all initialized |
| outColor += texture(uTex, vec4(1.0, 0.0, 0.0, 0.0)); |
| outColor += texture(uTex, vec4(-1.0, 0.0, 0.0, 0.0)); |
| outColor += texture(uTex, vec4(0.0, 1.0, 0.0, 0.0)); |
| outColor += texture(uTex, vec4(0.0, -1.0, 0.0, 0.0)); |
| outColor += texture(uTex, vec4(0.0, 0.0, 1.0, 0.0)); |
| outColor += texture(uTex, vec4(0.0, 0.0, -1.0, 0.0)); |
| |
| color = outColor; |
| })"; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| glUseProgram(program); |
| |
| glUniform1i(glGetUniformLocation(program, "uTex"), 0); |
| glActiveTexture(GL_TEXTURE0); |
| |
| // Bind the default texture and don't set it up. This ends up being incomplete. |
| glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, 0); |
| |
| drawQuad(program, "pos", 0.5f, 1.0f, true); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_COLOR_EQ(0, 0, angle::GLColor::black); |
| } |
| |
| // Verifies that an incomplete integer texture has a signed integer type default value. |
| TEST_P(IncompleteTextureTestES3, IntegerType) |
| { |
| // GLES backend on Adreno has a problem to create a incomplete texture, although it doesn't go |
| // through the routine which creates a incomplete texture in the ANGLE driver. |
| ANGLE_SKIP_TEST_IF(IsAdreno() && IsAndroid() && IsOpenGLES()); |
| |
| // http://crbug.com/1168370 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsOpenGL()); |
| |
| constexpr char kVS[] = R"(#version 300 es |
| in highp vec2 position; |
| out highp vec2 texCoord; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| texCoord = (position * 0.5) + 0.5; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| in highp vec2 texCoord; |
| out highp ivec4 color; |
| uniform highp isampler2D tex; |
| void main() |
| { |
| ivec2 texSize = textureSize(tex, 0); |
| ivec2 texel = ivec2(vec2(texSize) * texCoord); |
| color = texelFetch(tex, texel, 0); |
| })"; |
| |
| constexpr GLint clearColori[4] = {-10, 20, -30, 40}; |
| constexpr GLint blackColori[4] = {0, 0, 0, 127}; |
| |
| setupFramebuffer(GL_RGBA8I); |
| glClearBufferiv(GL_COLOR, 0, clearColori); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_8I(0, 0, clearColori[0], clearColori[1], clearColori[2], clearColori[3]); |
| |
| // Since no texture attachment has been specified, it is incomplete by definition |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glUseProgram(program); |
| |
| drawQuad(program, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| const int width = getWindowWidth() - 1; |
| const int height = getWindowHeight() - 1; |
| EXPECT_PIXEL_8I(0, 0, blackColori[0], blackColori[1], blackColori[2], blackColori[3]); |
| EXPECT_PIXEL_8I(width, 0, blackColori[0], blackColori[1], blackColori[2], blackColori[3]); |
| EXPECT_PIXEL_8I(0, height, blackColori[0], blackColori[1], blackColori[2], blackColori[3]); |
| EXPECT_PIXEL_8I(width, height, blackColori[0], blackColori[1], blackColori[2], blackColori[3]); |
| } |
| |
| // Verifies that an incomplete unsigned integer texture has an unsigned integer type default value. |
| TEST_P(IncompleteTextureTestES3, UnsignedIntegerType) |
| { |
| // GLES backend on Adreno has a problem to create a incomplete texture, although it doesn't go |
| // through the routine which creates a incomplete texture in the ANGLE driver. |
| ANGLE_SKIP_TEST_IF(IsAdreno() && IsAndroid() && IsOpenGLES()); |
| |
| // http://crbug.com/1168370 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsOpenGL()); |
| |
| constexpr char kVS[] = R"(#version 300 es |
| in highp vec2 position; |
| out highp vec2 texCoord; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| texCoord = (position * 0.5) + 0.5; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| in highp vec2 texCoord; |
| out highp uvec4 color; |
| uniform highp usampler2D tex; |
| void main() |
| { |
| ivec2 texSize = textureSize(tex, 0); |
| ivec2 texel = ivec2(vec2(texSize) * texCoord); |
| color = texelFetch(tex, texel, 0); |
| })"; |
| |
| constexpr GLuint clearColorui[4] = {40, 30, 20, 10}; |
| constexpr GLuint blackColorui[4] = {0, 0, 0, 255}; |
| |
| setupFramebuffer(GL_RGBA8UI); |
| glClearBufferuiv(GL_COLOR, 0, clearColorui); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_8UI(0, 0, clearColorui[0], clearColorui[1], clearColorui[2], clearColorui[3]); |
| |
| // Since no texture attachment has been specified, it is incomplete by definition |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glUseProgram(program); |
| |
| drawQuad(program, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| const int width = getWindowWidth() - 1; |
| const int height = getWindowHeight() - 1; |
| EXPECT_PIXEL_8UI(0, 0, blackColorui[0], blackColorui[1], blackColorui[2], blackColorui[3]); |
| EXPECT_PIXEL_8UI(width, 0, blackColorui[0], blackColorui[1], blackColorui[2], blackColorui[3]); |
| EXPECT_PIXEL_8UI(0, height, blackColorui[0], blackColorui[1], blackColorui[2], blackColorui[3]); |
| EXPECT_PIXEL_8UI(width, height, blackColorui[0], blackColorui[1], blackColorui[2], |
| blackColorui[3]); |
| } |
| |
| // Verifies that we are able to create an incomplete shadow texture. |
| TEST_P(IncompleteTextureTestES3, ShadowType) |
| { |
| // GLES backend on Adreno has a problem to create a incomplete texture, although it doesn't go |
| // through the routine which creates a incomplete texture in the ANGLE driver. |
| ANGLE_SKIP_TEST_IF(IsAdreno() && IsAndroid() && IsOpenGLES()); |
| |
| // http://crbug.com/1168370 |
| ANGLE_SKIP_TEST_IF(IsOSX() && IsARM64() && IsOpenGL()); |
| |
| // http://anglebug.com/5594 |
| ANGLE_SKIP_TEST_IF(IsD3D11() || IsOSX()); |
| |
| constexpr char kVS[] = R"(#version 300 es |
| in highp vec2 position; |
| out highp vec3 texCoord; |
| void main() |
| { |
| gl_Position = vec4(position, 0, 1); |
| texCoord = vec3(((position * 0.5) + 0.5), 0.5); |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| in highp vec3 texCoord; |
| out highp vec4 color; |
| uniform highp sampler2DShadow tex; |
| void main() |
| { |
| color = vec4(vec3(texture(tex, texCoord)), 1.0f); |
| })"; |
| |
| constexpr GLColor clearColor = {10, 40, 20, 30}; |
| constexpr GLColor blackColor = {0, 0, 0, 255}; |
| |
| setupFramebuffer(GL_RGBA8); |
| glClearBufferfv(GL_COLOR, 0, clearColor.toNormalizedVector().data()); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_EQ(0, 0, clearColor[0], clearColor[1], clearColor[2], clearColor[3]); |
| |
| // Since no texture attachment has been specified, it is incomplete by definition |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glUseProgram(program); |
| glUniform1i(glGetUniformLocation(program, "tex"), 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(program, "position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| |
| const int width = getWindowWidth() - 1; |
| const int height = getWindowHeight() - 1; |
| EXPECT_PIXEL_EQ(0, 0, blackColor[0], blackColor[1], blackColor[2], blackColor[3]); |
| EXPECT_PIXEL_EQ(width, 0, blackColor[0], blackColor[1], blackColor[2], blackColor[3]); |
| EXPECT_PIXEL_EQ(0, height, blackColor[0], blackColor[1], blackColor[2], blackColor[3]); |
| EXPECT_PIXEL_EQ(width, height, blackColor[0], blackColor[1], blackColor[2], blackColor[3]); |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES2(IncompleteTextureTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IncompleteTextureTestES3); |
| ANGLE_INSTANTIATE_TEST_ES3(IncompleteTextureTestES3); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IncompleteTextureTestES31); |
| ANGLE_INSTANTIATE_TEST_ES31(IncompleteTextureTestES31); |