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, ...@@ -421,19 +421,30 @@ angle::Result FramebufferVk::blitWithCopy(ContextVk *contextVk,
bool blitDepthBuffer, bool blitDepthBuffer,
bool blitStencilBuffer) bool blitStencilBuffer)
{ {
vk::ImageHelper *writeImage = drawRenderTarget->getImageForWrite(&mFramebuffer); VkImageAspectFlags aspectMask =
vk::GetDepthStencilAspectFlagsForCopy(blitDepthBuffer, blitStencilBuffer);
vk::CommandBuffer *commandBuffer; vk::CommandBuffer *commandBuffer;
ANGLE_TRY(mFramebuffer.recordCommands(contextVk, &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( vk::ImageHelper *readImage = readRenderTarget->getImageForRead(
&mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer); &mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer);
VkImageAspectFlags aspectMask = VkImageSubresourceLayers readSubresource = {};
vk::GetDepthStencilAspectFlagsForCopy(blitDepthBuffer, blitStencilBuffer); readSubresource.aspectMask = aspectMask;
readSubresource.mipLevel = 0;
readSubresource.baseArrayLayer = 0;
readSubresource.layerCount = 1;
VkImageSubresourceLayers writeSubresource = readSubresource;
vk::ImageHelper::Copy(readImage, writeImage, gl::Offset(), gl::Offset(), vk::ImageHelper::Copy(readImage, writeImage, gl::Offset(), gl::Offset(),
gl::Extents(copyArea.width, copyArea.height, 1), aspectMask, gl::Extents(copyArea.width, copyArea.height, 1), readSubresource,
commandBuffer); writeSubresource, commandBuffer);
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -140,6 +140,20 @@ vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readi ...@@ -140,6 +140,20 @@ vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readi
ASSERT(mImage && mImage->valid()); ASSERT(mImage && mImage->valid());
// TODO(jmadill): Better simultaneous resource access. http://anglebug.com/2679 // 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->addWriteDependency(readingResource);
mImage->changeLayout(mImage->getAspectFlags(), layout, commandBuffer); mImage->changeLayout(mImage->getAspectFlags(), layout, commandBuffer);
......
...@@ -27,12 +27,28 @@ namespace rx ...@@ -27,12 +27,28 @@ namespace rx
{ {
namespace namespace
{ {
constexpr VkImageUsageFlags kStagingImageFlags = constexpr VkImageUsageFlags kDrawStagingImageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 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 = constexpr VkFormatFeatureFlags kBlitFeatureFlags =
VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT; 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, bool CanCopyWithDraw(RendererVk *renderer,
const vk::Format &srcFormat, const vk::Format &srcFormat,
const vk::Format &destFormat) const vk::Format &destFormat)
...@@ -314,23 +330,31 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context, ...@@ -314,23 +330,31 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
const vk::Format &srcFormat = framebufferVk->getColorReadRenderTarget()->getImageFormat(); const vk::Format &srcFormat = framebufferVk->getColorReadRenderTarget()->getImageFormat();
const vk::Format &destFormat = renderer->getFormat(internalFormat.sizedInternalFormat); 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); bool forceCpuPath = ForceCpuPathForCopy(renderer, mImage);
// If it's possible to perform the copy with a draw call, do that. // If it's possible to perform the copy with a draw call, do that.
if (CanCopyWithDraw(renderer, srcFormat, destFormat) && !forceCpuPath) if (CanCopyWithDraw(renderer, srcFormat, destFormat) && !forceCpuPath)
{ {
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget(); RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
bool isViewportFlipY = contextVk->isViewportFlipEnabledForDrawFBO();
// Layer count can only be 1 as the source is a framebuffer. // 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, return copySubImageImplWithDraw(contextVk, offsetImageIndex, modifiedDestOffset, destFormat,
destFormat, 0, clippedSourceArea, isViewportFlipY, false, 0, clippedSourceArea, isViewportFlipY, false, false, false,
false, false, &colorReadRT->getImage(), &colorReadRT->getImage(), colorReadRT->getReadImageView());
colorReadRT->getReadImageView()));
return angle::Result::Continue;
} }
// Do a CPU readback that does the conversion, and then stage the change to the pixel buffer. // 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, ...@@ -364,17 +388,23 @@ angle::Result TextureVk::copySubTextureImpl(ContextVk *contextVk,
const gl::ImageIndex offsetImageIndex = getNativeImageIndex(index); 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); bool forceCpuPath = ForceCpuPathForCopy(renderer, mImage);
// If it's possible to perform the copy with a draw call, do that. // If it's possible to perform the copy with a draw call, do that.
if (CanCopyWithDraw(renderer, sourceVkFormat, destVkFormat) && !forceCpuPath) if (CanCopyWithDraw(renderer, sourceVkFormat, destVkFormat) && !forceCpuPath)
{ {
ANGLE_TRY(copySubImageImplWithDraw(contextVk, offsetImageIndex, destOffset, destVkFormat, return copySubImageImplWithDraw(contextVk, offsetImageIndex, destOffset, destVkFormat,
sourceLevel, sourceArea, false, unpackFlipY, sourceLevel, sourceArea, false, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha, unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
&source->getImage(), &source->getReadImageView())); &source->getImage(), &source->getReadImageView());
return angle::Result::Continue;
} }
if (sourceLevel != 0) if (sourceLevel != 0)
...@@ -429,6 +459,94 @@ angle::Result TextureVk::copySubTextureImpl(ContextVk *contextVk, ...@@ -429,6 +459,94 @@ angle::Result TextureVk::copySubTextureImpl(ContextVk *contextVk,
return angle::Result::Continue; 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, angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
const gl::ImageIndex &index, const gl::ImageIndex &index,
const gl::Offset &destOffset, const gl::Offset &destOffset,
...@@ -493,7 +611,7 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk, ...@@ -493,7 +611,7 @@ angle::Result TextureVk::copySubImageImplWithDraw(ContextVk *contextVk,
ANGLE_TRY(stagingImage->init2DStaging(contextVk, renderer->getMemoryProperties(), ANGLE_TRY(stagingImage->init2DStaging(contextVk, renderer->getMemoryProperties(),
gl::Extents(sourceArea.width, sourceArea.height, 1), gl::Extents(sourceArea.width, sourceArea.height, 1),
destFormat, kStagingImageFlags, layerCount)); destFormat, kDrawStagingImageFlags, layerCount));
params.destOffset[0] = 0; params.destOffset[0] = 0;
params.destOffset[1] = 0; params.destOffset[1] = 0;
......
...@@ -207,6 +207,14 @@ class TextureVk : public TextureImpl ...@@ -207,6 +207,14 @@ class TextureVk : public TextureImpl
bool unpackUnmultiplyAlpha, bool unpackUnmultiplyAlpha,
TextureVk *source); 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, angle::Result copySubImageImplWithDraw(ContextVk *contextVk,
const gl::ImageIndex &index, const gl::ImageIndex &index,
const gl::Offset &destOffset, const gl::Offset &destOffset,
......
...@@ -1607,26 +1607,21 @@ void ImageHelper::Copy(ImageHelper *srcImage, ...@@ -1607,26 +1607,21 @@ void ImageHelper::Copy(ImageHelper *srcImage,
const gl::Offset &srcOffset, const gl::Offset &srcOffset,
const gl::Offset &dstOffset, const gl::Offset &dstOffset,
const gl::Extents &copySize, const gl::Extents &copySize,
VkImageAspectFlags aspectMask, const VkImageSubresourceLayers &srcSubresource,
const VkImageSubresourceLayers &dstSubresource,
CommandBuffer *commandBuffer) CommandBuffer *commandBuffer)
{ {
ASSERT(commandBuffer->valid() && srcImage->valid() && dstImage->valid()); ASSERT(commandBuffer->valid() && srcImage->valid() && dstImage->valid());
srcImage->changeLayout(srcImage->getAspectFlags(), ImageLayout::TransferSrc, commandBuffer); ASSERT(srcImage->getCurrentLayout() == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
dstImage->changeLayout(dstImage->getAspectFlags(), ImageLayout::TransferDst, commandBuffer); ASSERT(dstImage->getCurrentLayout() == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageCopy region = {}; VkImageCopy region = {};
region.srcSubresource.aspectMask = aspectMask; region.srcSubresource = srcSubresource;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffset.x = srcOffset.x; region.srcOffset.x = srcOffset.x;
region.srcOffset.y = srcOffset.y; region.srcOffset.y = srcOffset.y;
region.srcOffset.z = srcOffset.z; region.srcOffset.z = srcOffset.z;
region.dstSubresource.aspectMask = aspectMask; region.dstSubresource = dstSubresource;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffset.x = dstOffset.x; region.dstOffset.x = dstOffset.x;
region.dstOffset.y = dstOffset.y; region.dstOffset.y = dstOffset.y;
region.dstOffset.z = dstOffset.z; region.dstOffset.z = dstOffset.z;
......
...@@ -614,7 +614,8 @@ class ImageHelper final : public CommandGraphResource ...@@ -614,7 +614,8 @@ class ImageHelper final : public CommandGraphResource
const gl::Offset &srcOffset, const gl::Offset &srcOffset,
const gl::Offset &dstOffset, const gl::Offset &dstOffset,
const gl::Extents &copySize, const gl::Extents &copySize,
VkImageAspectFlags aspectMask, const VkImageSubresourceLayers &srcSubresources,
const VkImageSubresourceLayers &dstSubresources,
CommandBuffer *commandBuffer); CommandBuffer *commandBuffer);
angle::Result generateMipmapsWithBlit(ContextVk *contextVk, GLuint maxLevel); 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