| // |
| // 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" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| void TexImageCubeMapFaces(GLint level, |
| GLenum internalformat, |
| GLsizei width, |
| GLenum format, |
| GLenum type, |
| void *pixels) |
| { |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internalformat, width, width, 0, format, |
| type, pixels); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internalformat, width, width, 0, format, |
| type, pixels); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internalformat, width, width, 0, format, |
| type, pixels); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internalformat, width, width, 0, format, |
| type, pixels); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internalformat, width, width, 0, format, |
| type, pixels); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internalformat, width, width, 0, format, |
| type, pixels); |
| } |
| |
| class BaseMipmapTest : public ANGLETest |
| { |
| protected: |
| void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight) |
| { |
| glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glViewport(0, 0, viewportWidth, viewportHeight); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(program, "position", 0.0f); |
| } |
| }; |
| |
| } // namespace |
| |
| class MipmapTest : public BaseMipmapTest |
| { |
| protected: |
| MipmapTest() |
| : m2DProgram(0), |
| mCubeProgram(0), |
| mTexture2D(0), |
| mTextureCube(0), |
| mLevelZeroBlueInitData(), |
| mLevelZeroWhiteInitData(), |
| mLevelOneGreenInitData(), |
| mLevelTwoRedInitData(), |
| mOffscreenFramebuffer(0) |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| void setUp2DProgram() |
| { |
| // Vertex Shader source |
| constexpr char kVS[] = R"(attribute vec4 position; |
| varying vec2 vTexCoord; |
| |
| void main() |
| { |
| gl_Position = position; |
| vTexCoord = (position.xy * 0.5) + 0.5; |
| })"; |
| |
| // Fragment Shader source |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform sampler2D uTexture; |
| varying vec2 vTexCoord; |
| |
| void main() |
| { |
| gl_FragColor = texture2D(uTexture, vTexCoord); |
| })"; |
| |
| m2DProgram = CompileProgram(kVS, kFS); |
| ASSERT_NE(0u, m2DProgram); |
| } |
| |
| void setUpCubeProgram() |
| { |
| // A simple vertex shader for the texture cube |
| constexpr char kVS[] = R"(attribute vec4 position; |
| varying vec4 vPosition; |
| void main() |
| { |
| gl_Position = position; |
| vPosition = position; |
| })"; |
| |
| // A very simple fragment shader to sample from the negative-Y face of a texture cube. |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform samplerCube uTexture; |
| varying vec4 vPosition; |
| |
| void main() |
| { |
| gl_FragColor = textureCube(uTexture, vec3(vPosition.x, -1, vPosition.y)); |
| })"; |
| |
| mCubeProgram = CompileProgram(kVS, kFS); |
| ASSERT_NE(0u, mCubeProgram); |
| } |
| |
| void testSetUp() override |
| { |
| setUp2DProgram(); |
| |
| setUpCubeProgram(); |
| |
| mLevelZeroBlueInitData = |
| createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255); // Blue |
| mLevelZeroWhiteInitData = |
| createRGBInitData(getWindowWidth(), getWindowHeight(), 255, 255, 255); // White |
| mLevelOneGreenInitData = |
| createRGBInitData((getWindowWidth() / 2), (getWindowHeight() / 2), 0, 255, 0); // Green |
| mLevelTwoRedInitData = |
| createRGBInitData((getWindowWidth() / 4), (getWindowHeight() / 4), 255, 0, 0); // Red |
| |
| glGenFramebuffers(1, &mOffscreenFramebuffer); |
| glGenTextures(1, &mTexture2D); |
| |
| // Initialize the texture2D to be empty, and don't use mips. |
| glBindTexture(GL_TEXTURE_2D, mTexture2D); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, |
| GL_UNSIGNED_BYTE, nullptr); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| |
| ASSERT_EQ(getWindowWidth(), getWindowHeight()); |
| |
| // Create a non-mipped texture cube. Set the negative-Y face to be blue. |
| glGenTextures(1, &mTextureCube); |
| glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); |
| TexImageCubeMapFaces(0, GL_RGB, getWindowWidth(), GL_RGB, GL_UNSIGNED_BYTE, nullptr); |
| glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, getWindowWidth(), getWindowWidth(), |
| 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData.data()); |
| |
| // Complete the texture cube without mipmaps to start with. |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(m2DProgram); |
| glDeleteProgram(mCubeProgram); |
| glDeleteFramebuffers(1, &mOffscreenFramebuffer); |
| glDeleteTextures(1, &mTexture2D); |
| glDeleteTextures(1, &mTextureCube); |
| } |
| |
| std::vector<GLubyte> createRGBInitData(GLint width, GLint height, GLint r, GLint g, GLint b) |
| { |
| std::vector<GLubyte> data(3 * width * height); |
| |
| for (int i = 0; i < width * height; i += 1) |
| { |
| data[3 * i + 0] = static_cast<GLubyte>(r); |
| data[3 * i + 1] = static_cast<GLubyte>(g); |
| data[3 * i + 2] = static_cast<GLubyte>(b); |
| } |
| |
| return data; |
| } |
| |
| void clearTextureLevel0(GLenum textarget, |
| GLuint texture, |
| GLfloat red, |
| GLfloat green, |
| GLfloat blue, |
| GLfloat alpha) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textarget, texture, 0); |
| ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| glClearColor(red, green, blue, alpha); |
| glClear(GL_COLOR_BUFFER_BIT); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| } |
| |
| GLuint m2DProgram; |
| GLuint mCubeProgram; |
| GLuint mTexture2D; |
| GLuint mTextureCube; |
| |
| std::vector<GLubyte> mLevelZeroBlueInitData; |
| std::vector<GLubyte> mLevelZeroWhiteInitData; |
| std::vector<GLubyte> mLevelOneGreenInitData; |
| std::vector<GLubyte> mLevelTwoRedInitData; |
| |
| private: |
| GLuint mOffscreenFramebuffer; |
| }; |
| |
| class MipmapTestES3 : public BaseMipmapTest |
| { |
| protected: |
| MipmapTestES3() |
| : mTexture(0), |
| mArrayProgram(0), |
| mTextureArraySliceUniformLocation(-1), |
| m3DProgram(0), |
| mTexture3DSliceUniformLocation(-1), |
| mTexture3DLODUniformLocation(-1), |
| m2DProgram(0) |
| |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| const char *vertexShaderSource() |
| { |
| // Don't put "#version ..." on its own line. See [cpp]p1: |
| // "If there are sequences of preprocessing tokens within the list of arguments that |
| // would otherwise act as preprocessing directives, the behavior is undefined" |
| return |
| R"(#version 300 es |
| precision highp float; |
| in vec4 position; |
| out vec2 texcoord; |
| |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| } |
| |
| void setUpArrayProgram() |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| uniform highp sampler2DArray tex; |
| uniform int slice; |
| in vec2 texcoord; |
| out vec4 out_FragColor; |
| |
| void main() |
| { |
| out_FragColor = texture(tex, vec3(texcoord, float(slice))); |
| })"; |
| |
| mArrayProgram = CompileProgram(vertexShaderSource(), kFS); |
| if (mArrayProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| mTextureArraySliceUniformLocation = glGetUniformLocation(mArrayProgram, "slice"); |
| ASSERT_NE(-1, mTextureArraySliceUniformLocation); |
| |
| glUseProgram(mArrayProgram); |
| glUseProgram(0); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void setUp3DProgram() |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| uniform highp sampler3D tex; |
| uniform float slice; |
| uniform float lod; |
| in vec2 texcoord; |
| out vec4 out_FragColor; |
| |
| void main() |
| { |
| out_FragColor = textureLod(tex, vec3(texcoord, slice), lod); |
| })"; |
| |
| m3DProgram = CompileProgram(vertexShaderSource(), kFS); |
| if (m3DProgram == 0) |
| { |
| FAIL() << "shader compilation failed."; |
| } |
| |
| mTexture3DSliceUniformLocation = glGetUniformLocation(m3DProgram, "slice"); |
| ASSERT_NE(-1, mTexture3DSliceUniformLocation); |
| |
| mTexture3DLODUniformLocation = glGetUniformLocation(m3DProgram, "lod"); |
| ASSERT_NE(-1, mTexture3DLODUniformLocation); |
| |
| glUseProgram(m3DProgram); |
| glUniform1f(mTexture3DLODUniformLocation, 0); |
| glUseProgram(0); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void setUp2DProgram() |
| { |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| uniform highp sampler2D tex; |
| in vec2 texcoord; |
| out vec4 out_FragColor; |
| |
| void main() |
| { |
| out_FragColor = texture(tex, texcoord); |
| })"; |
| |
| m2DProgram = CompileProgram(vertexShaderSource(), kFS); |
| ASSERT_NE(0u, m2DProgram); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void setUpCubeProgram() |
| { |
| // A very simple fragment shader to sample from the negative-Y face of a texture cube. |
| constexpr char kFS[] = R"(#version 300 es |
| precision mediump float; |
| uniform samplerCube uTexture; |
| in vec2 texcoord; |
| out vec4 out_FragColor; |
| |
| void main() |
| { |
| out_FragColor = texture(uTexture, vec3(texcoord.x, -1, texcoord.y)); |
| })"; |
| |
| mCubeProgram = CompileProgram(vertexShaderSource(), kFS); |
| ASSERT_NE(0u, mCubeProgram); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testSetUp() override |
| { |
| glGenTextures(1, &mTexture); |
| ASSERT_GL_NO_ERROR(); |
| |
| setUpArrayProgram(); |
| setUp3DProgram(); |
| setUp2DProgram(); |
| setUpCubeProgram(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteTextures(1, &mTexture); |
| |
| glDeleteProgram(mArrayProgram); |
| glDeleteProgram(m3DProgram); |
| glDeleteProgram(m2DProgram); |
| glDeleteProgram(mCubeProgram); |
| } |
| |
| GLuint mTexture; |
| |
| GLuint mArrayProgram; |
| GLint mTextureArraySliceUniformLocation; |
| |
| GLuint m3DProgram; |
| GLint mTexture3DSliceUniformLocation; |
| GLint mTexture3DLODUniformLocation; |
| |
| GLuint m2DProgram; |
| |
| GLuint mCubeProgram; |
| }; |
| |
| class MipmapTestES31 : public BaseMipmapTest |
| { |
| protected: |
| MipmapTestES31() |
| |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| }; |
| // This test uses init data for the first three levels of the texture. It passes the level 0 data |
| // in, then renders, then level 1, then renders, etc. This ensures that renderers using the zero LOD |
| // workaround (e.g. D3D11 FL9_3) correctly pass init data to the mipmapped texture, even if the the |
| // zero-LOD texture is currently in use. |
| TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData) |
| { |
| // Pass in level zero init data. |
| glBindTexture(GL_TEXTURE_2D, mTexture2D); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, |
| GL_UNSIGNED_BYTE, mLevelZeroBlueInitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Disable mips. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| |
| // Draw a full-sized quad, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Draw a half-sized quad, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Draw a quarter-sized quad, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| |
| // Complete the texture by initializing the remaining levels. |
| int n = 1; |
| while (getWindowWidth() / (1U << n) >= 1) |
| { |
| glTexImage2D(GL_TEXTURE_2D, n, GL_RGB, getWindowWidth() / (1U << n), |
| getWindowWidth() / (1U << n), 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| n += 1; |
| } |
| |
| // Pass in level one init data. |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, getWindowWidth() / 2, getWindowHeight() / 2, 0, GL_RGB, |
| GL_UNSIGNED_BYTE, mLevelOneGreenInitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw a full-sized quad, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Draw a half-sized quad, and check it's blue. We've not enabled mipmaps yet, so our init data |
| // for level one shouldn't be used. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Enable mipmaps. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| |
| // Draw a half-sized quad, and check it's green. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green); |
| |
| // Draw a quarter-sized quad, and check it's black, since we've not passed any init data for |
| // level two. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::black); |
| |
| // Pass in level two init data. |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, getWindowWidth() / 4, getWindowHeight() / 4, 0, GL_RGB, |
| GL_UNSIGNED_BYTE, mLevelTwoRedInitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Draw a full-sized quad, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Draw a half-sized quad, and check it's green. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green); |
| |
| // Draw a quarter-sized quad, and check it's red. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); |
| |
| // Now disable mipmaps again, and render multiple sized quads. They should all be blue, since |
| // level 0 is blue. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| |
| // Now reset level 0 to white, keeping mipmaps disabled. Then, render various sized quads. They |
| // should be white. |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, |
| GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::white); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::white); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::white); |
| |
| // Then enable mipmaps again. The quads should be white, green, red respectively. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::white); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); |
| } |
| |
| // This test generates (and uses) mipmaps on a texture using init data. D3D11 will use a |
| // non-renderable TextureStorage for this. The test then disables mips, renders to level zero of the |
| // texture, and reenables mips before using the texture again. To do this, D3D11 has to convert the |
| // TextureStorage into a renderable one. This test ensures that the conversion works correctly. In |
| // particular, on D3D11 Feature Level 9_3 it ensures that both the zero LOD workaround texture AND |
| // the 'normal' texture are copied during conversion. |
| TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender) |
| { |
| // Pass in initial data so the texture is blue. |
| glBindTexture(GL_TEXTURE_2D, mTexture2D); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, |
| GL_UNSIGNED_BYTE, mLevelZeroBlueInitData.data()); |
| |
| // Then generate the mips. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Enable mipmaps. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| |
| // Now draw the texture to various different sized areas. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Use mip level 1 |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Use mip level 2 |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Disable mips. Render a quad using the texture and ensure it's blue. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Clear level 0 of the texture to red. |
| clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 1.0f, 0.0f, 0.0f, 1.0f); |
| |
| // Reenable mips, and try rendering different-sized quads. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| |
| // Level 0 is now red, so this should render red. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red); |
| |
| // Use mip level 1, blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Use mip level 2, blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| } |
| |
| // This test ensures that mips are correctly generated from a rendered image. |
| // In particular, on D3D11 Feature Level 9_3, the clear call will be performed on the zero-level |
| // texture, rather than the mipped one. The test ensures that the zero-level texture is correctly |
| // copied into the mipped texture before the mipmaps are generated. |
| TEST_P(MipmapTest, GenerateMipmapFromRenderedImage) |
| { |
| glBindTexture(GL_TEXTURE_2D, mTexture2D); |
| // Clear the texture to blue. |
| clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Then generate the mips |
| glGenerateMipmap(GL_TEXTURE_2D); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Enable mips. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| |
| // Now draw the texture to various different sized areas. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Use mip level 1 |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Use mip level 2 |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| } |
| |
| // Test to ensure that rendering to a mipmapped texture works, regardless of whether mipmaps are |
| // enabled or not. |
| // TODO: This test hits a texture rebind bug in the D3D11 renderer. Fix this. |
| TEST_P(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap) |
| { |
| // TODO(geofflang): Figure out why this is broken on AMD OpenGL |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL()); |
| |
| glBindTexture(GL_TEXTURE_2D, mTexture2D); |
| |
| // Clear the texture to blue. |
| clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f); |
| |
| // Now, draw the texture to a quad that's the same size as the texture. This draws to the |
| // default framebuffer. The quad should be blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Now go back to the texture, and generate mips on it. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Now try rendering the textured quad again. Note: we've not told GL to use the generated mips. |
| // The quad should be blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Now tell GL to use the generated mips. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| EXPECT_GL_NO_ERROR(); |
| |
| // Now render the textured quad again. It should be still be blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Now render the textured quad to an area smaller than the texture (i.e. to force |
| // minification). This should be blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| |
| // Now clear the texture to green. This just clears the top level. The lower mips should remain |
| // blue. |
| clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 1.0f, 0.0f, 1.0f); |
| |
| // Render a textured quad equal in size to the texture. This should be green, since we just |
| // cleared level 0. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green); |
| |
| // Render a small textured quad. This forces minification, so should render blue (the color of |
| // levels 1+). |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue); |
| |
| // Disable mipmaps again |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Render a textured quad equal in size to the texture. This should be green, the color of level |
| // 0 in the texture. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green); |
| |
| // Render a small textured quad. This would force minification if mips were enabled, but they're |
| // not. Therefore, this should be green. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green); |
| } |
| |
| // This test defines a valid mipchain manually, with an extra level that's unused on the first few |
| // draws. Later on, it redefines the whole mipchain but this time, uses the last mip that was |
| // already uploaded before. The test expects that mip to be usable. |
| TEST_P(MipmapTest, DefineValidExtraLevelAndUseItLater) |
| { |
| glBindTexture(GL_TEXTURE_2D, mTexture2D); |
| |
| GLubyte *levels[] = {mLevelZeroBlueInitData.data(), mLevelOneGreenInitData.data(), |
| mLevelTwoRedInitData.data()}; |
| |
| int maxLevel = 1 + static_cast<int>(floor(log2(std::max(getWindowWidth(), getWindowHeight())))); |
| |
| for (int i = 0; i < maxLevel; i++) |
| { |
| glTexImage2D(GL_TEXTURE_2D, i, GL_RGB, getWindowWidth() >> i, getWindowHeight() >> i, 0, |
| GL_RGB, GL_UNSIGNED_BYTE, levels[i % 3]); |
| } |
| |
| // Define an extra level that won't be used for now |
| std::vector<GLubyte> magentaExtraLevelData = |
| createRGBInitData(getWindowWidth() * 2, getWindowHeight() * 2, 255, 0, 255); |
| glTexImage2D(GL_TEXTURE_2D, maxLevel, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, |
| magentaExtraLevelData.data()); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| // Enable mipmaps. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| |
| // Draw a full-sized quad using mip 0, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| |
| // Draw a full-sized quad using mip 1, and check it's green. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green); |
| |
| // Draw a full-sized quad using mip 2, and check it's red. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); |
| |
| // Draw a full-sized quad using the last mip, and check it's green. |
| clearAndDrawQuad(m2DProgram, 1, 1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); |
| |
| // Now redefine everything above level 8 to be a mipcomplete chain again. |
| std::vector<GLubyte> levelDoubleSizeYellowInitData = |
| createRGBInitData(getWindowWidth() * 2, getWindowHeight() * 2, 255, 255, 0); |
| |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth() * 2, getWindowHeight() * 2, 0, GL_RGB, |
| GL_UNSIGNED_BYTE, levelDoubleSizeYellowInitData.data()); // 256 |
| |
| for (int i = 0; i < maxLevel - 1; i++) |
| { |
| glTexImage2D(GL_TEXTURE_2D, i + 1, GL_RGB, getWindowWidth() >> i, getWindowHeight() >> i, 0, |
| GL_RGB, GL_UNSIGNED_BYTE, levels[i % 3]); |
| } |
| |
| // At this point we have a valid mip chain, the last level being magenta if we draw 1x1 pixel. |
| clearAndDrawQuad(m2DProgram, 1, 1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); |
| |
| // Draw a full-sized quad using mip 0, and check it's yellow. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() * 2, getWindowHeight() * 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::yellow); |
| |
| // Draw a full-sized quad using mip 1, and check it's blue. |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Draw a full-sized quad using mip 2, and check it's green. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green); |
| } |
| |
| // Regression test for a bug that cause mipmaps to only generate using the top left corner as input. |
| TEST_P(MipmapTest, MipMapGenerationD3D9Bug) |
| { |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_storage") || |
| !IsGLExtensionEnabled("GL_OES_rgb8_rgba8") || |
| !IsGLExtensionEnabled("GL_ANGLE_texture_usage")); |
| |
| const GLColor mip0Color[4] = { |
| GLColor::red, |
| GLColor::green, |
| GLColor::red, |
| GLColor::green, |
| }; |
| const GLColor mip1Color = GLColor(127, 127, 0, 255); |
| |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture.get()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| glTexStorage2DEXT(GL_TEXTURE_2D, 2, GL_RGBA8_OES, 2, 2); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color); |
| glGenerateMipmap(GL_TEXTURE_2D); |
| |
| // Only draw to a 1 pixel viewport so the lower mip is used |
| clearAndDrawQuad(m2DProgram, 1, 1); |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, mip1Color, 1.0); |
| } |
| |
| // This test ensures that the level-zero workaround for TextureCubes (on D3D11 Feature Level 9_3) |
| // works as expected. It tests enabling/disabling mipmaps, generating mipmaps, and rendering to |
| // level zero. |
| TEST_P(MipmapTest, TextureCubeGeneralLevelZero) |
| { |
| // http://anglebug.com/3145 |
| ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan()); |
| |
| // http://anglebug.com/2822 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan()); |
| |
| glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); |
| |
| // Draw. Since the negative-Y face's is blue, this should be blue. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Generate mipmaps, and render. This should be blue. |
| glGenerateMipmap(GL_TEXTURE_CUBE_MAP); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Draw using a smaller viewport (to force a lower LOD of the texture). This should still be |
| // blue. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Now clear the negative-Y face of the cube to red. |
| clearTextureLevel0(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mTextureCube, 1.0f, 0.0f, 0.0f, 1.0f); |
| |
| // Draw using a full-size viewport. This should be red. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw using a quarter-size viewport, to force a lower LOD. This should be *BLUE*, since we |
| // only cleared level zero of the negative-Y face to red, and left its mipmaps blue. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Disable mipmaps again, and draw a to a quarter-size viewport. |
| // Since this should use level zero of the texture, this should be *RED*. |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // This test ensures that rendering to level-zero of a TextureCube works as expected. |
| TEST_P(MipmapTest, TextureCubeRenderToLevelZero) |
| { |
| // http://anglebug.com/3145 |
| ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan()); |
| |
| // http://anglebug.com/2822 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan()); |
| |
| glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube); |
| |
| // Draw. Since the negative-Y face's is blue, this should be blue. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| // Now clear the negative-Y face of the cube to red. |
| clearTextureLevel0(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mTextureCube, 1.0f, 0.0f, 0.0f, 1.0f); |
| |
| // Draw using a full-size viewport. This should be red. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Draw a to a quarter-size viewport. This should also be red. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap. |
| // Then tests if the mipmaps are rendered correctly for all three layers. |
| TEST_P(MipmapTestES3, MipmapsForTextureArray) |
| { |
| int px = getWindowWidth() / 2; |
| int py = getWindowHeight() / 2; |
| |
| glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture); |
| |
| glTexStorage3D(GL_TEXTURE_2D_ARRAY, 5, GL_RGBA8, 16, 16, 3); |
| |
| // Fill the first layer with red |
| std::vector<GLColor> pixelsRed(16 * 16, GLColor::red); |
| glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsRed.data()); |
| |
| // Fill the second layer with green |
| std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green); |
| glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsGreen.data()); |
| |
| // Fill the third layer with blue |
| std::vector<GLColor> pixelsBlue(16 * 16, GLColor::blue); |
| glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 2, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsBlue.data()); |
| |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| glGenerateMipmap(GL_TEXTURE_2D_ARRAY); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(mArrayProgram); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw the first slice |
| glUniform1i(mTextureArraySliceUniformLocation, 0); |
| drawQuad(mArrayProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red); |
| |
| // Draw the second slice |
| glUniform1i(mTextureArraySliceUniformLocation, 1); |
| drawQuad(mArrayProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green); |
| |
| // Draw the third slice |
| glUniform1i(mTextureArraySliceUniformLocation, 2); |
| drawQuad(mArrayProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::blue); |
| } |
| |
| // Create a mipmapped 2D array texture with more layers than width / height, and call |
| // GenerateMipmap. |
| TEST_P(MipmapTestES3, MipmapForDeepTextureArray) |
| { |
| int px = getWindowWidth() / 2; |
| int py = getWindowHeight() / 2; |
| |
| glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture); |
| |
| // Fill the whole texture with red. |
| std::vector<GLColor> pixelsRed(2 * 2 * 4, GLColor::red); |
| glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsRed.data()); |
| |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| glGenerateMipmap(GL_TEXTURE_2D_ARRAY); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(mArrayProgram); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw the first slice |
| glUniform1i(mTextureArraySliceUniformLocation, 0); |
| drawQuad(mArrayProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red); |
| |
| // Draw the fourth slice |
| glUniform1i(mTextureArraySliceUniformLocation, 3); |
| drawQuad(mArrayProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red); |
| } |
| |
| // Creates a mipmapped 3D texture with two layers, and calls ANGLE's GenerateMipmap. |
| // Then tests if the mipmaps are rendered correctly for all two layers. |
| TEST_P(MipmapTestES3, MipmapsForTexture3D) |
| { |
| // TODO(cnorthrop): Enabled the group to cover texture base level, but this test |
| // needs some triage: http://anglebug.com/3950 |
| ANGLE_SKIP_TEST_IF(IsVulkan()); |
| |
| int px = getWindowWidth() / 2; |
| int py = getWindowHeight() / 2; |
| |
| glBindTexture(GL_TEXTURE_3D, mTexture); |
| |
| glTexStorage3D(GL_TEXTURE_3D, 5, GL_RGBA8, 16, 16, 2); |
| |
| // Fill the first layer with red |
| std::vector<GLColor> pixelsRed(16 * 16, GLColor::red); |
| glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsRed.data()); |
| |
| // Fill the second layer with green |
| std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green); |
| glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsGreen.data()); |
| |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| glGenerateMipmap(GL_TEXTURE_3D); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| glUseProgram(m3DProgram); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Mipmap level 0 |
| // Draw the first slice |
| glUniform1f(mTexture3DLODUniformLocation, 0.); |
| glUniform1f(mTexture3DSliceUniformLocation, 0.25f); |
| drawQuad(m3DProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red); |
| |
| // Draw the second slice |
| glUniform1f(mTexture3DSliceUniformLocation, 0.75f); |
| drawQuad(m3DProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green); |
| |
| // Mipmap level 1 |
| // The second mipmap should only have one slice. |
| glUniform1f(mTexture3DLODUniformLocation, 1.); |
| drawQuad(m3DProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0); |
| |
| glUniform1f(mTexture3DSliceUniformLocation, 0.75f); |
| drawQuad(m3DProgram, "position", 0.5f); |
| EXPECT_GL_NO_ERROR(); |
| EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0); |
| } |
| |
| // Create a 2D texture with levels 0-2, call GenerateMipmap with base level 1 so that level 0 stays |
| // the same, and then sample levels 0 and 2. |
| // GLES 3.0.4 section 3.8.10: |
| // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from |
| // the levelbase array, regardless of their previous contents. All other mipmap arrays, including |
| // the levelbase array, are left unchanged by this computation." |
| TEST_P(MipmapTestES3, GenerateMipmapBaseLevel) |
| { |
| // Observed incorrect rendering on AMD, sampling level 2 returns black. |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsDesktopOpenGL()); |
| |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| |
| ASSERT_EQ(getWindowWidth(), getWindowHeight()); |
| |
| // Fill level 0 with blue |
| std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, pixelsBlue.data()); |
| |
| // Fill level 1 with red |
| std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight() / 4, GLColor::red); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data()); |
| |
| // Fill level 2 with green |
| std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 16, GLColor::green); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth() / 4, getWindowHeight() / 4, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, pixelsGreen.data()); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // The blue level 0 should be untouched by this since base level is 1. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw using level 2. It should be set to red by GenerateMipmap. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); |
| |
| // Draw using level 0. It should still be blue. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| } |
| |
| // Create a cube map with levels 0-2, call GenerateMipmap with base level 1 so that level 0 stays |
| // the same, and then sample levels 0 and 2. |
| // GLES 3.0.4 section 3.8.10: |
| // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from |
| // the levelbase array, regardless of their previous contents. All other mipmap arrays, including |
| // the levelbase array, are left unchanged by this computation." |
| TEST_P(MipmapTestES3, GenerateMipmapCubeBaseLevel) |
| { |
| // Observed incorrect rendering on AMD, sampling level 2 returns black. |
| ANGLE_SKIP_TEST_IF(IsAMD() && IsDesktopOpenGL()); |
| |
| ASSERT_EQ(getWindowWidth(), getWindowHeight()); |
| |
| glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture); |
| std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowWidth(), GLColor::blue); |
| TexImageCubeMapFaces(0, GL_RGBA8, getWindowWidth(), GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsBlue.data()); |
| |
| // Fill level 1 with red |
| std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowWidth() / 4, GLColor::red); |
| TexImageCubeMapFaces(1, GL_RGBA8, getWindowWidth() / 2, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsRed.data()); |
| |
| // Fill level 2 with green |
| std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowWidth() / 16, GLColor::green); |
| TexImageCubeMapFaces(2, GL_RGBA8, getWindowWidth() / 4, GL_RGBA, GL_UNSIGNED_BYTE, |
| pixelsGreen.data()); |
| |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 1); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // The blue level 0 should be untouched by this since base level is 1. |
| glGenerateMipmap(GL_TEXTURE_CUBE_MAP); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw using level 2. It should be set to red by GenerateMipmap. |
| clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); |
| |
| // Observed incorrect rendering on NVIDIA, level zero seems to be incorrectly affected by |
| // GenerateMipmap. |
| // http://anglebug.com/3851 |
| ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL()); |
| |
| // Draw using level 0. It should still be blue. |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0); |
| clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| } |
| |
| // Create a texture with levels 0-2, call GenerateMipmap with max level 1 so that level 2 stays the |
| // same, and then sample levels 1 and 2. |
| // GLES 3.0.4 section 3.8.10: |
| // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from |
| // the levelbase array, regardless of their previous contents. All other mipmap arrays, including |
| // the levelbase array, are left unchanged by this computation." |
| TEST_P(MipmapTestES3, GenerateMipmapMaxLevel) |
| { |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| |
| // Fill level 0 with blue |
| std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, pixelsBlue.data()); |
| |
| // Fill level 1 with red |
| std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight() / 4, GLColor::red); |
| glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data()); |
| |
| // Fill level 2 with green |
| std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 16, GLColor::green); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth() / 4, getWindowHeight() / 4, 0, |
| GL_RGBA, GL_UNSIGNED_BYTE, pixelsGreen.data()); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // The green level 2 should be untouched by this since max level is 1. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Draw using level 1. It should be set to blue by GenerateMipmap. |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue); |
| |
| // Draw using level 2. It should still be green. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2); |
| clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green); |
| } |
| |
| // Call GenerateMipmap with out-of-range base level. The spec is interpreted so that an out-of-range |
| // base level does not have a color-renderable/texture-filterable internal format, so the |
| // GenerateMipmap call generates INVALID_OPERATION. GLES 3.0.4 section 3.8.10: |
| // "If the levelbase array was not specified with an unsized internal format from table 3.3 or a |
| // sized internal format that is both color-renderable and texture-filterable according to table |
| // 3.13, an INVALID_OPERATION error is generated." |
| TEST_P(MipmapTestES3, GenerateMipmapBaseLevelOutOfRange) |
| { |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| |
| // Fill level 0 with blue |
| std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, pixelsBlue.data()); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1000); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // Expecting the out-of-range base level to be treated as not color-renderable and |
| // texture-filterable. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // Draw using level 0. It should still be blue. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue); |
| } |
| |
| // Call GenerateMipmap with out-of-range base level on an immutable texture. The base level should |
| // be clamped, so the call doesn't generate an error. |
| TEST_P(MipmapTestES3, GenerateMipmapBaseLevelOutOfRangeImmutableTexture) |
| { |
| // TODO(cnorthrop): Interacts with immutable texture supprt: http://anglebug.com/3950 |
| ANGLE_SKIP_TEST_IF(IsVulkan()); |
| |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1000); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // This is essentially a no-op, since the texture only has one level. |
| glGenerateMipmap(GL_TEXTURE_2D); |
| |
| EXPECT_GL_NO_ERROR(); |
| |
| // The only level of the texture should still be green. |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green); |
| } |
| |
| // A native version of the WebGL2 test tex-base-level-bug.html |
| TEST_P(MipmapTestES3, BaseLevelTextureBug) |
| { |
| ANGLE_SKIP_TEST_IF(IsOpenGL() && IsAMD()); |
| |
| // Regression in 10.12.4 needing workaround -- crbug.com/705865. |
| // Seems to be passing on AMD GPUs. Definitely not NVIDIA. |
| // Probably not Intel. |
| ANGLE_SKIP_TEST_IF(IsOSX() && (IsNVIDIA() || IsIntel())); |
| |
| // TODO(cnorthrop): Figure out what's going on here: http://anglebug.com/3950 |
| ANGLE_SKIP_TEST_IF(IsVulkan()); |
| |
| std::vector<GLColor> texDataRed(2u * 2u, GLColor::red); |
| |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataRed.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(m2DProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| drawQuad(m2DProgram, "position", 0.5f); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| TEST_P(MipmapTestES31, MipmapWithMemoryBarrier) |
| { |
| std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight(), GLColor::red); |
| std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 4, GLColor::green); |
| |
| constexpr char kVS[] = R"(#version 300 es |
| precision highp float; |
| in vec4 position; |
| out vec2 texcoord; |
| |
| void main() |
| { |
| gl_Position = vec4(position.xy, 0.0, 1.0); |
| texcoord = (position.xy * 0.5) + 0.5; |
| })"; |
| |
| constexpr char kFS[] = R"(#version 300 es |
| precision highp float; |
| uniform highp sampler2D tex; |
| in vec2 texcoord; |
| out vec4 out_FragColor; |
| |
| void main() |
| { |
| out_FragColor = texture(tex, texcoord); |
| })"; |
| |
| ANGLE_GL_PROGRAM(m2DProgram, kVS, kFS); |
| glUseProgram(m2DProgram); |
| |
| // Create a texture with red and enable the mipmap |
| GLTexture texture; |
| glBindTexture(GL_TEXTURE_2D, texture); |
| // Fill level 0 with red |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, pixelsRed.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| EXPECT_GL_NO_ERROR(); |
| glGenerateMipmap(GL_TEXTURE_2D); |
| ASSERT_GL_NO_ERROR(); |
| // level 2 is red |
| clearAndDrawQuad(m2DProgram.get(), getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red); |
| |
| // Clear the level 1 to green |
| glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, getWindowWidth() / 2, getWindowHeight() / 2, GL_RGBA, |
| GL_UNSIGNED_BYTE, pixelsGreen.data()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1); |
| EXPECT_GL_NO_ERROR(); |
| glGenerateMipmap(GL_TEXTURE_2D); |
| ASSERT_GL_NO_ERROR(); |
| // Insert a memory barrier, then it will break the graph node submission order. |
| glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); |
| // level 0 is red |
| clearAndDrawQuad(m2DProgram.get(), getWindowWidth(), getWindowHeight()); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red); |
| // Draw using level 2. It should be set to green by GenerateMipmap. |
| clearAndDrawQuad(m2DProgram.get(), getWindowWidth() / 4, getWindowHeight() / 4); |
| EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green); |
| } |
| |
| // 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(MipmapTest); |
| ANGLE_INSTANTIATE_TEST_ES3(MipmapTestES3); |
| ANGLE_INSTANTIATE_TEST_ES31(MipmapTestES31); |