| // |
| // 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" |
| |
| #include "common/mathutil.h" |
| #include "platform/FeaturesD3D.h" |
| |
| using namespace angle; |
| |
| class DepthStencilFormatsTestBase : public ANGLETest |
| { |
| protected: |
| DepthStencilFormatsTestBase() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| bool checkTexImageFormatSupport(GLenum format, GLenum type) |
| { |
| EXPECT_GL_NO_ERROR(); |
| |
| GLuint tex = 0; |
| glGenTextures(1, &tex); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, format, 1, 1, 0, format, type, nullptr); |
| glDeleteTextures(1, &tex); |
| |
| return (glGetError() == GL_NO_ERROR); |
| } |
| |
| bool checkTexStorageFormatSupport(GLenum internalFormat) |
| { |
| EXPECT_GL_NO_ERROR(); |
| |
| GLuint tex = 0; |
| glGenTextures(1, &tex); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexStorage2DEXT(GL_TEXTURE_2D, 1, internalFormat, 1, 1); |
| glDeleteTextures(1, &tex); |
| |
| return (glGetError() == GL_NO_ERROR); |
| } |
| |
| bool checkRenderbufferFormatSupport(GLenum internalFormat) |
| { |
| EXPECT_GL_NO_ERROR(); |
| |
| GLuint rb = 0; |
| glGenRenderbuffers(1, &rb); |
| glBindRenderbuffer(GL_RENDERBUFFER, rb); |
| glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1); |
| glDeleteRenderbuffers(1, &rb); |
| |
| return (glGetError() == GL_NO_ERROR); |
| } |
| |
| void verifyDepthRenderBuffer(GLenum internalFormat) |
| { |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLRenderbuffer rbDepth; |
| glBindRenderbuffer(GL_RENDERBUFFER, rbDepth); |
| glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLFramebuffer fbo; |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbDepth); |
| |
| EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| ASSERT_GL_NO_ERROR(); |
| |
| ANGLE_GL_PROGRAM(programRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_GEQUAL); |
| glClearDepthf(0.99f); |
| glClearColor(0, 0, 0, 1); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| // Pass Depth Test and draw red |
| float depthValue = 1.0f; |
| drawQuad(programRed.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| ANGLE_GL_PROGRAM(programGreen, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green()); |
| |
| // Fail Depth Test and color buffer is unchanged |
| depthValue = 0.98f; |
| drawQuad(programGreen.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| ANGLE_GL_PROGRAM(programBlue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue()); |
| glClearDepthf(0.0f); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| |
| // Pass Depth Test and draw blue |
| depthValue = 0.01f; |
| drawQuad(programBlue.get(), essl1_shaders::PositionAttrib(), depthValue * 2 - 1); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| |
| glDisable(GL_DEPTH_TEST); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| 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"); |
| EXPECT_NE(-1, mTextureUniformLocation); |
| |
| glGenTextures(1, &mTexture); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void testTearDown() override |
| { |
| glDeleteProgram(mProgram); |
| glDeleteTextures(1, &mTexture); |
| } |
| |
| GLuint mProgram; |
| GLuint mTexture; |
| GLint mTextureUniformLocation; |
| }; |
| |
| class DepthStencilFormatsTest : public DepthStencilFormatsTestBase |
| {}; |
| |
| class DepthStencilFormatsTestES3 : public DepthStencilFormatsTestBase |
| {}; |
| |
| TEST_P(DepthStencilFormatsTest, DepthTexture) |
| { |
| bool shouldHaveTextureSupport = (IsGLExtensionEnabled("GL_ANGLE_depth_texture") || |
| IsGLExtensionEnabled("GL_OES_depth_texture")); |
| |
| EXPECT_EQ(shouldHaveTextureSupport, |
| checkTexImageFormatSupport(GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT)); |
| EXPECT_EQ(shouldHaveTextureSupport, |
| checkTexImageFormatSupport(GL_DEPTH_COMPONENT, GL_UNSIGNED_INT)); |
| |
| if (IsGLExtensionEnabled("GL_EXT_texture_storage")) |
| { |
| EXPECT_EQ(shouldHaveTextureSupport, checkTexStorageFormatSupport(GL_DEPTH_COMPONENT16)); |
| EXPECT_EQ(shouldHaveTextureSupport, checkTexStorageFormatSupport(GL_DEPTH_COMPONENT32_OES)); |
| } |
| } |
| |
| TEST_P(DepthStencilFormatsTest, PackedDepthStencil) |
| { |
| // Expected to fail in D3D9 if GL_OES_packed_depth_stencil is not present. |
| // Expected to fail in D3D11 if GL_OES_packed_depth_stencil or GL_ANGLE_depth_texture is not |
| // present. |
| |
| bool shouldHaveRenderbufferSupport = IsGLExtensionEnabled("GL_OES_packed_depth_stencil"); |
| EXPECT_EQ(shouldHaveRenderbufferSupport, |
| checkRenderbufferFormatSupport(GL_DEPTH24_STENCIL8_OES)); |
| |
| bool shouldHaveTextureSupport = (IsGLExtensionEnabled("GL_OES_packed_depth_stencil") && |
| IsGLExtensionEnabled("GL_OES_depth_texture")) || |
| IsGLExtensionEnabled("GL_ANGLE_depth_texture"); |
| EXPECT_EQ(shouldHaveTextureSupport, |
| checkTexImageFormatSupport(GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES)); |
| |
| if (IsGLExtensionEnabled("GL_EXT_texture_storage")) |
| { |
| bool shouldHaveTexStorageSupport = IsGLExtensionEnabled("GL_OES_packed_depth_stencil") || |
| IsGLExtensionEnabled("GL_ANGLE_depth_texture"); |
| EXPECT_EQ(shouldHaveTexStorageSupport, |
| checkTexStorageFormatSupport(GL_DEPTH24_STENCIL8_OES)); |
| } |
| } |
| |
| // This test will initialize a depth texture and then render with it and verify |
| // pixel correctness. |
| // This is modeled after webgl-depth-texture.html |
| TEST_P(DepthStencilFormatsTest, DepthTextureRender) |
| { |
| constexpr char kVS[] = R"(attribute vec4 a_position; |
| void main() |
| { |
| gl_Position = a_position; |
| })"; |
| |
| constexpr char kFS[] = R"(precision mediump float; |
| uniform sampler2D u_texture; |
| uniform vec2 u_resolution; |
| void main() |
| { |
| vec2 texcoord = (gl_FragCoord.xy - vec2(0.5)) / (u_resolution - vec2(1.0)); |
| gl_FragColor = texture2D(u_texture, texcoord); |
| })"; |
| |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture") && |
| !IsGLExtensionEnabled("GL_ANGLE_depth_texture")); |
| |
| // http://anglebug.com/3454 |
| ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsD3D9()); |
| |
| const int res = 2; |
| const int destRes = 4; |
| GLint resolution; |
| |
| ANGLE_GL_PROGRAM(program, kVS, kFS); |
| |
| glUseProgram(program); |
| resolution = glGetUniformLocation(program, "u_resolution"); |
| ASSERT_NE(-1, resolution); |
| glUniform2f(resolution, static_cast<float>(destRes), static_cast<float>(destRes)); |
| |
| GLuint buffer; |
| glGenBuffers(1, &buffer); |
| glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| float verts[] = { |
| 1, 1, 1, -1, 1, 0, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 0, |
| }; |
| glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW); |
| glEnableVertexAttribArray(0); |
| glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); |
| |
| // OpenGL ES does not have a FLIPY PixelStore attribute |
| // glPixelStorei(GL_UNPACK_FLIP) |
| |
| enum ObjType |
| { |
| GL, |
| EXT |
| }; |
| struct TypeInfo |
| { |
| ObjType obj; |
| GLuint attachment; |
| GLuint format; |
| GLuint type; |
| void *data; |
| int depthBits; |
| int stencilBits; |
| }; |
| |
| GLuint fakeData[10] = {0}; |
| |
| std::vector<TypeInfo> types = { |
| {ObjType::GL, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, fakeData, 16, 0}, |
| {ObjType::GL, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, fakeData, 16, 0}, |
| {ObjType::EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES, |
| fakeData, 24, 8}, |
| }; |
| |
| for (const TypeInfo &type : types) |
| { |
| GLTexture cubeTex; |
| glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTex); |
| ASSERT_GL_NO_ERROR(); |
| |
| std::vector<GLuint> targets{GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, |
| GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, |
| GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}; |
| |
| for (const GLuint target : targets) |
| { |
| glTexImage2D(target, 0, type.format, 1, 1, 0, type.format, type.type, nullptr); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| std::vector<GLuint> filterModes = {GL_LINEAR, GL_NEAREST}; |
| |
| const bool supportPackedDepthStencilFramebuffer = getClientMajorVersion() >= 3; |
| |
| for (const GLuint filterMode : filterModes) |
| { |
| GLTexture tex; |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterMode); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterMode); |
| |
| // test level > 0 |
| glTexImage2D(GL_TEXTURE_2D, 1, type.format, 1, 1, 0, type.format, type.type, nullptr); |
| if (IsGLExtensionEnabled("GL_OES_depth_texture")) |
| { |
| EXPECT_GL_NO_ERROR(); |
| } |
| else |
| { |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // test with data |
| glTexImage2D(GL_TEXTURE_2D, 0, type.format, 1, 1, 0, type.format, type.type, type.data); |
| if (IsGLExtensionEnabled("GL_OES_depth_texture")) |
| { |
| EXPECT_GL_NO_ERROR(); |
| } |
| else |
| { |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // test copyTexImage2D |
| glCopyTexImage2D(GL_TEXTURE_2D, 0, type.format, 0, 0, 1, 1, 0); |
| GLuint error = glGetError(); |
| ASSERT_TRUE(error == GL_INVALID_ENUM || error == GL_INVALID_OPERATION); |
| |
| // test real thing |
| glTexImage2D(GL_TEXTURE_2D, 0, type.format, res, res, 0, type.format, type.type, |
| nullptr); |
| EXPECT_GL_NO_ERROR(); |
| |
| // test texSubImage2D |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, type.format, type.type, type.data); |
| if (IsGLExtensionEnabled("GL_OES_depth_texture")) |
| { |
| EXPECT_GL_NO_ERROR(); |
| } |
| else |
| { |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // test copyTexSubImage2D |
| glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // test generateMipmap |
| glGenerateMipmap(GL_TEXTURE_2D); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| GLuint fbo = 0; |
| glGenFramebuffers(1, &fbo); |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| if (type.depthBits > 0 && type.stencilBits > 0 && !supportPackedDepthStencilFramebuffer) |
| { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex, 0); |
| EXPECT_GL_NO_ERROR(); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex, |
| 0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| else |
| { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, type.attachment, GL_TEXTURE_2D, tex, 0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| |
| // Ensure DEPTH_BITS returns >= 16 bits for UNSIGNED_SHORT and UNSIGNED_INT, >= 24 |
| // UNSIGNED_INT_24_8_WEBGL. If there is stencil, ensure STENCIL_BITS reports >= 8 for |
| // UNSIGNED_INT_24_8_WEBGL. |
| |
| GLint depthBits = 0; |
| glGetIntegerv(GL_DEPTH_BITS, &depthBits); |
| EXPECT_GE(depthBits, type.depthBits); |
| |
| GLint stencilBits = 0; |
| glGetIntegerv(GL_STENCIL_BITS, &stencilBits); |
| EXPECT_GE(stencilBits, type.stencilBits); |
| |
| // TODO: remove this check if the spec is updated to require these combinations to work. |
| if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) |
| { |
| // try adding a color buffer. |
| GLuint colorTex = 0; |
| glGenTextures(1, &colorTex); |
| glBindTexture(GL_TEXTURE_2D, colorTex); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, res, res, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| nullptr); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| colorTex, 0); |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| } |
| |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| |
| // use the default texture to render with while we return to the depth texture. |
| glBindTexture(GL_TEXTURE_2D, 0); |
| |
| /* Setup 2x2 depth texture: |
| * 1 0.6 0.8 |
| * | |
| * 0 0.2 0.4 |
| * 0---1 |
| */ |
| const GLfloat d00 = 0.2; |
| const GLfloat d01 = 0.4; |
| const GLfloat d10 = 0.6; |
| const GLfloat d11 = 0.8; |
| glEnable(GL_SCISSOR_TEST); |
| glScissor(0, 0, 1, 1); |
| glClearDepthf(d00); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| glScissor(1, 0, 1, 1); |
| glClearDepthf(d10); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| glScissor(0, 1, 1, 1); |
| glClearDepthf(d01); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| glScissor(1, 1, 1, 1); |
| glClearDepthf(d11); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| glDisable(GL_SCISSOR_TEST); |
| |
| // render the depth texture. |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| glViewport(0, 0, destRes, destRes); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| glDisable(GL_DITHER); |
| glEnable(GL_DEPTH_TEST); |
| glClearColor(1, 0, 0, 1); |
| glClearDepthf(1.0); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| glDrawArrays(GL_TRIANGLES, 0, 6); |
| |
| GLubyte actualPixels[destRes * destRes * 4]; |
| glReadPixels(0, 0, destRes, destRes, GL_RGBA, GL_UNSIGNED_BYTE, actualPixels); |
| const GLfloat eps = 0.002; |
| std::vector<GLfloat> expectedMin; |
| std::vector<GLfloat> expectedMax; |
| if (filterMode == GL_NEAREST) |
| { |
| GLfloat init[] = {d00, d00, d10, d10, d00, d00, d10, d10, |
| d01, d01, d11, d11, d01, d01, d11, d11}; |
| expectedMin.insert(expectedMin.begin(), init, init + 16); |
| expectedMax.insert(expectedMax.begin(), init, init + 16); |
| |
| for (int i = 0; i < 16; i++) |
| { |
| expectedMin[i] = expectedMin[i] - eps; |
| expectedMax[i] = expectedMax[i] + eps; |
| } |
| } |
| else |
| { |
| GLfloat initMin[] = { |
| d00 - eps, d00, d00, d10 - eps, d00, d00, d00, d10, |
| d00, d00, d00, d10, d01 - eps, d01, d01, d11 - eps, |
| }; |
| GLfloat initMax[] = { |
| d00 + eps, d10, d10, d10 + eps, d01, d11, d11, d11, |
| d01, d11, d11, d11, d01 + eps, d11, d11, d11 + eps, |
| }; |
| expectedMin.insert(expectedMin.begin(), initMin, initMin + 16); |
| expectedMax.insert(expectedMax.begin(), initMax, initMax + 16); |
| } |
| for (int yy = 0; yy < destRes; ++yy) |
| { |
| for (int xx = 0; xx < destRes; ++xx) |
| { |
| const int t = xx + destRes * yy; |
| const GLfloat was = (GLfloat)(actualPixels[4 * t] / 255.0); // 4bpp |
| const GLfloat eMin = expectedMin[t]; |
| const GLfloat eMax = expectedMax[t]; |
| EXPECT_TRUE(was >= eMin && was <= eMax) |
| << "At " << xx << ", " << yy << ", expected within [" << eMin << ", " |
| << eMax << "] was " << was; |
| } |
| } |
| |
| // check limitations |
| // Note: What happens if current attachment type is GL_DEPTH_STENCIL_ATTACHMENT |
| // and you try to call glFramebufferTexture2D with GL_DEPTH_ATTACHMENT? |
| // The webGL test this code came from expected that to fail. |
| // I think due to this line in ES3 spec: |
| // GL_INVALID_OPERATION is generated if textarget and texture are not compatible |
| // However, that's not the behavior I'm seeing, nor does it seem that a depth_stencil |
| // buffer isn't compatible with a depth attachment (e.g. stencil is unused). |
| if (type.attachment == GL_DEPTH_ATTACHMENT) |
| { |
| glBindFramebuffer(GL_FRAMEBUFFER, fbo); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, type.attachment, GL_TEXTURE_2D, 0, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
| tex, 0); |
| EXPECT_GLENUM_NE(GL_NO_ERROR, glGetError()); |
| EXPECT_GLENUM_NE(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION); |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| EXPECT_GL_NO_ERROR(); |
| } |
| } |
| } |
| } |
| |
| // This test will initialize a frame buffer, attaching a color and 16-bit depth buffer, |
| // render to it with depth testing, and verify pixel correctness. |
| TEST_P(DepthStencilFormatsTest, DepthBuffer16) |
| { |
| verifyDepthRenderBuffer(GL_DEPTH_COMPONENT16); |
| } |
| |
| // This test will initialize a frame buffer, attaching a color and 24-bit depth buffer, |
| // render to it with depth testing, and verify pixel correctness. |
| TEST_P(DepthStencilFormatsTest, DepthBuffer24) |
| { |
| bool shouldHaveRenderbufferSupport = IsGLExtensionEnabled("GL_OES_depth24"); |
| EXPECT_EQ(shouldHaveRenderbufferSupport, |
| checkRenderbufferFormatSupport(GL_DEPTH_COMPONENT24_OES)); |
| |
| if (shouldHaveRenderbufferSupport) |
| { |
| verifyDepthRenderBuffer(GL_DEPTH_COMPONENT24_OES); |
| } |
| } |
| |
| TEST_P(DepthStencilFormatsTestES3, DrawWithDepth16) |
| { |
| GLushort data[16]; |
| for (unsigned int i = 0; i < 16; i++) |
| { |
| data[i] = std::numeric_limits<GLushort>::max(); |
| } |
| glBindTexture(GL_TEXTURE_2D, mTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT16, 4, 4); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, data); |
| |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| |
| glUseProgram(mProgram); |
| glUniform1i(mTextureUniformLocation, 0); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| drawQuad(mProgram, "position", 0.5f); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| GLubyte pixel[4]; |
| glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); |
| |
| // Only require the red and alpha channels have the correct values, the depth texture extensions |
| // leave the green and blue channels undefined |
| ASSERT_NEAR(255, pixel[0], 2.0); |
| ASSERT_EQ(255, pixel[3]); |
| } |
| |
| // This test reproduces a driver bug on Intel windows platforms on driver version |
| // from 4815 to 4901. |
| // When rendering with Stencil buffer enabled and depth buffer disabled, large |
| // viewport will lead to memory leak and driver crash. And the pixel result |
| // is a random value. |
| TEST_P(DepthStencilFormatsTestES3, DrawWithLargeViewport) |
| { |
| ANGLE_SKIP_TEST_IF(IsIntel() && (IsOSX() || IsWindows())); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| |
| glEnable(GL_STENCIL_TEST); |
| glDisable(GL_DEPTH_TEST); |
| |
| // The iteration is to reproduce memory leak when rendering several times. |
| for (int i = 0; i < 10; ++i) |
| { |
| // Create offscreen fbo and its color attachment and depth stencil attachment. |
| GLTexture framebufferColorTexture; |
| glBindTexture(GL_TEXTURE_2D, framebufferColorTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth(), getWindowHeight()); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLTexture framebufferStencilTexture; |
| glBindTexture(GL_TEXTURE_2D, framebufferStencilTexture); |
| glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, getWindowWidth(), getWindowHeight()); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLFramebuffer fb; |
| glBindFramebuffer(GL_FRAMEBUFFER, fb); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| framebufferColorTexture, 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
| framebufferStencilTexture, 0); |
| |
| EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLint kStencilRef = 4; |
| glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); |
| glStencilFunc(GL_ALWAYS, kStencilRef, 0xFF); |
| |
| float viewport[2]; |
| glGetFloatv(GL_MAX_VIEWPORT_DIMS, viewport); |
| |
| glViewport(0, 0, static_cast<GLsizei>(viewport[0]), static_cast<GLsizei>(viewport[1])); |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| } |
| } |
| |
| // Verify that stencil component of depth texture is uploaded |
| TEST_P(DepthStencilFormatsTest, VerifyDepthStencilUploadData) |
| { |
| // http://anglebug.com/3683 |
| // When bug is resolved we can remove this skip. |
| ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid()); |
| |
| // http://anglebug.com/3689 |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsAMD()); |
| |
| bool shouldHaveTextureSupport = (IsGLExtensionEnabled("GL_OES_packed_depth_stencil") && |
| IsGLExtensionEnabled("GL_OES_depth_texture")) || |
| (getClientMajorVersion() >= 3); |
| |
| ANGLE_SKIP_TEST_IF(!shouldHaveTextureSupport || |
| !checkTexImageFormatSupport(GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES)); |
| |
| ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red()); |
| |
| glEnable(GL_STENCIL_TEST); |
| glDisable(GL_DEPTH_TEST); |
| glDepthMask(GL_FALSE); |
| |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| glClearColor(0, 0, 0, 1); |
| |
| // Create offscreen fbo and its color attachment and depth stencil attachment. |
| GLTexture framebufferColorTexture; |
| glBindTexture(GL_TEXTURE_2D, framebufferColorTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| ASSERT_GL_NO_ERROR(); |
| |
| // drawQuad's depth range is -1.0 to 1.0, so a depth value of 0.5 (0x7fffff) matches a drawQuad |
| // depth of 0.0. |
| std::vector<GLuint> depthStencilData(getWindowWidth() * getWindowHeight(), 0x7fffffA9); |
| GLTexture framebufferStencilTexture; |
| glBindTexture(GL_TEXTURE_2D, framebufferStencilTexture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_STENCIL, getWindowWidth(), getWindowHeight(), 0, |
| GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES, depthStencilData.data()); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLFramebuffer fb; |
| glBindFramebuffer(GL_FRAMEBUFFER, fb); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| framebufferColorTexture, 0); |
| |
| if (getClientMajorVersion() >= 3) |
| { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
| framebufferStencilTexture, 0); |
| ASSERT_GL_NO_ERROR(); |
| } |
| else |
| { |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, |
| framebufferStencilTexture, 0); |
| ASSERT_GL_NO_ERROR(); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, |
| framebufferStencilTexture, 0); |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); |
| |
| GLint kStencilRef = 0xA9; |
| glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); |
| glStencilFunc(GL_EQUAL, kStencilRef, 0xFF); |
| |
| glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 1.0f); |
| ASSERT_GL_NO_ERROR(); |
| |
| glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); |
| EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Check Z values |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| glDisable(GL_STENCIL_TEST); |
| glEnable(GL_DEPTH_TEST); |
| glDepthFunc(GL_LESS); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), -0.1f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red); |
| |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.1f); |
| ASSERT_GL_NO_ERROR(); |
| |
| EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::black); |
| } |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| ANGLE_INSTANTIATE_TEST_ES2(DepthStencilFormatsTest); |
| ANGLE_INSTANTIATE_TEST_ES3(DepthStencilFormatsTestES3); |
| |
| class TinyDepthStencilWorkaroundTest : public ANGLETest |
| { |
| public: |
| TinyDepthStencilWorkaroundTest() |
| { |
| setWindowWidth(512); |
| setWindowHeight(512); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| } |
| |
| // Override the features to enable "tiny" depth/stencil textures. |
| void overrideWorkaroundsD3D(FeaturesD3D *features) override |
| { |
| features->overrideFeatures({"emulate_tiny_stencil_textures"}, true); |
| } |
| }; |
| |
| // Tests that the tiny depth stencil textures workaround does not "stick" depth textures. |
| // http://anglebug.com/1664 |
| TEST_P(TinyDepthStencilWorkaroundTest, DepthTexturesStick) |
| { |
| // http://anglebug.com/4092 |
| ANGLE_SKIP_TEST_IF((IsAndroid() && IsOpenGLES()) || (IsLinux() && IsVulkan())); |
| ANGLE_SKIP_TEST_IF(isSwiftshader()); |
| constexpr char kDrawVS[] = |
| "#version 100\n" |
| "attribute vec3 vertex;\n" |
| "void main () {\n" |
| " gl_Position = vec4(vertex.x, vertex.y, vertex.z * 2.0 - 1.0, 1);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(drawProgram, kDrawVS, essl1_shaders::fs::Red()); |
| |
| constexpr char kBlitVS[] = |
| "#version 100\n" |
| "attribute vec2 vertex;\n" |
| "varying vec2 position;\n" |
| "void main () {\n" |
| " position = vertex * .5 + .5;\n" |
| " gl_Position = vec4(vertex, 0, 1);\n" |
| "}\n"; |
| |
| constexpr char kBlitFS[] = |
| "#version 100\n" |
| "precision mediump float;\n" |
| "uniform sampler2D texture;\n" |
| "varying vec2 position;\n" |
| "void main () {\n" |
| " gl_FragColor = vec4 (texture2D (texture, position).rrr, 1.);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(blitProgram, kBlitVS, kBlitFS); |
| |
| GLint blitTextureLocation = glGetUniformLocation(blitProgram.get(), "texture"); |
| ASSERT_NE(-1, blitTextureLocation); |
| |
| GLTexture colorTex; |
| glBindTexture(GL_TEXTURE_2D, colorTex.get()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| glBindTexture(GL_TEXTURE_2D, 0); |
| |
| GLTexture depthTex; |
| glBindTexture(GL_TEXTURE_2D, depthTex.get()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); |
| ASSERT_EQ(getWindowWidth(), getWindowHeight()); |
| int levels = gl::log2(getWindowWidth()); |
| for (int mipLevel = 0; mipLevel <= levels; ++mipLevel) |
| { |
| int size = getWindowWidth() >> mipLevel; |
| glTexImage2D(GL_TEXTURE_2D, mipLevel, GL_DEPTH_STENCIL, size, size, 0, GL_DEPTH_STENCIL, |
| GL_UNSIGNED_INT_24_8_OES, nullptr); |
| } |
| |
| glBindTexture(GL_TEXTURE_2D, 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| GLFramebuffer framebuffer; |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get()); |
| |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex.get(), 0); |
| glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex.get(), 0); |
| |
| ASSERT_GL_NO_ERROR(); |
| |
| glDepthRangef(0.0f, 1.0f); |
| glViewport(0, 0, getWindowWidth(), getWindowHeight()); |
| glClearColor(0, 0, 0, 1); |
| |
| // Draw loop. |
| for (unsigned int frame = 0; frame < 3; ++frame) |
| { |
| // draw into FBO |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get()); |
| glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); |
| |
| glEnable(GL_DEPTH_TEST); |
| |
| float depth = ((frame % 2 == 0) ? 0.0f : 1.0f); |
| drawQuad(drawProgram.get(), "vertex", depth); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, 0); |
| |
| // blit FBO |
| glDisable(GL_DEPTH_TEST); |
| |
| glUseProgram(blitProgram.get()); |
| glUniform1i(blitTextureLocation, 0); |
| glBindTexture(GL_TEXTURE_2D, depthTex.get()); |
| |
| drawQuad(blitProgram.get(), "vertex", 0.5f); |
| |
| Vector4 depthVec(depth, depth, depth, 1); |
| GLColor depthColor(depthVec); |
| |
| EXPECT_PIXEL_COLOR_NEAR(0, 0, depthColor, 1); |
| ASSERT_GL_NO_ERROR(); |
| } |
| } |
| |
| ANGLE_INSTANTIATE_TEST_ES3(TinyDepthStencilWorkaroundTest); |