Commit 7dafe3eb by Shahbaz Youssefi Committed by Commit Bot

Vulkan: optimize image memory barriers

Each image was tracking its current layout, but not the pipeline stage it was used. Additionally, the barrier access masks were inferred from the layout. This incurred two inefficiencies: - The src pipeline stage mask often included all stages, causing unnecessarily heavy barriers. - The access masks included all possible accesses by a layout, which in some cases was overkill, like VK_ACCESS_MEMORY_WRITE_BIT for VK_IMAGE_LAYOUT_GENERAL (which will eventually used for compute shader output). This change instead creates an enum where each element represents the layout, the stage and access masks when transitioning into the layout and the stage and access masks when transitioning out of that layout. The image will instead track a value of this enum (instead of VkImageLayout), which allows it to create the layout transition barriers as tight as possible, since it includes all the necessary information. Bug: angleproject:2999 Change-Id: I91535ce06d10530a6fc217ad3b94b7e288521e25 Reviewed-on: https://chromium-review.googlesource.com/c/1440074 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent f3e823db
......@@ -427,7 +427,7 @@ angle::Result FramebufferVk::blitWithCopy(ContextVk *contextVk,
ANGLE_TRY(mFramebuffer.recordCommands(contextVk, &commandBuffer));
vk::ImageHelper *readImage = readRenderTarget->getImageForRead(
&mFramebuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, commandBuffer);
&mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer);
VkImageAspectFlags aspectMask =
vk::GetDepthStencilAspectFlagsForCopy(blitDepthBuffer, blitStencilBuffer);
......@@ -505,9 +505,8 @@ angle::Result FramebufferVk::blitWithReadback(ContextVk *contextVk,
// destination target.
vk::ImageHelper *imageForWrite = drawRenderTarget->getImageForWrite(&mFramebuffer);
imageForWrite->changeLayoutWithStages(
imageForWrite->getAspectFlags(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, commandBuffer);
imageForWrite->changeLayout(imageForWrite->getAspectFlags(), vk::ImageLayout::TransferDst,
commandBuffer);
commandBuffer->copyBufferToImage(destBufferHandle, imageForWrite->getImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyRegion);
......@@ -667,7 +666,7 @@ angle::Result FramebufferVk::blitWithCommand(ContextVk *contextVk,
colorBlit ? VK_IMAGE_ASPECT_COLOR_BIT
: vk::GetDepthStencilAspectFlags(readImageFormat.textureFormat());
vk::ImageHelper *srcImage = readRenderTarget->getImageForRead(
&mFramebuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, commandBuffer);
&mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer);
const gl::Extents sourceFrameBufferExtents = readRenderTarget->getImageExtents();
gl::Rectangle readRect = readRectIn;
......@@ -702,9 +701,7 @@ angle::Result FramebufferVk::blitWithCommand(ContextVk *contextVk,
// Requirement of the copyImageToBuffer, the dst image must be in
// VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL layout.
dstImage->changeLayoutWithStages(aspectMask, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, commandBuffer);
dstImage->changeLayout(aspectMask, vk::ImageLayout::TransferDst, commandBuffer);
commandBuffer->blitImage(srcImage->getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dstImage->getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit,
......@@ -1108,8 +1105,8 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk,
// Note that although we're reading from the image, we need to update the layout below.
vk::ImageHelper *srcImage = renderTarget->getImageForRead(
&mFramebuffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, commandBuffer);
vk::ImageHelper *srcImage =
renderTarget->getImageForRead(&mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer);
const angle::Format *readFormat = &srcImage->getFormat().textureFormat();
......
......@@ -860,15 +860,13 @@ angle::Result ProgramVk::updateTexturesDescriptorSet(ContextVk *contextVk,
vk::ImageHelper &image = textureVk->getImage();
// Ensure the image is in read-only layout
if (image.getCurrentLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
if (image.isLayoutChangeNecessary(vk::ImageLayout::FragmentShaderReadOnly))
{
vk::CommandBuffer *srcLayoutChange;
ANGLE_TRY(image.recordCommands(contextVk, &srcLayoutChange));
image.changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
srcLayoutChange);
image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT,
vk::ImageLayout::FragmentShaderReadOnly, srcLayoutChange);
}
image.addReadDependency(framebuffer);
......
......@@ -59,10 +59,8 @@ void RenderTargetVk::onColorDraw(vk::FramebufferHelper *framebufferVk,
renderPassDesc->packAttachment(mImage->getFormat());
// TODO(jmadill): Use automatic layout transition. http://anglebug.com/2361
mImage->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, commandBuffer);
mImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorAttachment,
commandBuffer);
// Set up dependencies between the RT resource and the Framebuffer.
mImage->addWriteDependency(framebufferVk);
......@@ -82,9 +80,7 @@ void RenderTargetVk::onDepthStencilDraw(vk::FramebufferHelper *framebufferVk,
const angle::Format &format = mImage->getFormat().textureFormat();
VkImageAspectFlags aspectFlags = vk::GetDepthStencilAspectFlags(format);
mImage->changeLayoutWithStages(aspectFlags, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, commandBuffer);
mImage->changeLayout(aspectFlags, vk::ImageLayout::DepthStencilAttachment, commandBuffer);
// Set up dependencies between the RT resource and the Framebuffer.
mImage->addWriteDependency(framebufferVk);
......@@ -134,7 +130,7 @@ void RenderTargetVk::updateSwapchainImage(vk::ImageHelper *image, vk::ImageView
}
vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readingResource,
VkImageLayout layout,
vk::ImageLayout layout,
vk::CommandBuffer *commandBuffer)
{
ASSERT(mImage && mImage->valid());
......@@ -142,9 +138,7 @@ vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readi
// TODO(jmadill): Better simultaneous resource access. http://anglebug.com/2679
mImage->addWriteDependency(readingResource);
mImage->changeLayoutWithStages(mImage->getAspectFlags(), layout,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, commandBuffer);
mImage->changeLayout(mImage->getAspectFlags(), layout, commandBuffer);
return mImage;
}
......
......@@ -14,6 +14,7 @@
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/renderer/renderer_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
namespace rx
{
......@@ -62,7 +63,7 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// getImageForRead will also transition the resource to the given layout.
vk::ImageHelper *getImageForRead(vk::CommandGraphResource *readingResource,
VkImageLayout layout,
vk::ImageLayout layout,
vk::CommandBuffer *commandBuffer);
vk::ImageHelper *getImageForWrite(vk::CommandGraphResource *writingResource) const;
......
......@@ -572,15 +572,12 @@ angle::Result WindowSurfaceVk::swapImpl(DisplayVk *displayVk, EGLint *rects, EGL
ANGLE_TRY(renderer->finishToSerial(displayVk, mSwapSerials[mCurrentSwapSerialIndex]));
}
vk::CommandBuffer *swapCommands = nullptr;
ANGLE_TRY(mSwapchainImages[mCurrentSwapchainImageIndex].image.recordCommands(displayVk,
&swapCommands));
SwapchainImage &image = mSwapchainImages[mCurrentSwapchainImageIndex];
image.image.changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, swapCommands);
vk::CommandBuffer *swapCommands = nullptr;
ANGLE_TRY(image.image.recordCommands(displayVk, &swapCommands));
image.image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::Present, swapCommands);
ANGLE_TRY(renderer->flush(displayVk));
......
......@@ -628,9 +628,7 @@ angle::Result TextureVk::copyImageDataToBuffer(ContextVk *contextVk,
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
// Requirement of the copyImageToBuffer, the source image must be in SRC_OPTIMAL layout.
mImage->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, commandBuffer);
mImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferSrc, commandBuffer);
// Allocate enough memory to copy the sourceArea region of the source texture into its pixel
// buffer.
......
......@@ -733,24 +733,20 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
pipelineDesc.setScissor(scissor);
// Change source layout outside render pass
if (src->getCurrentLayout() != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
if (src->isLayoutChangeNecessary(vk::ImageLayout::FragmentShaderReadOnly))
{
vk::CommandBuffer *srcLayoutChange;
ANGLE_TRY(src->recordCommands(contextVk, &srcLayoutChange));
src->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, srcLayoutChange);
src->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::FragmentShaderReadOnly,
srcLayoutChange);
}
// Change destination layout outside render pass as well
vk::CommandBuffer *destLayoutChange;
ANGLE_TRY(dest->recordCommands(contextVk, &destLayoutChange));
dest->changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, destLayoutChange);
dest->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorAttachment,
destLayoutChange);
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(
......
......@@ -471,6 +471,53 @@ class BufferHelper final : public CommandGraphResource
VkFlags mCurrentReadAccess;
};
// Imagine an image going through a few layout transitions:
//
// srcStage 1 dstStage 2 srcStage 2 dstStage 3
// Layout 1 ------Transition 1-----> Layout 2 ------Transition 2------> Layout 3
// srcAccess 1 dstAccess 2 srcAccess 2 dstAccess 3
// \_________________ ___________________/
// \/
// A transition
//
// Every transition requires 6 pieces of information: from/to layouts, src/dst stage masks and
// src/dst access masks. At the moment we decide to transition the image to Layout 2 (i.e.
// Transition 1), we need to have Layout 1, srcStage 1 and srcAccess 1 stored as history of the
// image. To perform the transition, we need to know Layout 2, dstStage 2 and dstAccess 2.
// Additionally, we need to know srcStage 2 and srcAccess 2 to retain them for the next transition.
//
// That is, with the history kept, on every new transition we need 5 pieces of new information:
// layout/dstStage/dstAccess to transition into the layout, and srcStage/srcAccess for the future
// transition out from it. Given the small number of possible combinations of these values, an
// enum is used were each value encapsulates these 5 pieces of information:
//
// +--------------------------------+
// srcStage 1 | dstStage 2 srcStage 2 | dstStage 3
// Layout 1 ------Transition 1-----> Layout 2 ------Transition 2------> Layout 3
// srcAccess 1 |dstAccess 2 srcAccess 2| dstAccess 3
// +--------------- ---------------+
// \/
// One enum value
//
// Note that, while generally dstStage for the to-transition and srcStage for the from-transition
// are the same, they may occasionally be BOTTOM_OF_PIPE and TOP_OF_PIPE respectively.
enum class ImageLayout
{
Undefined = 0,
PreInitialized = 1,
TransferSrc = 2,
TransferDst = 3,
ComputeShaderReadOnly = 4,
ComputeShaderWrite = 5,
FragmentShaderReadOnly = 6,
ColorAttachment = 7,
DepthStencilAttachment = 8,
Present = 9,
InvalidEnum = 10,
EnumCount = 10,
};
class ImageHelper final : public CommandGraphResource
{
public:
......@@ -541,13 +588,7 @@ class ImageHelper final : public CommandGraphResource
const Format &getFormat() const;
GLint getSamples() const;
VkImageLayout getCurrentLayout() const { return mCurrentLayout; }
void changeLayoutWithStages(VkImageAspectFlags aspectMask,
VkImageLayout newLayout,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
CommandBuffer *commandBuffer);
VkImageLayout getCurrentLayout() const;
void clearColor(const VkClearColorValue &color,
uint32_t baseMipLevel,
......@@ -624,6 +665,15 @@ class ImageHelper final : public CommandGraphResource
bool hasStagedUpdates() const;
// changeLayout automatically skips the layout change if it's unnecessary. This function can be
// used to prevent creating a command graph node and subsequently a command buffer for the sole
// purpose of performing a transition (which may then not be issued).
bool isLayoutChangeNecessary(ImageLayout newLayout);
void changeLayout(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
CommandBuffer *commandBuffer);
private:
struct SubresourceUpdate
{
......@@ -675,7 +725,7 @@ class ImageHelper final : public CommandGraphResource
GLint mSamples;
// Current state.
VkImageLayout mCurrentLayout;
ImageLayout mCurrentLayout;
// Cached properties.
uint32_t mLayerCount;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment