Commit 028df5f5 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Implement transfer path for texture copy

This is primarily in preparation for compressed texture copy, but has the following side effect: - When transfer is possible, it's faster than draw - When texture format does not support draw (but transfer is possible), it will avoid copying through CPU. Bug: angleproject:2670 Change-Id: I49e1b51e6ccec875db3f971106687c7d48c4916f Reviewed-on: https://chromium-review.googlesource.com/c/1470595 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org>
parent 8a64926e
......@@ -421,19 +421,30 @@ angle::Result FramebufferVk::blitWithCopy(ContextVk *contextVk,
bool blitDepthBuffer,
bool blitStencilBuffer)
{
vk::ImageHelper *writeImage = drawRenderTarget->getImageForWrite(&mFramebuffer);
VkImageAspectFlags aspectMask =
vk::GetDepthStencilAspectFlagsForCopy(blitDepthBuffer, blitStencilBuffer);
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(mFramebuffer.recordCommands(contextVk, &commandBuffer));
vk::ImageHelper *writeImage = drawRenderTarget->getImageForWrite(&mFramebuffer);
writeImage->changeLayout(writeImage->getAspectFlags(), vk::ImageLayout::TransferDst,
commandBuffer);
vk::ImageHelper *readImage = readRenderTarget->getImageForRead(
&mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer);
VkImageAspectFlags aspectMask =
vk::GetDepthStencilAspectFlagsForCopy(blitDepthBuffer, blitStencilBuffer);
VkImageSubresourceLayers readSubresource = {};
readSubresource.aspectMask = aspectMask;
readSubresource.mipLevel = 0;
readSubresource.baseArrayLayer = 0;
readSubresource.layerCount = 1;
VkImageSubresourceLayers writeSubresource = readSubresource;
vk::ImageHelper::Copy(readImage, writeImage, gl::Offset(), gl::Offset(),
gl::Extents(copyArea.width, copyArea.height, 1), aspectMask,
commandBuffer);
gl::Extents(copyArea.width, copyArea.height, 1), readSubresource,
writeSubresource, commandBuffer);
return angle::Result::Continue;
}
......
......@@ -140,6 +140,20 @@ vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readi
ASSERT(mImage && mImage->valid());
// TODO(jmadill): Better simultaneous resource access. http://anglebug.com/2679
//
// A better alternative would be:
//
// if (mImage->isLayoutChangeNecessary(layout)
// {
// vk::CommandBuffer *srcLayoutChange;
// ANGLE_TRY(mImage->recordCommands(contextVk, &srcLayoutChange));
// mImage->changeLayout(mImage->getAspectFlags(), layout, srcLayoutChange);
// }
// mImage->addReadDependency(readingResource);
//
// I.e. the transition should happen on a node generated from mImage itself.
// However, this needs context to be available here, or all call sites changed
// to perform the layout transition and set the dependency.
mImage->addWriteDependency(readingResource);
mImage->changeLayout(mImage->getAspectFlags(), layout, commandBuffer);
......
......@@ -27,12 +27,28 @@ namespace rx
{
namespace
{
constexpr VkImageUsageFlags kStagingImageFlags =
constexpr VkImageUsageFlags kDrawStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
constexpr VkImageUsageFlags kTransferStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
constexpr VkFormatFeatureFlags kBlitFeatureFlags =
VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
bool CanCopyWithTransfer(RendererVk *renderer,
const vk::Format &srcFormat,
const vk::Format &destFormat)
{
// NOTE(syoussefi): technically, you can transfer between formats as long as they have the same
// size and are compatible, but for now, let's just support same-format copies with transfer.
return srcFormat.internalFormat == destFormat.internalFormat &&
renderer->hasTextureFormatFeatureBits(srcFormat.vkTextureFormat,
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT) &&
renderer->hasTextureFormatFeatureBits(destFormat.vkTextureFormat,
VK_FORMAT_FEATURE_TRANSFER_DST_BIT);
}
bool CanCopyWithDraw(RendererVk *renderer,
const vk::Format &srcFormat,
const vk::Format &destFormat)
......@@ -314,23 +330,31 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
const vk::Format &srcFormat = framebufferVk->getColorReadRenderTarget()->getImageFormat();
const vk::Format &destFormat = renderer->getFormat(internalFormat.sizedInternalFormat);
bool isViewportFlipY = contextVk->isViewportFlipEnabledForDrawFBO();
// If it's possible to perform the copy with a transfer, that's the best option.
if (!isViewportFlipY && CanCopyWithTransfer(renderer, srcFormat, destFormat))
{
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
return copySubImageImplWithTransfer(contextVk, offsetImageIndex, modifiedDestOffset,
destFormat, 0, clippedSourceArea,
&colorReadRT->getImage());
}
bool forceCpuPath = ForceCpuPathForCopy(renderer, mImage);
// If it's possible to perform the copy with a draw call, do that.
if (CanCopyWithDraw(renderer, srcFormat, destFormat) && !forceCpuPath)
{
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
bool isViewportFlipY = contextVk->isViewportFlipEnabledForDrawFBO();
// Layer count can only be 1 as the source is a framebuffer.
ASSERT(index.getLayerCount() == 1);
ASSERT(offsetImageIndex.getLayerCount() == 1);
ANGLE_TRY(copySubImageImplWithDraw(contextVk, offsetImageIndex, modifiedDestOffset,
destFormat, 0, clippedSourceArea, isViewportFlipY, false,
false, false, &colorReadRT->getImage(),
colorReadRT->getReadImageView()));
return angle::Result::Continue;
return copySubImageImplWithDraw(contextVk, offsetImageIndex, modifiedDestOffset, destFormat,
0, clippedSourceArea, isViewportFlipY, false, false, false,
&colorReadRT->getImage(), colorReadRT->getReadImageView());
}
// Do a CPU readback that does the conversion, and then stage the change to the pixel buffer.
......@@ -364,17 +388,23 @@ angle::Result TextureVk::copySubTextureImpl(ContextVk *contextVk,
const gl::ImageIndex offsetImageIndex = getNativeImageIndex(index);
// If it's possible to perform the copy with a transfer, that's the best option.
if (!unpackFlipY && !unpackPremultiplyAlpha && !unpackUnmultiplyAlpha &&
CanCopyWithTransfer(renderer, sourceVkFormat, destVkFormat))
{
return copySubImageImplWithTransfer(contextVk, offsetImageIndex, destOffset, destVkFormat,
sourceLevel, sourceArea, &source->getImage());
}
bool forceCpuPath = ForceCpuPathForCopy(renderer, mImage);
// If it's possible to perform the copy with a draw call, do that.
if (CanCopyWithDraw(renderer, sourceVkFormat, destVkFormat) && !forceCpuPath)
{
ANGLE_TRY(copySubImageImplWithDraw(contextVk, offsetImageIndex, destOffset, destVkFormat,
return copySubImageImplWithDraw(contextVk, offsetImageIndex, destOffset, destVkFormat,
sourceLevel, sourceArea, false, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
&source->getImage(), &source->getReadImageView()));
return angle::Result::Continue;
&source->getImage(), &source->getReadImageView());
}
if (sourceLevel != 0)
......@@ -429,6 +459,94 @@ angle::Result TextureVk::copySubTextureImpl(ContextVk *contextVk,
return angle::Result::Continue;
}
angle::Result TextureVk::copySubImageImplWithTransfer(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const vk::Format &destFormat,
size_t sourceLevel,
const gl::Rectangle &sourceArea,
vk::ImageHelper *srcImage)
{
RendererVk *renderer = contextVk->getRenderer();
uint32_t level = index.getLevelIndex();
uint32_t baseLayer = index.hasLayer() ? index.getLayerIndex() : 0;
uint32_t layerCount = index.getLayerCount();
gl::Offset srcOffset = {sourceArea.x, sourceArea.y, 0};
gl::Extents extents = {sourceArea.width, sourceArea.height, 1};
// Change source layout if necessary
if (srcImage->isLayoutChangeNecessary(vk::ImageLayout::TransferSrc))
{
vk::CommandBuffer *srcLayoutChange;
ANGLE_TRY(srcImage->recordCommands(contextVk, &srcLayoutChange));
srcImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferSrc,
srcLayoutChange);
}
VkImageSubresourceLayers srcSubresource = {};
srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
srcSubresource.mipLevel = sourceLevel;
srcSubresource.baseArrayLayer = 0;
srcSubresource.layerCount = layerCount;
// If destination is valid, copy the source directly into it.
if (mImage->valid())
{
// Make sure any updates to the image are already flushed.
ANGLE_TRY(ensureImageInitialized(contextVk));
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
// Change the image layout before the transfer
mImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst,
commandBuffer);
// Source's layout change should happen before the copy
srcImage->addReadDependency(mImage);
VkImageSubresourceLayers destSubresource = srcSubresource;
destSubresource.mipLevel = level;
destSubresource.baseArrayLayer = baseLayer;
vk::ImageHelper::Copy(srcImage, mImage, srcOffset, destOffset, extents, srcSubresource,
destSubresource, commandBuffer);
}
else
{
std::unique_ptr<vk::ImageHelper> stagingImage;
// Create a temporary image to stage the copy
stagingImage = std::make_unique<vk::ImageHelper>();
ANGLE_TRY(stagingImage->init2DStaging(contextVk, renderer->getMemoryProperties(),
gl::Extents(sourceArea.width, sourceArea.height, 1),
destFormat, kTransferStagingImageFlags, layerCount));
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(stagingImage->recordCommands(contextVk, &commandBuffer));
// Change the image layout before the transfer
stagingImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::TransferDst,
commandBuffer);
// Source's layout change should happen before the copy
srcImage->addReadDependency(stagingImage.get());
VkImageSubresourceLayers destSubresource = srcSubresource;
destSubresource.mipLevel = 0;
vk::ImageHelper::Copy(srcImage, stagingImage.get(), srcOffset, gl::Offset(), extents,
srcSubresource, destSubresource, commandBuffer);
// Stage the copy for when the image storage is actually created.
mImage->stageSubresourceUpdateFromImage(stagingImage.release(), index, destOffset, extents);
}
return angle::Result::Continue;
}
angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
......@@ -493,7 +611,7 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
ANGLE_TRY(stagingImage->init2DStaging(contextVk, renderer->getMemoryProperties(),
gl::Extents(sourceArea.width, sourceArea.height, 1),
destFormat, kStagingImageFlags, layerCount));
destFormat, kDrawStagingImageFlags, layerCount));
params.destOffset[0] = 0;
params.destOffset[1] = 0;
......
......@@ -207,6 +207,14 @@ class TextureVk : public TextureImpl
bool unpackUnmultiplyAlpha,
TextureVk *source);
angle::Result copySubImageImplWithTransfer(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
const vk::Format &destFormat,
size_t sourceLevel,
const gl::Rectangle &sourceArea,
vk::ImageHelper *srcImage);
angle::Result copySubImageImplWithDraw(ContextVk *contextVk,
const gl::ImageIndex &index,
const gl::Offset &destOffset,
......
......@@ -1607,26 +1607,21 @@ void ImageHelper::Copy(ImageHelper *srcImage,
const gl::Offset &srcOffset,
const gl::Offset &dstOffset,
const gl::Extents &copySize,
VkImageAspectFlags aspectMask,
const VkImageSubresourceLayers &srcSubresource,
const VkImageSubresourceLayers &dstSubresource,
CommandBuffer *commandBuffer)
{
ASSERT(commandBuffer->valid() && srcImage->valid() && dstImage->valid());
srcImage->changeLayout(srcImage->getAspectFlags(), ImageLayout::TransferSrc, commandBuffer);
dstImage->changeLayout(dstImage->getAspectFlags(), ImageLayout::TransferDst, commandBuffer);
ASSERT(srcImage->getCurrentLayout() == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
ASSERT(dstImage->getCurrentLayout() == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageCopy region = {};
region.srcSubresource.aspectMask = aspectMask;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcSubresource = srcSubresource;
region.srcOffset.x = srcOffset.x;
region.srcOffset.y = srcOffset.y;
region.srcOffset.z = srcOffset.z;
region.dstSubresource.aspectMask = aspectMask;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstSubresource = dstSubresource;
region.dstOffset.x = dstOffset.x;
region.dstOffset.y = dstOffset.y;
region.dstOffset.z = dstOffset.z;
......
......@@ -614,7 +614,8 @@ class ImageHelper final : public CommandGraphResource
const gl::Offset &srcOffset,
const gl::Offset &dstOffset,
const gl::Extents &copySize,
VkImageAspectFlags aspectMask,
const VkImageSubresourceLayers &srcSubresources,
const VkImageSubresourceLayers &dstSubresources,
CommandBuffer *commandBuffer);
angle::Result generateMipmapsWithBlit(ContextVk *contextVk, GLuint maxLevel);
......
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