Handle Clear* commands for layered framebuffers
The patch adds support for clearing the layers of 2D array textures
attached to a multi-view framebuffer. According to the ANGLE_multiview
spec, the layers which are outside of the range
[baseViewIndex; baseViewIndex + numViews) should remain unmodified.
Because the native Clear* commands clear all of the layers, a workaround
is implemented which creates a FBO, attaches a single layer from all
multi-view attachments and clears the contents.
BUG=angleproject:2062
TEST=angle_end2end_tests
Change-Id: Ibf711d02046233eed16bdd3f9c96fc38f82ed0a8
Reviewed-on: https://chromium-review.googlesource.com/615242
Commit-Queue: Martin Radev <mradev@nvidia.com>
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/tests/gl_tests/FramebufferMultiviewTest.cpp b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
index 7ace41e..3da5e40 100644
--- a/src/tests/gl_tests/FramebufferMultiviewTest.cpp
+++ b/src/tests/gl_tests/FramebufferMultiviewTest.cpp
@@ -62,10 +62,10 @@
PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
};
-class FramebufferMultiviewClearTest : public FramebufferMultiviewTest
+class FramebufferMultiviewSideBySideClearTest : public FramebufferMultiviewTest
{
protected:
- FramebufferMultiviewClearTest() {}
+ FramebufferMultiviewSideBySideClearTest() {}
void initializeFBOs(size_t numColorBuffers, bool stencil, bool depth)
{
@@ -116,7 +116,7 @@
}
glDrawBuffers(static_cast<GLsizei>(drawBuffers.size()), drawBuffers.data());
- // Generate normal fbo and attach textures.
+ // Generate non-multiview fbo and attach textures.
glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO);
for (size_t i = 0u; i < numColorBuffers; ++i)
{
@@ -164,8 +164,118 @@
GLTexture mStencilTex;
};
-// Test that the framebuffer tokens introduced by ANGLE_multiview can be used query the framebuffer
-// state and that their corresponding default values are correctly set.
+class FramebufferMultiviewLayeredClearTest : public FramebufferMultiviewTest
+{
+ protected:
+ FramebufferMultiviewLayeredClearTest() {}
+
+ void initializeFBOs(int width,
+ int height,
+ int numLayers,
+ int baseViewIndex,
+ int numViews,
+ int numColorAttachments,
+ bool stencil,
+ bool depth)
+ {
+ ASSERT(baseViewIndex + numViews <= numLayers);
+
+ // Generate textures.
+ mColorTex.resize(numColorAttachments);
+ for (int i = 0; i < numColorAttachments; ++i)
+ {
+ glBindTexture(GL_TEXTURE_2D_ARRAY, mColorTex[i]);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, width, height, numLayers, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, nullptr);
+ }
+
+ if (stencil)
+ {
+ glBindTexture(GL_TEXTURE_2D_ARRAY, mStencilTex);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, width, height, numLayers, 0,
+ GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, nullptr);
+ }
+ else if (depth)
+ {
+ glBindTexture(GL_TEXTURE_2D_ARRAY, mDepthTex);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, width, height, numLayers, 0,
+ GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
+ }
+
+ // Generate multiview FBO and attach textures.
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+ for (int i = 0; i < numColorAttachments; ++i)
+ {
+ glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER,
+ static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i),
+ mColorTex[i], 0, baseViewIndex, numViews);
+ }
+
+ if (stencil)
+ {
+ glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ mStencilTex, 0, baseViewIndex, numViews);
+ }
+ else if (depth)
+ {
+ glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+ mDepthTex, 0, baseViewIndex, numViews);
+ }
+
+ const auto &drawBuffers = GetDrawBufferRange(numColorAttachments);
+ glDrawBuffers(numColorAttachments, drawBuffers.data());
+
+ // Generate non-multiview FBOs and attach textures.
+ mNonMultiviewFBO.resize(numLayers);
+ for (int i = 0; i < numLayers; ++i)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO[i]);
+ for (int j = 0; j < numColorAttachments; ++j)
+ {
+ glFramebufferTextureLayer(GL_FRAMEBUFFER,
+ static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + j),
+ mColorTex[j], 0, i);
+ }
+ if (stencil)
+ {
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, mStencilTex,
+ 0, i);
+ }
+ else if (depth)
+ {
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, mDepthTex, 0, i);
+ }
+ glDrawBuffers(numColorAttachments, drawBuffers.data());
+
+ glClearColor(1, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ GLColor getLayerColor(size_t layer, GLenum attachment, GLint x, GLint y)
+ {
+ ASSERT(layer < mNonMultiviewFBO.size());
+ glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO[layer]);
+ glReadBuffer(attachment);
+ return angle::ReadColor(x, y);
+ }
+
+ GLColor getLayerColor(size_t layer, GLenum attachment)
+ {
+ return getLayerColor(layer, attachment, 0, 0);
+ }
+
+ GLFramebuffer mMultiviewFBO;
+ std::vector<GLFramebuffer> mNonMultiviewFBO;
+ std::vector<GLTexture> mColorTex;
+ GLTexture mDepthTex;
+ GLTexture mStencilTex;
+};
+
+// Test that the framebuffer tokens introduced by ANGLE_multiview can be used to query the
+// framebuffer state and that their corresponding default values are correctly set.
TEST_P(FramebufferMultiviewTest, DefaultState)
{
if (!requestMultiviewExtension())
@@ -588,7 +698,7 @@
}
// Test that glClear clears only the contents of each view if the scissor test is enabled.
-TEST_P(FramebufferMultiviewClearTest, SideBySideColorBufferClear)
+TEST_P(FramebufferMultiviewSideBySideClearTest, ColorBufferClear)
{
if (!requestMultiviewExtension())
{
@@ -707,7 +817,7 @@
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
- // Test framebuffer completeness when the 1st attachment has a normal layout.
+ // Test framebuffer completeness when the 1st attachment has a non-multiview layout.
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, otherTexLayered, 0, 0);
ASSERT_GL_NO_ERROR();
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_ANGLE,
@@ -722,7 +832,7 @@
}
// Test that glClear clears all of the contents if the scissor test is disabled.
-TEST_P(FramebufferMultiviewClearTest, SideBySideClearWithDisabledScissorTest)
+TEST_P(FramebufferMultiviewSideBySideClearTest, ClearWithDisabledScissorTest)
{
if (!requestMultiviewExtension())
{
@@ -756,7 +866,7 @@
}
// Test that glClear clears the depth buffer of each view.
-TEST_P(FramebufferMultiviewClearTest, SideBySideDepthBufferClear)
+TEST_P(FramebufferMultiviewSideBySideClearTest, DepthBufferClear)
{
if (!requestMultiviewExtension())
{
@@ -812,7 +922,7 @@
}
// Test that glClear clears the stencil buffer of each view.
-TEST_P(FramebufferMultiviewClearTest, SideBySideStencilBufferClear)
+TEST_P(FramebufferMultiviewSideBySideClearTest, StencilBufferClear)
{
if (!requestMultiviewExtension())
{
@@ -876,7 +986,7 @@
}
// Test that glClearBufferf clears the color buffer of each view.
-TEST_P(FramebufferMultiviewClearTest, SideBySideClearBufferF)
+TEST_P(FramebufferMultiviewSideBySideClearTest, ClearBufferF)
{
if (!requestMultiviewExtension())
{
@@ -916,5 +1026,204 @@
checkAlternatingColumnsOfRedAndGreenInFBO();
}
+// Test that glClear clears the contents of the color buffer for only the attached layers to a
+// layered FBO.
+TEST_P(FramebufferMultiviewLayeredClearTest, ColorBufferClear)
+{
+ if (!requestMultiviewExtension())
+ {
+ return;
+ }
+
+ initializeFBOs(1, 1, 4, 1, 2, 1, false, false);
+
+ // Bind and specify viewport/scissor dimensions for each view.
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+
+ glClearColor(0, 1, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::green, getLayerColor(2, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT0));
+}
+
+// Test that glClearBufferfv can be used to clear individual color buffers of a layered FBO.
+TEST_P(FramebufferMultiviewLayeredClearTest, ClearIndividualColorBuffer)
+{
+ if (!requestMultiviewExtension())
+ {
+ return;
+ }
+
+ initializeFBOs(1, 1, 4, 1, 2, 2, false, false);
+
+ for (int i = 0; i < 2; ++i)
+ {
+ for (int layer = 0; layer < 4; ++layer)
+ {
+ GLenum colorAttachment = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i);
+ EXPECT_EQ(GLColor::red, getLayerColor(layer, colorAttachment));
+ }
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+
+ float clearValues0[4] = {0.f, 0.f, 1.f, 1.f};
+ glClearBufferfv(GL_COLOR, 0, clearValues0);
+
+ float clearValues1[4] = {0.f, 1.f, 0.f, 1.f};
+ glClearBufferfv(GL_COLOR, 1, clearValues1);
+
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::blue, getLayerColor(1, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::blue, getLayerColor(2, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT0));
+
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT1));
+ EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT1));
+ EXPECT_EQ(GLColor::green, getLayerColor(2, GL_COLOR_ATTACHMENT1));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT1));
+}
+
+// Test that glClearBufferfi clears the contents of the stencil buffer for only the attached layers
+// to a layered FBO.
+TEST_P(FramebufferMultiviewLayeredClearTest, ClearBufferfi)
+{
+ if (!requestMultiviewExtension())
+ {
+ return;
+ }
+
+ // Create program to draw a quad.
+ const std::string &vs =
+ "#version 300 es\n"
+ "in vec3 vPos;\n"
+ "void main(){\n"
+ " gl_Position = vec4(vPos, 1.);\n"
+ "}\n";
+ const std::string &fs =
+ "#version 300 es\n"
+ "precision mediump float;\n"
+ "uniform vec3 uCol;\n"
+ "out vec4 col;\n"
+ "void main(){\n"
+ " col = vec4(uCol,1.);\n"
+ "}\n";
+ ANGLE_GL_PROGRAM(program, vs, fs);
+ glUseProgram(program);
+ GLuint mColorUniformLoc = glGetUniformLocation(program, "uCol");
+
+ initializeFBOs(1, 1, 4, 1, 2, 1, true, false);
+ glEnable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+
+ // Set clear values.
+ glClearColor(1, 0, 0, 1);
+ glClearStencil(0xFF);
+
+ // Clear the color and stencil buffers of each layer.
+ for (size_t i = 0u; i < mNonMultiviewFBO.size(); ++i)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO[i]);
+ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ }
+
+ // Switch to multiview framebuffer and clear portions of the texture.
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+ glClearBufferfi(GL_DEPTH_STENCIL, 0, 0.0f, 0);
+
+ // Draw a fullscreen quad, but adjust the stencil function so that only the cleared regions pass
+ // the test.
+ glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
+ glStencilFunc(GL_EQUAL, 0x00, 0xFF);
+ for (size_t i = 0u; i < mNonMultiviewFBO.size(); ++i)
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, mNonMultiviewFBO[i]);
+ glUniform3f(mColorUniformLoc, 0.0f, 1.0f, 0.0f);
+ drawQuad(program, "vPos", 0.0f, 1.0f, true);
+ }
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::green, getLayerColor(2, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT0));
+}
+
+// Test that glClear does not clear the content of a detached texture.
+TEST_P(FramebufferMultiviewLayeredClearTest, UnmodifiedDetachedTexture)
+{
+ if (!requestMultiviewExtension())
+ {
+ return;
+ }
+
+ initializeFBOs(1, 1, 4, 1, 2, 2, false, false);
+
+ // Clear all attachments.
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+ glClearColor(0, 1, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ for (int i = 0; i < 2; ++i)
+ {
+ GLenum colorAttachment = static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + i);
+ EXPECT_EQ(GLColor::red, getLayerColor(0, colorAttachment));
+ EXPECT_EQ(GLColor::green, getLayerColor(1, colorAttachment));
+ EXPECT_EQ(GLColor::green, getLayerColor(2, colorAttachment));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, colorAttachment));
+ }
+
+ // Detach and clear again.
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+ glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, 0, 0, 1, 2);
+ glClearColor(1, 1, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Check that color attachment 0 is modified.
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::yellow, getLayerColor(1, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::yellow, getLayerColor(2, GL_COLOR_ATTACHMENT0));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT0));
+
+ // Check that color attachment 1 is unmodified.
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT1));
+ EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT1));
+ EXPECT_EQ(GLColor::green, getLayerColor(2, GL_COLOR_ATTACHMENT1));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT1));
+}
+
+// Test that glClear clears only the contents within the scissor rectangle of the attached layers.
+TEST_P(FramebufferMultiviewLayeredClearTest, ScissoredClear)
+{
+ if (!requestMultiviewExtension())
+ {
+ return;
+ }
+
+ initializeFBOs(2, 1, 4, 1, 2, 1, false, false);
+
+ // Bind and specify viewport/scissor dimensions for each view.
+ glBindFramebuffer(GL_FRAMEBUFFER, mMultiviewFBO);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(1, 0, 1, 1);
+ glClearColor(0, 1, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT0, 0, 0));
+ EXPECT_EQ(GLColor::red, getLayerColor(0, GL_COLOR_ATTACHMENT0, 1, 0));
+
+ EXPECT_EQ(GLColor::red, getLayerColor(1, GL_COLOR_ATTACHMENT0, 0, 0));
+ EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT0, 1, 0));
+
+ EXPECT_EQ(GLColor::red, getLayerColor(2, GL_COLOR_ATTACHMENT0, 0, 0));
+ EXPECT_EQ(GLColor::green, getLayerColor(2, GL_COLOR_ATTACHMENT0, 1, 0));
+
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT0, 0, 0));
+ EXPECT_EQ(GLColor::red, getLayerColor(3, GL_COLOR_ATTACHMENT0, 1, 0));
+}
+
ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, ES3_OPENGL());
-ANGLE_INSTANTIATE_TEST(FramebufferMultiviewClearTest, ES3_OPENGL(), ES3_D3D11());
+ANGLE_INSTANTIATE_TEST(FramebufferMultiviewSideBySideClearTest, ES3_OPENGL(), ES3_D3D11());
+ANGLE_INSTANTIATE_TEST(FramebufferMultiviewLayeredClearTest, ES3_OPENGL());
\ No newline at end of file