Vulkan: Use render pass ops to clear images when possible
On tiling GPUs, render pass loadOp and stencilLoadOp can be used to very
cheaply clear an image as it is being render to. This change uses this
feature to clear render targets when possible.
Bug: angleproject:2361
Change-Id: Ic4bdc908873f4802760d549f4893f84a47beac0f
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1500576
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
index 3d46a06..347b45b 100644
--- a/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
+++ b/src/libANGLE/renderer/vulkan/FramebufferVk.cpp
@@ -217,11 +217,38 @@
}
}
+ // If scissor is enabled, but covers the whole of framebuffer, it can be considered disabled for
+ // the sake of clear.
+ const gl::Rectangle &scissorRect = glState.getScissor();
+ const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
+ mState.getDimensions().height);
+ bool isScissorTestEffectivelyEnabled =
+ glState.isScissorTestEnabled() && scissorRect != renderArea;
+
+ // We can use render pass load ops if clearing depth/stencil or unmasked color. If there's a
+ // depth mask, depth clearing is disabled. If there's a stencil mask, the clear value is
+ // already masked. There is no depth/stencil condition prohibiting the use of render pass
+ // loadOp.
+ VkColorComponentFlags colorMaskFlags = contextVk->getClearColorMask();
+ bool maskedClearColor =
+ clearColor && (mActiveColorComponents & colorMaskFlags) != mActiveColorComponents;
+
+ if (!maskedClearColor && !isScissorTestEffectivelyEnabled &&
+ !contextVk->getRenderer()->getFeatures().disableClearWithRenderPassLoadOp)
+ {
+ // Note(syoussefi): A possible optimization could be to allow depth/stencil clear in
+ // presence of color mask, and leave the color clear to the following steps, i.e. start a
+ // render pass with depth/stencil = Clear and have a subsequent draw call within the render
+ // pass to clear the color. That could render some of the depth/stencil clear paths below
+ // unnecessary.
+ return clearWithRenderPassOp(contextVk, clearColor, clearDepth, clearStencil,
+ contextVk->getClearColorValue().color, clearDepthStencilValue);
+ }
+
// The most costly clear mode is when we need to mask out specific color channels. This can
// only be done with a draw call. The scissor region however can easily be integrated with
// this method. Similarly for depth/stencil clear.
- VkColorComponentFlags colorMaskFlags = contextVk->getClearColorMask();
- if (clearColor && (mActiveColorComponents & colorMaskFlags) != mActiveColorComponents)
+ if (maskedClearColor)
{
ANGLE_TRY(clearWithDraw(contextVk, colorMaskFlags));
@@ -239,7 +266,7 @@
return angle::Result::Continue;
}
- if (glState.isScissorTestEnabled())
+ if (isScissorTestEffectivelyEnabled)
{
// With scissor test enabled, we clear very differently and we don't need to access
// the image inside each attachment we can just use clearCmdAttachments with our
@@ -842,14 +869,14 @@
return angle::Result::Continue;
}
- vk::RenderPass *renderPass = nullptr;
- ANGLE_TRY(
- contextVk->getRenderer()->getCompatibleRenderPass(contextVk, mRenderPassDesc, &renderPass));
+ vk::RenderPass *compatibleRenderPass = nullptr;
+ ANGLE_TRY(contextVk->getRenderer()->getCompatibleRenderPass(contextVk, mRenderPassDesc,
+ &compatibleRenderPass));
// If we've a Framebuffer provided by a Surface (default FBO/backbuffer), query it.
if (mBackbuffer)
{
- return mBackbuffer->getCurrentFramebuffer(contextVk, *renderPass, framebufferOut);
+ return mBackbuffer->getCurrentFramebuffer(contextVk, *compatibleRenderPass, framebufferOut);
}
// Gather VkImageViews over all FBO attachments, also size of attached region.
@@ -884,7 +911,7 @@
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.flags = 0;
- framebufferInfo.renderPass = renderPass->getHandle();
+ framebufferInfo.renderPass = compatibleRenderPass->getHandle();
framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
framebufferInfo.pAttachments = attachments.data();
framebufferInfo.width = static_cast<uint32_t>(attachmentsSize.width);
@@ -897,6 +924,19 @@
return angle::Result::Continue;
}
+angle::Result FramebufferVk::clearWithRenderPassOp(
+ ContextVk *contextVk,
+ bool clearColor,
+ bool clearDepth,
+ bool clearStencil,
+ const VkClearColorValue &clearColorValue,
+ const VkClearDepthStencilValue &clearDepthStencilValue)
+{
+ vk::CommandBuffer *commandBuffer;
+ return startNewRenderPassImpl(contextVk, clearColor, clearDepth, clearStencil, clearColorValue,
+ clearDepthStencilValue, &commandBuffer);
+}
+
angle::Result FramebufferVk::clearWithClearAttachments(
ContextVk *contextVk,
bool clearColor,
@@ -1056,15 +1096,34 @@
angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
vk::CommandBuffer **commandBufferOut)
{
+ VkClearColorValue unusedColor = {};
+ VkClearDepthStencilValue unusedDepthStencil = {};
+
+ return startNewRenderPassImpl(contextVk, false, false, false, unusedColor, unusedDepthStencil,
+ commandBufferOut);
+}
+
+angle::Result FramebufferVk::startNewRenderPassImpl(
+ ContextVk *contextVk,
+ bool clearColor,
+ bool clearDepth,
+ bool clearStencil,
+ const VkClearColorValue &clearColorValue,
+ const VkClearDepthStencilValue &clearDepthStencilValue,
+ vk::CommandBuffer **commandBufferOut)
+{
vk::Framebuffer *framebuffer = nullptr;
ANGLE_TRY(getFramebuffer(contextVk, &framebuffer));
- // TODO(jmadill): Proper clear value implementation. http://anglebug.com/2361
+ vk::AttachmentOpsArray renderPassAttachmentOps;
std::vector<VkClearValue> attachmentClearValues;
vk::CommandBuffer *writeCommands = nullptr;
ANGLE_TRY(mFramebuffer.recordCommands(contextVk, &writeCommands));
+ VkClearValue clearValue;
+ clearValue.color = clearColorValue;
+
vk::RenderPassDesc renderPassDesc;
// Initialize RenderPass info.
@@ -1076,21 +1135,58 @@
ASSERT(colorRenderTarget);
colorRenderTarget->onColorDraw(&mFramebuffer, writeCommands, &renderPassDesc);
- attachmentClearValues.emplace_back(contextVk->getClearColorValue());
+
+ VkAttachmentLoadOp colorLoadOp =
+ clearColor ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
+
+ renderPassAttachmentOps.setLayout(attachmentClearValues.size(),
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
+ renderPassAttachmentOps.setLoadOp(attachmentClearValues.size(), colorLoadOp,
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE);
+ renderPassAttachmentOps.setStoreOp(attachmentClearValues.size(),
+ VK_ATTACHMENT_STORE_OP_STORE,
+ VK_ATTACHMENT_STORE_OP_DONT_CARE);
+
+ // If the render target doesn't have alpha, but its emulated format has it, clear the alpha
+ // to 1.
+ VkClearValue value = clearValue;
+ if (mEmulatedAlphaAttachmentMask[colorIndex])
+ {
+ value.color.float32[3] = 1.0;
+ }
+
+ attachmentClearValues.emplace_back(value);
}
RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil();
if (depthStencilRenderTarget)
{
depthStencilRenderTarget->onDepthStencilDraw(&mFramebuffer, writeCommands, &renderPassDesc);
- attachmentClearValues.emplace_back(contextVk->getClearDepthStencilValue());
+
+ VkAttachmentLoadOp depthLoadOp =
+ clearDepth ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
+ VkAttachmentLoadOp stencilLoadOp =
+ clearStencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
+
+ renderPassAttachmentOps.setLayout(attachmentClearValues.size(),
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+ VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
+ renderPassAttachmentOps.setLoadOp(attachmentClearValues.size(), depthLoadOp, stencilLoadOp);
+ renderPassAttachmentOps.setStoreOp(attachmentClearValues.size(),
+ VK_ATTACHMENT_STORE_OP_STORE,
+ VK_ATTACHMENT_STORE_OP_STORE);
+
+ clearValue.depthStencil = clearDepthStencilValue;
+ attachmentClearValues.emplace_back(clearValue);
}
gl::Rectangle renderArea =
gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
return mFramebuffer.beginRenderPass(contextVk, *framebuffer, renderArea, mRenderPassDesc,
- attachmentClearValues, commandBufferOut);
+ renderPassAttachmentOps, attachmentClearValues,
+ commandBufferOut);
}
void FramebufferVk::updateActiveColorMasks(size_t colorIndex, bool r, bool g, bool b, bool a)