// // 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/autogen/FeaturesD3D_autogen.h" using namespace angle; struct ReadbackTestParam { GLuint attachment; GLuint format; GLuint type; void *data; int depthBits; int stencilBits; }; 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, 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, 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, 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); } bool hasReadDepthSupport() const { return IsGLExtensionEnabled("GL_NV_read_depth"); } bool hasReadDepthStencilSupport() const { return IsGLExtensionEnabled("GL_NV_read_depth_stencil"); } bool hasReadStencilSupport() const { return IsGLExtensionEnabled("GL_NV_read_stencil"); } bool hasFloatDepthSupport() const { return IsGLExtensionEnabled("GL_NV_depth_buffer_float2"); } void depthStencilReadbackCase(const ReadbackTestParam &type); 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_cube_map")) && 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)); } } void DepthStencilFormatsTestBase::depthStencilReadbackCase(const ReadbackTestParam &type) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture")); const bool hasFloatDepth = (type.type == GL_FLOAT); ANGLE_SKIP_TEST_IF(hasFloatDepth && !hasFloatDepthSupport()); const bool hasStencil = (type.format != GL_DEPTH_COMPONENT); const bool supportPackedDepthStencilFramebuffer = getClientMajorVersion() >= 3; const int res = 2; const int destRes = 4; 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, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // test level > 0 glTexImage2D(GL_TEXTURE_2D, 1, type.format, 1, 1, 0, type.format, type.type, nullptr); EXPECT_GL_NO_ERROR(); // test with data glTexImage2D(GL_TEXTURE_2D, 0, type.format, 1, 1, 0, type.format, type.type, type.data); EXPECT_GL_NO_ERROR(); // 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); EXPECT_GL_NO_ERROR(); 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 */ GLbitfield clearBits = GL_DEPTH_BUFFER_BIT; if (hasStencil) { clearBits |= GL_STENCIL_BUFFER_BIT; } 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); glClearStencil(1); glClear(clearBits); glScissor(1, 0, 1, 1); glClearDepthf(d10); glClearStencil(2); glClear(clearBits); glScissor(0, 1, 1, 1); glClearDepthf(d01); glClearStencil(3); glClear(clearBits); glScissor(1, 1, 1, 1); glClearDepthf(d11); glClearStencil(4); glClear(clearBits); glDisable(GL_SCISSOR_TEST); GLubyte actualPixels[destRes * destRes * 8]; glReadPixels(0, 0, destRes, destRes, GL_DEPTH_COMPONENT, hasFloatDepth ? GL_FLOAT : GL_UNSIGNED_SHORT, actualPixels); if (hasReadDepthSupport()) { EXPECT_GL_NO_ERROR(); if (hasFloatDepth) { constexpr float kEpsilon = 0.002f; const float *pixels = reinterpret_cast(actualPixels); ASSERT_NEAR(pixels[0], d00, kEpsilon); ASSERT_NEAR(pixels[0 + destRes], d01, kEpsilon); ASSERT_NEAR(pixels[1], d10, kEpsilon); ASSERT_NEAR(pixels[1 + destRes], d11, kEpsilon); } else { constexpr unsigned short kEpsilon = 2; const unsigned short *pixels = reinterpret_cast(actualPixels); ASSERT_NEAR(pixels[0], gl::unorm<16>(d00), kEpsilon); ASSERT_NEAR(pixels[0 + destRes], gl::unorm<16>(d01), kEpsilon); ASSERT_NEAR(pixels[1], gl::unorm<16>(d10), kEpsilon); ASSERT_NEAR(pixels[1 + destRes], gl::unorm<16>(d11), kEpsilon); } } else { EXPECT_GL_ERROR(GL_INVALID_OPERATION); } if (hasStencil) { glReadPixels(0, 0, destRes, destRes, GL_STENCIL_INDEX_OES, GL_UNSIGNED_BYTE, actualPixels); if (hasReadStencilSupport()) { EXPECT_GL_NO_ERROR(); ASSERT_TRUE((actualPixels[0] == 1) && (actualPixels[1] == 2) && (actualPixels[0 + destRes] == 3) && (actualPixels[1 + destRes] == 4)); } else { EXPECT_GL_ERROR(GL_INVALID_OPERATION); } ASSERT(!hasFloatDepth); glReadPixels(0, 0, destRes, destRes, GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES, actualPixels); if (hasReadDepthStencilSupport()) { EXPECT_GL_NO_ERROR(); struct Pixel { uint32_t x; uint32_t d24() const { return x >> 8; } uint8_t s8() const { return x & 0xff; } }; constexpr unsigned short kEpsilon = 2; const Pixel *pixels = reinterpret_cast(actualPixels); ASSERT_NEAR(pixels[0].d24(), gl::unorm<24>(d00), kEpsilon); ASSERT_NEAR(pixels[0 + destRes].d24(), gl::unorm<24>(d01), kEpsilon); ASSERT_NEAR(pixels[1].d24(), gl::unorm<24>(d10), kEpsilon); ASSERT_NEAR(pixels[1 + destRes].d24(), gl::unorm<24>(d11), kEpsilon); ASSERT_TRUE((pixels[0].s8() == 1) && (pixels[1].s8() == 2) && (pixels[0 + destRes].s8() == 3) && (pixels[1 + destRes].s8() == 4)); } else { EXPECT_GL_ERROR(GL_INVALID_OPERATION); } } } // This test will initialize a depth texture, clear it and read it back, if possible TEST_P(DepthStencilFormatsTest, DepthStencilReadback_UShort) { GLuint fakeData[10] = {0}; ReadbackTestParam type = { GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, fakeData, 16, 0}; depthStencilReadbackCase(type); } // This test will initialize a depth texture, clear it and read it back, if possible TEST_P(DepthStencilFormatsTest, DepthStencilReadback_UInt) { // http://anglebug.com/40644772 ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL()); GLuint fakeData[10] = {0}; ReadbackTestParam type = { GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, fakeData, 16, 0}; depthStencilReadbackCase(type); } // This test will initialize a depth texture, clear it and read it back, if possible TEST_P(DepthStencilFormatsTest, DepthStencilReadback_Float) { // http://anglebug.com/40644772 ANGLE_SKIP_TEST_IF(IsMac() && IsIntelUHD630Mobile() && IsDesktopOpenGL()); GLuint fakeData[10] = {0}; ReadbackTestParam type = {GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_FLOAT, fakeData, 32, 0}; depthStencilReadbackCase(type); } // This test will initialize a depth texture, clear it and read it back, if possible TEST_P(DepthStencilFormatsTest, DepthStencilReadback_DepthStencil) { GLuint fakeData[10] = {0}; ReadbackTestParam type = { GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES, fakeData, 24, 8}; depthStencilReadbackCase(type); } // Verify that packed D/S readPixels with a D32_FLOAT_S8X24_UINT attachment TEST_P(DepthStencilFormatsTestES3, DepthStencilReadback_DepthFloatStencil) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_packed_depth_stencil") || !IsGLExtensionEnabled("GL_NV_depth_buffer_float2") || !IsGLExtensionEnabled("GL_NV_read_depth") || !IsGLExtensionEnabled("GL_NV_read_depth_stencil") || !IsGLExtensionEnabled("GL_NV_read_stencil")); GLFramebuffer FBO; glBindFramebuffer(GL_FRAMEBUFFER, FBO); ASSERT_GL_NO_ERROR(); GLRenderbuffer RBO; glBindRenderbuffer(GL_RENDERBUFFER, RBO); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH32F_STENCIL8, getWindowWidth(), getWindowHeight()); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, RBO); ASSERT_GL_NO_ERROR(); constexpr float kDepthClearValue = 0.123f; constexpr uint8_t kStencilClearValue = 0x42; glClearDepthf(kDepthClearValue); glClearStencil(kStencilClearValue); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); ASSERT_GL_NO_ERROR(); struct { float depth; uint8_t stencil; char unused[3]; } pixel = {}; glReadPixels(0, 0, 1, 1, GL_DEPTH_STENCIL_OES, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, &pixel); EXPECT_GL_NO_ERROR(); EXPECT_FLOAT_EQ(pixel.depth, kDepthClearValue); EXPECT_EQ(pixel.stencil, kStencilClearValue); } // 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")); bool depthTextureCubeSupport = IsGLExtensionEnabled("GL_OES_depth_texture_cube_map"); bool textureSrgbDecodeSupport = IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode"); // http://anglebug.com/42262117 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(destRes), static_cast(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) struct TypeInfo { GLuint attachment; GLuint format; GLuint type; void *data; int depthBits; int stencilBits; }; GLuint fakeData[10] = {0}; std::vector types = { {GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, fakeData, 16, 0}, {GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, fakeData, 16, 0}, {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 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); if (depthTextureCubeSupport) { ASSERT_GL_NO_ERROR(); } else { EXPECT_GL_ERROR(GL_INVALID_OPERATION); } } std::vector 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); if (textureSrgbDecodeSupport) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT); } // 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 expectedMin; std::vector 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::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() && 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(viewport[0]), static_cast(viewport[1])); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); drawQuad(program, 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/42262342 // When bug is resolved we can remove this skip. ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid()); // http://anglebug.com/42262345 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 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_FRAMEBUFFER, fb); glClear(GL_COLOR_BUFFER_BIT); drawQuad(program, essl1_shaders::PositionAttrib(), 1.0f); ASSERT_GL_NO_ERROR(); glBindFramebuffer(GL_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, 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, essl1_shaders::PositionAttrib(), 0.1f); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::black); } // Verify that depth texture's data can be uploaded correctly TEST_P(DepthStencilFormatsTest, VerifyDepth32UploadData) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture")); 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(); // normalized 0.99 = 0xfd70a3d6 std::vector depthData(1, 0xfd70a3d6); GLTexture rbDepth; glBindTexture(GL_TEXTURE_2D, rbDepth); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, depthData.data()); ASSERT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rbDepth, 0); 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); glClearColor(0, 1, 0, 1); glClear(GL_COLOR_BUFFER_BIT); // Fail Depth Test and color buffer is unchanged float depthValue = 0.98f; drawQuad(programRed, essl1_shaders::PositionAttrib(), depthValue * 2 - 1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); // Pass Depth Test and draw red depthValue = 1.0f; drawQuad(programRed, essl1_shaders::PositionAttrib(), depthValue * 2 - 1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); ASSERT_GL_NO_ERROR(); // Change depth texture data glBindTexture(GL_TEXTURE_2D, rbDepth); depthData[0] = 0x7fffffff; // 0.5 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, depthData.data()); 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.48f; drawQuad(programGreen, 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, essl1_shaders::PositionAttrib(), depthValue * 2 - 1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); glDisable(GL_DEPTH_TEST); ASSERT_GL_NO_ERROR(); } // Verify that 16 bits depth texture's data can be uploaded correctly TEST_P(DepthStencilFormatsTest, VerifyDepth16UploadData) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture")); 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(); // normalized 0.99 = 0xfd6f std::vector depthData(1, 0xfd6f); GLTexture rbDepth; glBindTexture(GL_TEXTURE_2D, rbDepth); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, depthData.data()); ASSERT_GL_NO_ERROR(); GLFramebuffer fbo; glBindFramebuffer(GL_FRAMEBUFFER, fbo); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rbDepth, 0); 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); glClearColor(0, 1, 0, 1); glClear(GL_COLOR_BUFFER_BIT); // Fail Depth Test and color buffer is unchanged float depthValue = 0.98f; drawQuad(programRed, essl1_shaders::PositionAttrib(), depthValue * 2 - 1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); // Pass Depth Test and draw red depthValue = 1.0f; drawQuad(programRed, essl1_shaders::PositionAttrib(), depthValue * 2 - 1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); ASSERT_GL_NO_ERROR(); // Change depth texture data glBindTexture(GL_TEXTURE_2D, rbDepth); depthData[0] = 0x7fff; // 0.5 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, depthData.data()); 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.48f; drawQuad(programGreen, 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, essl1_shaders::PositionAttrib(), depthValue * 2 - 1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); glDisable(GL_DEPTH_TEST); ASSERT_GL_NO_ERROR(); } class DepthStencilFormatsTestES31 : public DepthStencilFormatsTestBase {}; // Test depth24 texture sample from fragment shader and then read access from compute TEST_P(DepthStencilFormatsTestES31, DrawReadDrawDispatch) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth24") || !IsGLExtensionEnabled("GL_NV_read_depth")); const char kVS[] = R"(#version 310 es layout (location = 0) in vec3 pos; void main(void) { gl_Position = vec4(pos, 1.0); })"; const char kFS[] = R"(#version 310 es precision highp float; uniform sampler2D tex; out vec4 fragColor; void main(void) { float d = texture(tex,vec2(0,0)).x; fragColor = vec4(d, 0, 0, 1); })"; // Create depth texture GLTexture depthTexture; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, depthTexture); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT24, 1, 1); // Clear depth texture to 0.51f GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); GLfloat depthValue = 0.51f; glClearDepthf(depthValue); glClear(GL_DEPTH_BUFFER_BIT); GLuint actualDepthValue; GLfloat depthErrorTolerance = 0.001f; glReadPixels(0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, &actualDepthValue); EXPECT_NEAR(depthValue, actualDepthValue / (float)UINT_MAX, depthErrorTolerance); glBindFramebuffer(GL_FRAMEBUFFER, 0); // Render to surface with texture sample from depth texture ANGLE_GL_PROGRAM(graphicsProgram, kVS, kFS); glUseProgram(graphicsProgram); const auto &quadVertices = GetQuadVertices(); GLBuffer arrayBuffer; glBindBuffer(GL_ARRAY_BUFFER, arrayBuffer); glBufferData(GL_ARRAY_BUFFER, quadVertices.size() * sizeof(Vector3), quadVertices.data(), GL_STATIC_DRAW); GLint positionAttributeLocation = 0; glBindAttribLocation(graphicsProgram, positionAttributeLocation, "pos"); glVertexAttribPointer(positionAttributeLocation, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(positionAttributeLocation); GLint uTextureLocation = glGetUniformLocation(graphicsProgram, "tex"); ASSERT_NE(-1, uTextureLocation); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, depthTexture); glUniform1i(uTextureLocation, 0); // Sample the depth texture from fragment shader and verify. This flushes out commands which // ensures there will be no layout transition in next renderPass. glDepthMask(false); glDisable(GL_DEPTH_TEST); glDrawArrays(GL_TRIANGLES, 0, 6); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor((GLubyte)(depthValue * 255), 0u, 0u, 255u)); // Sample the depth texture from fragment shader. No image layout transition expected here. glDrawArrays(GL_TRIANGLES, 0, 6); // Sample it from compute shader while the renderPass also sample from the same texture constexpr char kCS[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding=0) buffer buf { vec4 outData; }; uniform sampler2D u_tex2D; void main() { outData = texture(u_tex2D, vec2(gl_LocalInvocationID.xy)); })"; ANGLE_GL_COMPUTE_PROGRAM(computeProgram, kCS); glUseProgram(computeProgram); GLBuffer ssbo; const std::vector initialData(16, 0.0f); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBufferData(GL_SHADER_STORAGE_BUFFER, 16, initialData.data(), GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo); glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); const GLfloat *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 16, GL_MAP_READ_BIT)); EXPECT_NEAR(depthValue, ptr[0], depthErrorTolerance); EXPECT_GL_NO_ERROR(); } ANGLE_INSTANTIATE_TEST_ES2(DepthStencilFormatsTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DepthStencilFormatsTestES3); ANGLE_INSTANTIATE_TEST_ES3(DepthStencilFormatsTestES3); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DepthStencilFormatsTestES31); ANGLE_INSTANTIATE_TEST_ES31(DepthStencilFormatsTestES31); class TinyDepthStencilWorkaroundTest : public ANGLETest<> { public: TinyDepthStencilWorkaroundTest() { setWindowWidth(512); setWindowHeight(512); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); } }; // Tests that the tiny depth stencil textures workaround does not "stick" depth textures. // http://anglebug.com/40644610 TEST_P(TinyDepthStencilWorkaroundTest, DepthTexturesStick) { // http://anglebug.com/40096654 ANGLE_SKIP_TEST_IF((IsAndroid() && IsOpenGLES()) || (IsLinux() && IsVulkan())); 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, "texture"); ASSERT_NE(-1, blitTextureLocation); GLTexture 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_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); 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); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTex, 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); 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, "vertex", depth); glBindFramebuffer(GL_FRAMEBUFFER, 0); // blit FBO glDisable(GL_DEPTH_TEST); glUseProgram(blitProgram); glUniform1i(blitTextureLocation, 0); glBindTexture(GL_TEXTURE_2D, depthTex); drawQuad(blitProgram, "vertex", 0.5f); Vector4 depthVec(depth, depth, depth, 1); GLColor depthColor(depthVec); EXPECT_PIXEL_COLOR_NEAR(0, 0, depthColor, 1); ASSERT_GL_NO_ERROR(); } } // Initialize a depth texture by writing to it in a fragment shader then attempt to read it from a // compute shader. Regression test for D3D11 not unbinding the depth texture and the sampler binding // failing. TEST_P(DepthStencilFormatsTestES31, ReadDepthStencilInComputeShader) { constexpr char kTestVertexShader[] = R"(#version 310 es void main() { gl_PointSize = 1.0; vec2 pos[6] = vec2[6](vec2(-1.0f), vec2(1.0f, -1.0f), vec2(-1.0f, 1.0f), vec2(-1.0f, 1.0f), vec2(1.0f, -1.0f), vec2(1.0f)); gl_Position = vec4(pos[uint(gl_VertexID)], 0.5f, 1.0f); return; } )"; constexpr char kTestFragmentShader[] = R"(#version 310 es precision highp float; void main() { gl_FragDepth = 0.51f; return; } )"; constexpr char kTestComputeShader[] = R"(#version 310 es layout(binding = 0, std430) buffer dst_buf_block_ssbo { float inner[]; } dst_buf; uniform highp sampler2D tex; void blit_depth_to_buffer(uvec3 id) { uvec2 coord = (id.xy); uint dstOffset = (id.x + (id.y * 1u)); dst_buf.inner[dstOffset] = texelFetch(tex, ivec2(coord), 0).x; } layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; void main() { blit_depth_to_buffer(gl_GlobalInvocationID); } )"; GLfloat depthValue = 0.51f; GLfloat depthErrorTolerance = 0.001f; GLTexture depthTexture; glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, depthTexture); // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, 1, 1); // Clear depth texture to depthValue GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0); ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER); // 2. Draw and write to gl_FragDepth ANGLE_GL_PROGRAM(graphicsProgram, kTestVertexShader, kTestFragmentShader); glUseProgram(graphicsProgram); EXPECT_GL_NO_ERROR(); glDrawBuffers(0, nullptr); EXPECT_GL_NO_ERROR(); glClearDepthf(0.8); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); glDisable(GL_CULL_FACE); glDepthMask(true); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 6); EXPECT_GL_NO_ERROR(); glBindFramebuffer(GL_FRAMEBUFFER, 0); ANGLE_GL_COMPUTE_PROGRAM(computeProgram, kTestComputeShader); glUseProgram(computeProgram); GLint uTextureLocation = glGetUniformLocation(computeProgram, "tex"); ASSERT_NE(-1, uTextureLocation); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, depthTexture); glUniform1i(uTextureLocation, 0); GLBuffer ssbo; std::array initialData{0}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBufferData(GL_SHADER_STORAGE_BUFFER, 16, initialData.data(), GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo); glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); const GLfloat *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 16, GL_MAP_READ_BIT)); EXPECT_NEAR(depthValue, ptr[0], depthErrorTolerance); EXPECT_GL_NO_ERROR(); } GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TinyDepthStencilWorkaroundTest); ANGLE_INSTANTIATE_TEST_ES3_AND(TinyDepthStencilWorkaroundTest, ES3_D3D11().enable(Feature::EmulateTinyStencilTextures));