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(
......
......@@ -30,74 +30,166 @@ constexpr int kLineLoopDynamicBufferMinSize = 1024 * 1024;
// This is an arbitrary max. We can change this later if necessary.
constexpr uint32_t kDefaultDescriptorPoolMaxSets = 2048;
// Gets access flags based on layout.
VkAccessFlags GetSrcLayoutAccessFlags(VkImageLayout layout)
{
switch (layout)
{
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return VK_ACCESS_HOST_WRITE_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
return VK_ACCESS_MEMORY_WRITE_BIT;
case VK_IMAGE_LAYOUT_UNDEFINED:
// Note: source access mask never needs a READ bit, as WAR hazards
// don't need memory barriers (just execution barriers).
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return 0;
default:
// TODO(jmadill): Investigate other flags.
UNREACHABLE();
return 0;
}
}
VkAccessFlags GetDstLayoutAccessFlags(VkImageLayout layout)
{
switch (layout)
struct ImageMemoryBarrierData
{
// The Vk layout corresponding to the ImageLayout key.
VkImageLayout layout;
// The stage in which the image is used (or Bottom/Top if not using any specific stage). Unless
// Bottom/Top (Bottom used for transition to and Top used for transition from), the two values
// should match.
VkPipelineStageFlags dstStageMask;
VkPipelineStageFlags srcStageMask;
// Access mask when transitioning into this layout.
VkAccessFlags dstAccessMask;
// Access mask when transitioning out from this layout. Note that source access mask never
// needs a READ bit, as WAR hazards don't need memory barriers (just execution barriers).
VkAccessFlags srcAccessMask;
// If access is read-only, the execution barrier can be skipped altogether if retransitioning to
// the same layout. This is because read-after-read does not need an execution or memory
// barrier.
bool isReadOnlyAccess;
};
// clang-format off
constexpr angle::PackedEnumMap<ImageLayout, ImageMemoryBarrierData> kImageMemoryBarrierData = {
{
ImageLayout::Undefined,
{
VK_IMAGE_LAYOUT_UNDEFINED,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
// Transition to: we don't expect to transition into Undefined.
0,
// Transition from: there's no data in the image to care about.
0,
true,
},
},
{
ImageLayout::PreInitialized,
{
VK_IMAGE_LAYOUT_PREINITIALIZED,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
// Transition to: we don't expect to transition into PreInitialized.
0,
// Transition from: all writes must finish before barrier.
VK_ACCESS_HOST_WRITE_BIT,
false,
},
},
{
ImageLayout::TransferSrc,
{
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
// Transition to: all reads must happen after barrier.
VK_ACCESS_TRANSFER_READ_BIT,
// Transition from: RAR and WAR don't need memory barrier.
0,
true,
},
},
{
ImageLayout::TransferDst,
{
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
// Transition to: all writes must happen after barrier.
VK_ACCESS_TRANSFER_WRITE_BIT,
// Transition from: all writes must finish before barrier.
VK_ACCESS_TRANSFER_WRITE_BIT,
false,
},
},
{
ImageLayout::ComputeShaderReadOnly,
{
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
// Transition to: all reads must happen after barrier.
VK_ACCESS_SHADER_READ_BIT,
// Transition from: RAR and WAR don't need memory barrier.
0,
true,
},
},
{
ImageLayout::ComputeShaderWrite,
{
VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
// Transition to: all reads and writes must happen after barrier.
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
// Transition from: all writes must finish before barrier.
VK_ACCESS_SHADER_WRITE_BIT,
false,
},
},
{
ImageLayout::FragmentShaderReadOnly,
{
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
// Transition to: all reads must happen after barrier.
VK_ACCESS_SHADER_READ_BIT,
// Transition from: RAR and WAR don't need memory barrier.
0,
true,
},
},
{
ImageLayout::ColorAttachment,
{
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
// Transition to: all reads and writes must happen after barrier.
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
// Transition from: all writes must finish before barrier.
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
false,
},
},
{
ImageLayout::DepthStencilAttachment,
{
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
// Transition to: all reads and writes must happen after barrier.
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
// Transition from: all writes must finish before barrier.
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
false,
},
},
{
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
// vkQueuePresentKHR automatically performs the appropriate memory barriers:
ImageLayout::Present,
{
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
// transition to: vkQueuePresentKHR automatically performs the appropriate memory barriers:
//
// > Any writes to memory backing the images referenced by the pImageIndices and
// > pSwapchains members of pPresentInfo, that are available before vkQueuePresentKHR
// > is executed, are automatically made visible to the read access performed by the
// > presentation engine.
return 0;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
// NOTE(syoussefi): compute writes to images require them to be in GENERAL layout,
// and in those cases VK_ACCESS_SHADER_READ/WRITE_BIT are sufficient. However, the
// GENERAL layout covers so many cases that we can't narrow the access flags here.
// The possible solutions are either adding VK_IMAGE_LAYOUT_SHADER_WRITE_OPTIMAL to
// Vulkan, or tracking the necessary access mask alongside the old layout.
return VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
case VK_IMAGE_LAYOUT_UNDEFINED:
return 0;
default:
// TODO(jmadill): Investigate other flags.
UNREACHABLE();
return 0;
}
}
0,
// Transition from: RAR and WAR don't need memory barrier.
0,
true,
},
},
};
// clang-format on
VkImageCreateFlags GetImageCreateFlags(gl::TextureType textureType)
{
......@@ -1124,7 +1216,7 @@ ImageHelper::ImageHelper()
: CommandGraphResource(CommandGraphResourceType::Image),
mFormat(nullptr),
mSamples(0),
mCurrentLayout(VK_IMAGE_LAYOUT_UNDEFINED),
mCurrentLayout(ImageLayout::Undefined),
mLayerCount(0),
mLevelCount(0),
mStagingBuffer(kStagingBufferFlags, kStagingBufferSize, true)
......@@ -1143,7 +1235,7 @@ ImageHelper::ImageHelper(ImageHelper &&other)
mStagingBuffer(std::move(other.mStagingBuffer)),
mSubresourceUpdates(std::move(other.mSubresourceUpdates))
{
other.mCurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
other.mCurrentLayout = ImageLayout::Undefined;
other.mLayerCount = 0;
other.mLevelCount = 0;
}
......@@ -1201,7 +1293,7 @@ angle::Result ImageHelper::init(Context *context,
imageInfo.pQueueFamilyIndices = nullptr;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
mCurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
mCurrentLayout = ImageLayout::Undefined;
ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo));
......@@ -1294,7 +1386,7 @@ void ImageHelper::destroy(VkDevice device)
{
mImage.destroy(device);
mDeviceMemory.destroy(device);
mCurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
mCurrentLayout = ImageLayout::Undefined;
mLayerCount = 0;
mLevelCount = 0;
}
......@@ -1330,7 +1422,7 @@ angle::Result ImageHelper::init2DStaging(Context *context,
mLayerCount = layerCount;
mLevelCount = 1;
mCurrentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
mCurrentLayout = ImageLayout::Undefined;
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
......@@ -1348,7 +1440,7 @@ angle::Result ImageHelper::init2DStaging(Context *context,
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.queueFamilyIndexCount = 0;
imageInfo.pQueueFamilyIndices = nullptr;
imageInfo.initialLayout = mCurrentLayout;
imageInfo.initialLayout = getCurrentLayout();
ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo));
......@@ -1395,18 +1487,39 @@ GLint ImageHelper::getSamples() const
return mSamples;
}
void ImageHelper::changeLayoutWithStages(VkImageAspectFlags aspectMask,
VkImageLayout newLayout,
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
CommandBuffer *commandBuffer)
VkImageLayout ImageHelper::getCurrentLayout() const
{
return kImageMemoryBarrierData[mCurrentLayout].layout;
}
bool ImageHelper::isLayoutChangeNecessary(ImageLayout newLayout)
{
const ImageMemoryBarrierData &layoutData = kImageMemoryBarrierData[mCurrentLayout];
// If transitioning to the same read-only layout (RAR), don't generate a barrier.
bool sameLayoutReadAfterRead = mCurrentLayout == newLayout && layoutData.isReadOnlyAccess;
return !sameLayoutReadAfterRead;
}
void ImageHelper::changeLayout(VkImageAspectFlags aspectMask,
ImageLayout newLayout,
CommandBuffer *commandBuffer)
{
if (!isLayoutChangeNecessary(newLayout))
{
return;
}
const ImageMemoryBarrierData &transitionFrom = kImageMemoryBarrierData[mCurrentLayout];
const ImageMemoryBarrierData &transitionTo = kImageMemoryBarrierData[newLayout];
VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = 0;
imageMemoryBarrier.oldLayout = mCurrentLayout;
imageMemoryBarrier.newLayout = newLayout;
imageMemoryBarrier.srcAccessMask = transitionFrom.srcAccessMask;
imageMemoryBarrier.dstAccessMask = transitionTo.dstAccessMask;
imageMemoryBarrier.oldLayout = transitionFrom.layout;
imageMemoryBarrier.newLayout = transitionTo.layout;
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
imageMemoryBarrier.image = mImage.getHandle();
......@@ -1418,13 +1531,8 @@ void ImageHelper::changeLayoutWithStages(VkImageAspectFlags aspectMask,
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = mLayerCount;
// TODO(jmadill): Test all the permutations of the access flags.
imageMemoryBarrier.srcAccessMask = GetSrcLayoutAccessFlags(mCurrentLayout);
imageMemoryBarrier.dstAccessMask = GetDstLayoutAccessFlags(newLayout);
commandBuffer->pipelineBarrier(srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1,
&imageMemoryBarrier);
commandBuffer->pipelineBarrier(transitionFrom.srcStageMask, transitionTo.dstStageMask, 0, 0,
nullptr, 0, nullptr, 1, &imageMemoryBarrier);
mCurrentLayout = newLayout;
}
......@@ -1447,9 +1555,7 @@ void ImageHelper::clearColorLayer(const VkClearColorValue &color,
{
ASSERT(valid());
changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, ImageLayout::TransferDst, commandBuffer);
VkImageSubresourceRange range = {};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
......@@ -1458,7 +1564,7 @@ void ImageHelper::clearColorLayer(const VkClearColorValue &color,
range.baseArrayLayer = baseArrayLayer;
range.layerCount = layerCount;
commandBuffer->clearColorImage(mImage, mCurrentLayout, color, 1, &range);
commandBuffer->clearColorImage(mImage, getCurrentLayout(), color, 1, &range);
}
void ImageHelper::clearDepthStencil(VkImageAspectFlags imageAspectFlags,
......@@ -1468,9 +1574,7 @@ void ImageHelper::clearDepthStencil(VkImageAspectFlags imageAspectFlags,
{
ASSERT(valid());
changeLayoutWithStages(imageAspectFlags, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
changeLayout(imageAspectFlags, ImageLayout::TransferDst, commandBuffer);
VkImageSubresourceRange clearRange = {
/*aspectMask*/ clearAspectFlags,
......@@ -1480,7 +1584,7 @@ void ImageHelper::clearDepthStencil(VkImageAspectFlags imageAspectFlags,
/*layerCount*/ 1,
};
commandBuffer->clearDepthStencilImage(mImage, mCurrentLayout, depthStencil, 1, &clearRange);
commandBuffer->clearDepthStencilImage(mImage, getCurrentLayout(), depthStencil, 1, &clearRange);
}
gl::Extents ImageHelper::getSize(const gl::ImageIndex &index) const
......@@ -1504,21 +1608,8 @@ void ImageHelper::Copy(ImageHelper *srcImage,
{
ASSERT(commandBuffer->valid() && srcImage->valid() && dstImage->valid());
if (srcImage->getCurrentLayout() != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL &&
srcImage->getCurrentLayout() != VK_IMAGE_LAYOUT_GENERAL)
{
srcImage->changeLayoutWithStages(
srcImage->getAspectFlags(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
}
if (dstImage->getCurrentLayout() != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
dstImage->getCurrentLayout() != VK_IMAGE_LAYOUT_GENERAL)
{
dstImage->changeLayoutWithStages(
dstImage->getAspectFlags(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, commandBuffer);
}
srcImage->changeLayout(srcImage->getAspectFlags(), ImageLayout::TransferSrc, commandBuffer);
dstImage->changeLayout(dstImage->getAspectFlags(), ImageLayout::TransferDst, commandBuffer);
VkImageCopy region = {};
region.srcSubresource.aspectMask = aspectMask;
......@@ -1548,9 +1639,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
commandBuffer);
changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, ImageLayout::TransferDst, commandBuffer);
// We are able to use blitImage since the image format we are using supports it. This
// is a faster way we can generate the mips.
......@@ -1575,7 +1664,7 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
int32_t nextMipHeight = std::max<int32_t>(1, mipHeight >> 1);
barrier.subresourceRange.baseMipLevel = mipLevel - 1;
barrier.oldLayout = mCurrentLayout;
barrier.oldLayout = getCurrentLayout();
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
......@@ -1617,8 +1706,8 @@ angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint
0, 0, nullptr, 0, nullptr, 1, &barrier);
// This is just changing the internal state of the image helper so that the next call
// to changeLayoutWithStages will use this layout as the "oldLayout" argument.
mCurrentLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
// to changeLayout will use this layout as the "oldLayout" argument.
mCurrentLayout = ImageLayout::TransferSrc;
return angle::Result::Continue;
}
......@@ -1903,9 +1992,7 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
// Do not move this above the for loop, otherwise multiple updates can have race conditions
// and not be applied correctly as seen in:
// dEQP-gles2.functional_texture_specification_texsubimage2d_align_2d* tests on Windows AMD
changeLayoutWithStages(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst, commandBuffer);
if (update.updateSource == SubresourceUpdate::UpdateSource::Buffer)
{
......@@ -1917,10 +2004,8 @@ angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
// Note: currently, the staging images are only made through color attachment writes. If
// they were written to otherwise in the future, the src stage of this transition should
// be adjusted appropriately.
update.image.image->changeLayoutWithStages(
VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
commandBuffer);
update.image.image->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT,
vk::ImageLayout::TransferSrc, commandBuffer);
update.image.image->addReadDependency(this);
......
......@@ -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