blob: c5c1207bee190ead801d17f9ec9f2bd945d6cef8 [file] [log] [blame]
//
// 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);