Add missing completeness checks for texture attachments.
Texture attachments need to validate that the attached mip level is within
the [baseLevel,maxLevel] range and that the texture is complete if the
attached mip level is not the base level.
BUG=722684
Change-Id: I859766506b295638572b75a0e2e9fed168be047a
Reviewed-on: https://chromium-review.googlesource.com/506928
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/tests/gl_tests/FramebufferTest.cpp b/src/tests/gl_tests/FramebufferTest.cpp
index 6ed4f8f..9ed2152 100644
--- a/src/tests/gl_tests/FramebufferTest.cpp
+++ b/src/tests/gl_tests/FramebufferTest.cpp
@@ -12,6 +12,17 @@
using namespace angle;
+namespace
+{
+
+void ExpectFramebufferCompleteOrUnsupported(GLenum binding)
+{
+ GLenum status = glCheckFramebufferStatus(binding);
+ EXPECT_TRUE(status == GL_FRAMEBUFFER_COMPLETE || status == GL_FRAMEBUFFER_UNSUPPORTED);
+}
+
+} // anonymous namespace
+
class FramebufferFormatsTest : public ANGLETest
{
protected:
@@ -368,34 +379,17 @@
class FramebufferTest_ES3 : public ANGLETest
{
- protected:
- FramebufferTest_ES3() : mFramebuffer(0), mRenderbuffer(0) {}
-
- void SetUp() override
- {
- ANGLETest::SetUp();
-
- glGenFramebuffers(1, &mFramebuffer);
- glGenRenderbuffers(1, &mRenderbuffer);
- }
-
- void TearDown() override
- {
- glDeleteFramebuffers(1, &mFramebuffer);
- glDeleteRenderbuffers(1, &mRenderbuffer);
- ANGLETest::TearDown();
- }
-
- GLuint mFramebuffer;
- GLuint mRenderbuffer;
};
// Covers invalidating an incomplete framebuffer. This should be a no-op, but should not error.
TEST_P(FramebufferTest_ES3, InvalidateIncomplete)
{
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
+ GLFramebuffer framebuffer;
+ GLRenderbuffer renderbuffer;
+
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
@@ -410,15 +404,80 @@
// as a depth-stencil attachment. It is equivalent to detaching the depth-stencil attachment.
TEST_P(FramebufferTest_ES3, DepthOnlyAsDepthStencil)
{
- glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
- glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
+ GLFramebuffer framebuffer;
+ GLRenderbuffer renderbuffer;
+
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 4, 4);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
- mRenderbuffer);
+ renderbuffer);
EXPECT_GLENUM_NE(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
+// Test that the framebuffer correctly returns that it is not complete if invalid texture mip levels
+// are bound
+TEST_P(FramebufferTest_ES3, TextureAttachmentMipLevels)
+{
+ GLFramebuffer framebuffer;
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D, texture);
+
+ // Create a complete mip chain in mips 1 to 3
+ glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ // Create another complete mip chain in mips 4 to 5
+ glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexImage2D(GL_TEXTURE_2D, 5, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ // Create a non-complete mip chain in mip 6
+ glTexImage2D(GL_TEXTURE_2D, 6, GL_RGBA8, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ // Incomplete, mipLevel != baseLevel and texture is not mip complete
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 1);
+ EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ // Complete, mipLevel == baseLevel
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
+ ExpectFramebufferCompleteOrUnsupported(GL_FRAMEBUFFER);
+
+ // Complete, mipLevel != baseLevel but texture is now mip complete
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 2);
+ ExpectFramebufferCompleteOrUnsupported(GL_FRAMEBUFFER);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 3);
+ ExpectFramebufferCompleteOrUnsupported(GL_FRAMEBUFFER);
+
+ // Incomplete, attached level below the base level
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 1);
+ EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ // Incomplete, attached level is beyond effective max level
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 4);
+ EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+ glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+ // Complete, mipLevel == baseLevel
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 4);
+ ExpectFramebufferCompleteOrUnsupported(GL_FRAMEBUFFER);
+
+ // Complete, mipLevel != baseLevel but texture is now mip complete
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 5);
+ ExpectFramebufferCompleteOrUnsupported(GL_FRAMEBUFFER);
+
+ // Complete, mipLevel == baseLevel
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 6);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 6);
+ ExpectFramebufferCompleteOrUnsupported(GL_FRAMEBUFFER);
+}
+
ANGLE_INSTANTIATE_TEST(FramebufferTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
class FramebufferTest_ES31 : public ANGLETest