Skip to content

Commit

Permalink
Vulkan: Optimize color invalidates
Browse files Browse the repository at this point in the history
By not flushing the render pass when there is an invalidate.

Previously, the tracking of invalidation, write to attachments,
management of load/store ops, and whether image contents are defined or
not have all been unified between color and depth/stencil images.

As such, it is possible to not close the render pass when a color image
is invalidated just as is not done for depth/stencil images.  Together
with the optimization to resolve attachments [1], it is now finally
possible to efficiently do MSAA rendering with ANGLE.

Note that the optimization to use resolve attachments for depth/stencil
is not yet implemented.  For color only, the perf test added in [2]
shows the following improvement on Pixel 6:

- Single sampled rendering: ~2.73ms
- Resolve + invalidate (before optimizations): ~3.54ms
- Resolve + invalidate (after this change): ~2.85ms

[1]: https://chromium-review.googlesource.com/c/angle/angle/+/5388492
[2]: https://chromium-review.googlesource.com/c/angle/angle/+/5392548

Bug: angleproject:7551
Change-Id: I008adf9f53df97ab464b0a0399f0b312bf4d0d3f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/5391905
Reviewed-by: Yuxin Hu <[email protected]>
Reviewed-by: Amirali Abdolrashidi <[email protected]>
Commit-Queue: Shahbaz Youssefi <[email protected]>
  • Loading branch information
ShabbyX authored and Angle LUCI CQ committed Mar 28, 2024
1 parent 21b6899 commit 0e9254b
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 18 deletions.
12 changes: 0 additions & 12 deletions src/libANGLE/renderer/vulkan/FramebufferVk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2094,18 +2094,6 @@ angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk,
dsState, invalidateArea);
}
}
if (invalidateColorBuffers.any())
{
// Only end the render pass if invalidating at least one color buffer. Do not end the
// render pass if only the depth and/or stencil buffer is invalidated. At least one
// application invalidates those every frame, disables depth, and then continues to
// draw only to the color buffer.
//
// Since we are not aware of any application that invalidates a color buffer and
// continues to draw to it, we leave that unoptimized.
ANGLE_TRY(contextVk->flushCommandsAndEndRenderPass(
RenderPassClosureReason::ColorBufferInvalidate));
}
}

return angle::Result::Continue;
Expand Down
90 changes: 84 additions & 6 deletions src/tests/gl_tests/VulkanPerformanceCounterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,12 @@ TEST_P(VulkanPerformanceCounterTest_ES31, MultisampleResolveWithBlit)
// Test resolving a multisampled texture with blit and then invalidate the msaa buffer
TEST_P(VulkanPerformanceCounterTest_ES31, ResolveToFBOWithInvalidate)
{
angle::VulkanPerfCounters expected;

// Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0)
setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected);
expected.colorAttachmentResolves = getPerfCounters().colorAttachmentResolves + 1;

constexpr int kWindowWidth = 4;
constexpr int kWindowHeight = 4;
GLTexture resolveTexture;
Expand Down Expand Up @@ -1592,11 +1598,13 @@ TEST_P(VulkanPerformanceCounterTest_ES31, ResolveToFBOWithInvalidate)
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, &attachment);
glBindFramebuffer(GL_FRAMEBUFFER, resolveFBO);

EXPECT_EQ(getPerfCounters().colorStoreOpStores, 1u);

// Top-left pixels should be all red.
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);

EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses);
EXPECT_EQ(expected.colorAttachmentResolves, getPerfCounters().colorAttachmentResolves);
EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected);

ASSERT_GL_NO_ERROR();
}

Expand Down Expand Up @@ -1722,14 +1730,11 @@ void main()
ASSERT_GL_NO_ERROR();

EXPECT_EQ(expected.renderPasses, getPerfCounters().renderPasses);
// Currently not optimized well. http://anglebug.com/7551
EXPECT_EQ(expected.colorAttachmentResolves, getPerfCounters().colorAttachmentResolves);
EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected);
};

// TODO: currently, the Vulkan backend flushes the render pass after the first invalidate, so
// the second resolve doesn't get a chance to be optimized. http://anglebug.com/7551
// test(GLColor::blue, GLColor::yellow, Invalidate::AfterEachResolve);
test(GLColor::blue, GLColor::yellow, Invalidate::AfterEachResolve);
test(GLColor::cyan, GLColor::magenta, Invalidate::AllAtEnd);
}

Expand Down Expand Up @@ -6756,6 +6761,79 @@ TEST_P(VulkanPerformanceCounterTest, InvalidateThenRepeatedClearThenReadbackThen
EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected);
}

// Test that draw after invalidate restores the contents of the color image.
TEST_P(VulkanPerformanceCounterTest, InvalidateThenDraw)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName));

angle::VulkanPerfCounters expected;

constexpr GLsizei kSize = 2;

GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);

// Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+1, StoreNones+0)
setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 1, 0, &expected);

ANGLE_GL_PROGRAM(blue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
drawQuad(blue, essl1_shaders::PositionAttrib(), 0);

// Invalidate it such that the contents are marked as undefined
const GLenum discards[] = {GL_COLOR_ATTACHMENT0};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards);

// Draw again.
ANGLE_GL_PROGRAM(green, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuad(green, essl1_shaders::PositionAttrib(), 0);

EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected);
}

// Test that masked draw after invalidate does NOT restore the contents of the color image.
TEST_P(VulkanPerformanceCounterTest, InvalidateThenMaskedDraw)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled(kPerfMonitorExtensionName));

angle::VulkanPerfCounters expected;

constexpr GLsizei kSize = 2;

GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);

// Expect rpCount+1, color(Clears+0, Loads+0, LoadNones+0, Stores+0, StoreNones+0)
setExpectedCountersForColorOps(getPerfCounters(), 1, 0, 0, 0, 0, 0, &expected);

ANGLE_GL_PROGRAM(blue, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
drawQuad(blue, essl1_shaders::PositionAttrib(), 0);

// Invalidate it such that the contents are marked as undefined
const GLenum discards[] = {GL_COLOR_ATTACHMENT0};
glInvalidateFramebuffer(GL_FRAMEBUFFER, 1, discards);

// Draw again.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
ANGLE_GL_PROGRAM(green, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuad(green, essl1_shaders::PositionAttrib(), 0);

// Break the render pass
glFinish();

EXPECT_COLOR_OP_COUNTERS(getPerfCounters(), expected);
}

// Tests that the submission counters count the implicit submission in eglSwapBuffers().
TEST_P(VulkanPerformanceCounterTest, VerifySubmitCountersForSwapBuffer)
{
Expand Down

0 comments on commit 0e9254b

Please sign in to comment.