Validation cube completeness for FBO attachments.

ES3 adds a clause that cube map FBO attachments must be cube complete
to produce complete framebuffers. ES2 doesn't have this clause, but
some (if not all, unverified) OpenGL back-ends don't support these
incomplete cube map attachments.

BUG=angleproject:1259

Change-Id: Idd4564488375b8646dde712e6ce4a158c23020ee
Reviewed-on: https://chromium-review.googlesource.com/318264
Tryjob-Request: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Rafael Cintron <rafael.cintron@microsoft.com>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Tested-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Jamie Madill 2015-12-15 16:41:39 -05:00
Родитель 4bc76d07e8
Коммит 3215b207b2
2 изменённых файлов: 97 добавлений и 25 удалений

Просмотреть файл

@ -390,6 +390,17 @@ GLenum Framebuffer::checkStatus(const gl::Data &data) const
{ {
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
} }
// ES3 specifies that cube map texture attachments must be cube complete.
// This language is missing from the ES2 spec, but we enforce it here because some
// desktop OpenGL drivers also enforce this validation.
// TODO(jmadill): Check if OpenGL ES2 drivers enforce cube completeness.
const Texture *texture = colorAttachment.getTexture();
ASSERT(texture);
if (texture->getTarget() == GL_TEXTURE_CUBE_MAP && !texture->isCubeComplete())
{
return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
}
} }
else if (colorAttachment.type() == GL_RENDERBUFFER) else if (colorAttachment.type() == GL_RENDERBUFFER)
{ {

Просмотреть файл

@ -11,7 +11,7 @@ using namespace angle;
class FramebufferFormatsTest : public ANGLETest class FramebufferFormatsTest : public ANGLETest
{ {
protected: protected:
FramebufferFormatsTest() FramebufferFormatsTest() : mFramebuffer(0), mTexture(0), mRenderbuffer(0), mProgram(0)
{ {
setWindowWidth(128); setWindowWidth(128);
setWindowHeight(128); setWindowHeight(128);
@ -52,20 +52,13 @@ class FramebufferFormatsTest : public ANGLETest
void testTextureFormat(GLenum internalFormat, GLint minRedBits, GLint minGreenBits, GLint minBlueBits, void testTextureFormat(GLenum internalFormat, GLint minRedBits, GLint minGreenBits, GLint minBlueBits,
GLint minAlphaBits) GLint minAlphaBits)
{ {
GLuint tex = 0; glGenTextures(1, &mTexture);
glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, mTexture);
glBindTexture(GL_TEXTURE_2D, tex);
glTexStorage2DEXT(GL_TEXTURE_2D, 1, internalFormat, 1, 1); glTexStorage2DEXT(GL_TEXTURE_2D, 1, internalFormat, 1, 1);
GLuint fbo = 0; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
testBitCounts(fbo, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0); testBitCounts(mFramebuffer, minRedBits, minGreenBits, minBlueBits, minAlphaBits, 0, 0);
glDeleteTextures(1, &tex);
glDeleteFramebuffers(1, &fbo);
} }
void testRenderbufferMultisampleFormat(int minESVersion, GLenum attachmentType, GLenum internalFormat) void testRenderbufferMultisampleFormat(int minESVersion, GLenum attachmentType, GLenum internalFormat)
@ -108,33 +101,57 @@ class FramebufferFormatsTest : public ANGLETest
return; return;
} }
GLuint framebufferID; glGenRenderbuffers(1, &mRenderbuffer);
glGenFramebuffers(1, &framebufferID); glBindRenderbuffer(GL_RENDERBUFFER, mRenderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferID);
GLuint renderbufferID;
glGenRenderbuffers(1, &renderbufferID);
glBindRenderbuffer(GL_RENDERBUFFER, renderbufferID);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, internalFormat, 128, 128); glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, 2, internalFormat, 128, 128);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentType, GL_RENDERBUFFER, renderbufferID); glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentType, GL_RENDERBUFFER, mRenderbuffer);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
glDeleteRenderbuffers(1, &renderbufferID);
glDeleteFramebuffers(1, &framebufferID);
} }
virtual void SetUp() void SetUp() override
{ {
ANGLETest::SetUp(); ANGLETest::SetUp();
glGenFramebuffers(1, &mFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
} }
virtual void TearDown() void TearDown() override
{ {
ANGLETest::TearDown(); ANGLETest::TearDown();
if (mTexture != 0)
{
glDeleteTextures(1, &mTexture);
mTexture = 0;
}
if (mRenderbuffer != 0)
{
glDeleteRenderbuffers(1, &mRenderbuffer);
mRenderbuffer = 0;
}
if (mFramebuffer != 0)
{
glDeleteFramebuffers(1, &mFramebuffer);
mFramebuffer = 0;
}
if (mProgram != 0)
{
glDeleteProgram(mProgram);
mProgram = 0;
}
} }
GLuint mFramebuffer;
GLuint mTexture;
GLuint mRenderbuffer;
GLuint mProgram;
}; };
TEST_P(FramebufferFormatsTest, RGBA4) TEST_P(FramebufferFormatsTest, RGBA4)
@ -229,5 +246,49 @@ TEST_P(FramebufferFormatsTest, RenderbufferMultisample_STENCIL_INDEX8)
testRenderbufferMultisampleFormat(2, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX8); testRenderbufferMultisampleFormat(2, GL_STENCIL_ATTACHMENT, GL_STENCIL_INDEX8);
} }
// Test that binding an incomplete cube map is rejected by ANGLE.
TEST_P(FramebufferFormatsTest, IncompleteCubeMap)
{
// First make a complete CubeMap.
glGenTextures(1, &mTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
mTexture, 0);
// Verify the framebuffer is complete.
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Make the CubeMap cube-incomplete.
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
// Verify the framebuffer is incomplete.
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
// Verify drawing with the incomplete framebuffer produces a GL error
const std::string &vs = "attribute vec4 position; void main() { gl_Position = position; }";
const std::string &ps = "void main() { gl_FragColor = vec4(1, 0, 0, 1); }";
mProgram = CompileProgram(vs, ps);
ASSERT_NE(0u, mProgram);
drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(FramebufferFormatsTest, ES2_D3D9(), ES2_D3D11(), ES3_D3D11(), ES2_OPENGL(), ES3_OPENGL());