Vulkan: Fix resolve-with-subpass retaining resolve attachment
The temporary resolve attachment used to optimize blit was not removed
from the framebuffer and render pass descriptions, resulting in
subsequent draws to be resolved again.
Bug: b/159903491
Change-Id: I9a9c90d25cb07ba9a25999f808b164f9085387d0
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2390441
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
Reviewed-by: Charlie Lao <cclao@google.com>
diff --git a/src/tests/gl_tests/FramebufferTest.cpp b/src/tests/gl_tests/FramebufferTest.cpp
index e46e005..de6b391 100644
--- a/src/tests/gl_tests/FramebufferTest.cpp
+++ b/src/tests/gl_tests/FramebufferTest.cpp
@@ -1003,8 +1003,19 @@
glGetQueryObjectuiv(query, GL_QUERY_RESULT, &passedCount);
EXPECT_EQ(static_cast<GLint>(passedCount), 0);
}
+
+ static constexpr char kFSWriteRedGreen[] = R"(#extension GL_EXT_draw_buffers : enable
+precision highp float;
+void main()
+{
+ gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0); // attachment 0: red
+ gl_FragData[1] = vec4(0.0, 1.0, 0.0, 1.0); // attachment 1: green
+})";
};
+// Until C++17, need to redundantly declare the constexpr array members outside the class.
+constexpr char FramebufferTest_ES31::kFSWriteRedGreen[];
+
// Test that without attachment, if either the value of FRAMEBUFFER_DEFAULT_WIDTH or
// FRAMEBUFFER_DEFAULT_HEIGHT parameters is zero, the framebuffer is incomplete.
TEST_P(FramebufferTest_ES31, IncompleteMissingAttachmentDefaultParam)
@@ -1446,14 +1457,7 @@
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Setup program to render red into attachment 0 and green into attachment 1.
- const char *fs = R"(#extension GL_EXT_draw_buffers : enable
-precision highp float;
-void main()
-{
- gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0); // attachment 0: red
- gl_FragData[1] = vec4(0.0, 1.0, 0.0, 1.0); // attachment 1: green
-})";
- ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), fs);
+ ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFSWriteRedGreen);
glUseProgram(program);
constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, kDrawBuffers);
@@ -1534,14 +1538,7 @@
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Setup program to render red into attachment 0 and green into attachment 1.
- const char *fs = R"(#extension GL_EXT_draw_buffers : enable
-precision highp float;
-void main()
-{
- gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0); // attachment 0: red
- gl_FragData[1] = vec4(0.0, 1.0, 0.0, 1.0); // attachment 1: green
-})";
- ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), fs);
+ ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFSWriteRedGreen);
glUseProgram(program);
constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, kDrawBuffers);
@@ -1586,6 +1583,269 @@
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
}
+// Test resolving a multisampled texture with blit, then drawing multisampled again. The latter
+// should not get re-resolved automatically.
+TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlitThenDraw)
+{
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
+ EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
+ EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
+ EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFBO);
+ ANGLE_GL_PROGRAM(blueProgram, essl3_shaders::vs::Passthrough(), essl3_shaders::fs::Blue());
+ drawQuad(blueProgram, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // The resolved FBO should be unaffected by the last draw call.
+ EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
+ EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
+ EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
+ EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
+}
+
+// Test resolving a multisampled texture with blit, then drawing multisampled again. The latter
+// should not get re-resolved automatically. Resoloves color attachment 1.
+TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlitThenDrawAttachment1)
+{
+ ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_draw_buffers"));
+
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ GLTexture msaaTextureRed;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTextureRed.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ msaaTextureRed.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ GLTexture msaaTextureGreen;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, msaaTextureGreen.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D_MULTISAMPLE,
+ msaaTextureGreen.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ // Setup program to render red into attachment 0 and green into attachment 1.
+ ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFSWriteRedGreen);
+ glUseProgram(program);
+ constexpr GLenum kDrawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
+ glDrawBuffers(2, kDrawBuffers);
+
+ drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ glReadBuffer(GL_COLOR_ATTACHMENT1); // Green
+ glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFBO);
+ ANGLE_GL_PROGRAM(blueProgram, essl3_shaders::vs::Passthrough(), essl3_shaders::fs::Blue());
+ drawQuad(blueProgram, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // The resolved FBO should be unaffected by the last draw call.
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::green);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::green);
+}
+
+// Test resolving a multisampled texture with blit, then drawing multisampled again and resolving to
+// same framebuffer.
+TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlitThenDrawThenResolveAgain)
+{
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture;
+ GLFramebuffer resolveFBO;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
+ EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
+ EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
+ EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFBO);
+ ANGLE_GL_PROGRAM(blueProgram, essl3_shaders::vs::Passthrough(), essl3_shaders::fs::Blue());
+ drawQuad(blueProgram, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO);
+ glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ // Verify that the resolve happened correctly
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
+}
+
+// Test resolving a multisampled texture with blit, then drawing multisampled again and resolving to
+// another framebuffer.
+TEST_P(FramebufferTest_ES31, MultisampleResolveWithBlitThenDrawThenResolveAgainToDifferentFBO)
+{
+ constexpr int kSize = 16;
+ glViewport(0, 0, kSize, kSize);
+
+ GLFramebuffer msaaFBO;
+ glBindFramebuffer(GL_FRAMEBUFFER, msaaFBO.get());
+
+ GLTexture texture;
+ glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture.get());
+ glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8, kSize, kSize, false);
+ ASSERT_GL_NO_ERROR();
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
+ texture.get(), 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ ANGLE_GL_PROGRAM(gradientProgram, essl31_shaders::vs::Passthrough(),
+ essl31_shaders::fs::RedGreenGradient());
+ drawQuad(gradientProgram, essl31_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture1;
+ GLFramebuffer resolveFBO1;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture1);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO1);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture1, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO1);
+ glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO1);
+ EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
+ EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
+ EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
+ EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
+
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFBO);
+ ANGLE_GL_PROGRAM(blueProgram, essl3_shaders::vs::Passthrough(), essl3_shaders::fs::Blue());
+ drawQuad(blueProgram, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
+ ASSERT_GL_NO_ERROR();
+
+ // Create another FBO to resolve the multisample buffer into.
+ GLTexture resolveTexture2;
+ GLFramebuffer resolveFBO2;
+ glBindTexture(GL_TEXTURE_2D, resolveTexture2);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO2);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveTexture2, 0);
+ ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFBO);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO2);
+ glBlitFramebuffer(0, 0, kSize, kSize, 0, 0, kSize, kSize, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+
+ // Verify that the resolve happened to the correct FBO
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO2);
+ EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, GLColor::blue);
+ EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, GLColor::blue);
+
+ // The first resolve FBO should be untouched.
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFBO1);
+ EXPECT_PIXEL_NEAR(0, 0, 0, 0, 0, 255, 1.0); // Black
+ EXPECT_PIXEL_NEAR(kSize - 1, 1, 239, 0, 0, 255, 1.0); // Red
+ EXPECT_PIXEL_NEAR(0, kSize - 1, 0, 239, 0, 255, 1.0); // Green
+ EXPECT_PIXEL_NEAR(kSize - 1, kSize - 1, 239, 239, 0, 255, 1.0); // Yellow
+}
+
// If there are no attachments, rendering will be limited to a rectangle having a lower left of
// (0, 0) and an upper right of(width, height), where width and height are the framebuffer
// object's default width and height.