Commit 857c09db by Geoff Lang Committed by Commit Bot

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/506928Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
parent 9733ceef
...@@ -73,6 +73,35 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach ...@@ -73,6 +73,35 @@ bool CheckAttachmentCompleteness(const Context *context, const FramebufferAttach
{ {
return false; return false;
} }
if (!texture->getImmutableFormat())
{
GLuint attachmentMipLevel = static_cast<GLuint>(attachment.mipLevel());
// From the ES 3.0 spec, pg 213:
// If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE and the value of
// FRAMEBUFFER_ATTACHMENT_OBJECT_NAME does not name an immutable-format texture,
// then the value of FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL must be in the
// range[levelbase, q], where levelbase is the value of TEXTURE_BASE_LEVEL and q is
// the effective maximum texture level defined in the Mipmapping discussion of
// section 3.8.10.4.
if (attachmentMipLevel < texture->getBaseLevel() ||
attachmentMipLevel > texture->getMipmapMaxLevel())
{
return false;
}
// Form the ES 3.0 spec, pg 213/214:
// If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE and the value of
// FRAMEBUFFER_ATTACHMENT_OBJECT_NAME does not name an immutable-format texture and
// the value of FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL is not levelbase, then the
// texture must be mipmap complete, and if FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names
// a cubemap texture, the texture must also be cube complete.
if (attachmentMipLevel != texture->getBaseLevel() && !texture->isMipmapComplete())
{
return false;
}
}
} }
return true; return true;
......
...@@ -839,6 +839,11 @@ GLboolean Texture::getFixedSampleLocations(GLenum target, size_t level) const ...@@ -839,6 +839,11 @@ GLboolean Texture::getFixedSampleLocations(GLenum target, size_t level) const
return mState.getImageDesc(target, level).fixedSampleLocations; return mState.getImageDesc(target, level).fixedSampleLocations;
} }
GLuint Texture::getMipmapMaxLevel() const
{
return mState.getMipmapMaxLevel();
}
bool Texture::isMipmapComplete() const bool Texture::isMipmapComplete() const
{ {
return mState.computeMipmapCompleteness(); return mState.computeMipmapCompleteness();
......
...@@ -260,6 +260,9 @@ class Texture final : public egl::ImageSibling, ...@@ -260,6 +260,9 @@ class Texture final : public egl::ImageSibling,
GLboolean getFixedSampleLocations(GLenum target, size_t level) const; GLboolean getFixedSampleLocations(GLenum target, size_t level) const;
const Format &getFormat(GLenum target, size_t level) const; const Format &getFormat(GLenum target, size_t level) const;
// Returns the value called "q" in the GLES 3.0.4 spec section 3.8.10.
GLuint getMipmapMaxLevel() const;
bool isMipmapComplete() const; bool isMipmapComplete() const;
Error setImage(const Context *context, Error setImage(const Context *context,
......
...@@ -12,6 +12,17 @@ ...@@ -12,6 +12,17 @@
using namespace angle; 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 class FramebufferFormatsTest : public ANGLETest
{ {
protected: protected:
...@@ -368,34 +379,17 @@ ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest, ...@@ -368,34 +379,17 @@ ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest,
class FramebufferTest_ES3 : public ANGLETest 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. // Covers invalidating an incomplete framebuffer. This should be a no-op, but should not error.
TEST_P(FramebufferTest_ES3, InvalidateIncomplete) TEST_P(FramebufferTest_ES3, InvalidateIncomplete)
{ {
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); GLFramebuffer framebuffer;
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); GLRenderbuffer renderbuffer;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, mRenderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT, EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER)); glCheckFramebufferStatus(GL_FRAMEBUFFER));
...@@ -410,15 +404,80 @@ TEST_P(FramebufferTest_ES3, InvalidateIncomplete) ...@@ -410,15 +404,80 @@ TEST_P(FramebufferTest_ES3, InvalidateIncomplete)
// as a depth-stencil attachment. It is equivalent to detaching the depth-stencil attachment. // as a depth-stencil attachment. It is equivalent to detaching the depth-stencil attachment.
TEST_P(FramebufferTest_ES3, DepthOnlyAsDepthStencil) TEST_P(FramebufferTest_ES3, DepthOnlyAsDepthStencil)
{ {
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); GLFramebuffer framebuffer;
glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer); GLRenderbuffer renderbuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 4, 4); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 4, 4);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
mRenderbuffer); renderbuffer);
EXPECT_GLENUM_NE(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER)); 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()); ANGLE_INSTANTIATE_TEST(FramebufferTest_ES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
class FramebufferTest_ES31 : public ANGLETest class FramebufferTest_ES31 : public ANGLETest
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment