Add support for compressed image copy
Copying compressed images is just a matter of taking
block size into account and using a block as a single
unit of measure, instead of using a texel.
Bug b/119620767
Tests: dEQP-VK.api.copy_and_blit.core.image_to_image.all_formats.color.*
Change-Id: Ie77defc197ac7abb09a8555b384093fd50be681b
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28048
Tested-by: Alexis Hétu <sugoi@google.com>
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Vulkan/VkFormat.cpp b/src/Vulkan/VkFormat.cpp
index 79e3d23..d7dedf5 100644
--- a/src/Vulkan/VkFormat.cpp
+++ b/src/Vulkan/VkFormat.cpp
@@ -318,6 +318,273 @@
return false;
}
+bool Format::isCompressed() const
+{
+ switch(format)
+ {
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+ case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+ case VK_FORMAT_BC7_UNORM_BLOCK:
+ case VK_FORMAT_BC7_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int Format::blockWidth() const
+{
+ switch(format)
+ {
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+ case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+ case VK_FORMAT_BC7_UNORM_BLOCK:
+ case VK_FORMAT_BC7_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+ return 4;
+ case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+ return 5;
+ case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+ return 6;
+ case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+ return 8;
+ case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+ return 10;
+ case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ return 12;
+ default:
+ return 1;
+ }
+}
+
+int Format::blockHeight() const
+{
+ switch(format)
+ {
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+ case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+ case VK_FORMAT_BC7_UNORM_BLOCK:
+ case VK_FORMAT_BC7_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+ return 4;
+ case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+ return 5;
+ case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+ return 6;
+ case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+ return 8;
+ case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+ return 10;
+ case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ return 12;
+ default:
+ return 1;
+ }
+}
+
+int Format::bytesPerBlock() const
+{
+ switch(format)
+ {
+ case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+ case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+ case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+ case VK_FORMAT_BC4_UNORM_BLOCK:
+ case VK_FORMAT_BC4_SNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+ case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+ return 8;
+ case VK_FORMAT_BC2_UNORM_BLOCK:
+ case VK_FORMAT_BC2_SRGB_BLOCK:
+ case VK_FORMAT_BC3_UNORM_BLOCK:
+ case VK_FORMAT_BC3_SRGB_BLOCK:
+ case VK_FORMAT_BC5_UNORM_BLOCK:
+ case VK_FORMAT_BC5_SNORM_BLOCK:
+ case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+ case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+ case VK_FORMAT_BC7_UNORM_BLOCK:
+ case VK_FORMAT_BC7_SRGB_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+ case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+ case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+ case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+ case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+ case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+ return 16;
+ default:
+ return bytes();
+ }
+}
+
int Format::componentCount() const
{
switch(format)
diff --git a/src/Vulkan/VkFormat.h b/src/Vulkan/VkFormat.h
index 595b48c4..5721d06 100644
--- a/src/Vulkan/VkFormat.h
+++ b/src/Vulkan/VkFormat.h
@@ -45,6 +45,11 @@
bool isSRGBwritable() const;
bool isFloatFormat() const;
+ bool isCompressed() const;
+ int blockWidth() const;
+ int blockHeight() const;
+ int bytesPerBlock() const;
+
int componentCount() const;
bool isUnsignedComponent(int component) const;
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 0abc1c5..4b559cf 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -141,8 +141,10 @@
VkImageAspectFlagBits srcAspect = static_cast<VkImageAspectFlagBits>(pRegion.srcSubresource.aspectMask);
VkImageAspectFlagBits dstAspect = static_cast<VkImageAspectFlagBits>(pRegion.dstSubresource.aspectMask);
- int srcBytesPerTexel = bytesPerTexel(srcAspect);
- ASSERT(srcBytesPerTexel == dst->bytesPerTexel(dstAspect));
+ Format srcFormat = getFormat(srcAspect);
+ Format dstFormat = dst->getFormat(dstAspect);
+ int srcBytesPerBlock = srcFormat.bytesPerBlock();
+ ASSERT(srcBytesPerBlock == dstFormat.bytesPerBlock());
const uint8_t* srcMem = static_cast<const uint8_t*>(getTexelPointer(pRegion.srcOffset, pRegion.srcSubresource));
uint8_t* dstMem = static_cast<uint8_t*>(dst->getTexelPointer(pRegion.dstOffset, pRegion.dstSubresource));
@@ -154,49 +156,58 @@
VkExtent3D srcExtent = getMipLevelExtent(pRegion.srcSubresource.mipLevel);
VkExtent3D dstExtent = dst->getMipLevelExtent(pRegion.dstSubresource.mipLevel);
+ VkExtent3D copyExtent = imageExtentInBlocks(pRegion.extent, srcAspect);
- bool isSinglePlane = (pRegion.extent.depth == 1);
- bool isSingleLine = (pRegion.extent.height == 1) && isSinglePlane;
+ bool isSinglePlane = (copyExtent.depth == 1);
+ bool isSingleLine = (copyExtent.height == 1) && isSinglePlane;
// In order to copy multiple lines using a single memcpy call, we
// have to make sure that we need to copy the entire line and that
// both source and destination lines have the same length in bytes
bool isEntireLine = (pRegion.extent.width == srcExtent.width) &&
(pRegion.extent.width == dstExtent.width) &&
- (srcRowPitchBytes == dstRowPitchBytes);
+ // For non compressed formats, blockWidth is 1. For compressed
+ // formats, rowPitchBytes returns the number of bytes for a row of
+ // blocks, so we have to divide by the block height, which means:
+ // srcRowPitchBytes / srcBlockWidth == dstRowPitchBytes / dstBlockWidth
+ // And, to avoid potential non exact integer division, for example if a
+ // block has 16 bytes and represents 5 lines, we change the equation to:
+ // srcRowPitchBytes * dstBlockWidth == dstRowPitchBytes * srcBlockWidth
+ ((srcRowPitchBytes * dstFormat.blockWidth()) ==
+ (dstRowPitchBytes * srcFormat.blockWidth()));
// In order to copy multiple planes using a single memcpy call, we
// have to make sure that we need to copy the entire plane and that
// both source and destination planes have the same length in bytes
bool isEntirePlane = isEntireLine &&
- (pRegion.extent.height == srcExtent.height) &&
- (pRegion.extent.height == dstExtent.height) &&
+ (copyExtent.height == srcExtent.height) &&
+ (copyExtent.height == dstExtent.height) &&
(srcSlicePitchBytes == dstSlicePitchBytes);
if(isSingleLine) // Copy one line
{
- size_t copySize = pRegion.extent.width * srcBytesPerTexel;
+ size_t copySize = copyExtent.width * srcBytesPerBlock;
ASSERT((srcMem + copySize) < end());
ASSERT((dstMem + copySize) < dst->end());
memcpy(dstMem, srcMem, copySize);
}
else if(isEntireLine && isSinglePlane) // Copy one plane
{
- size_t copySize = pRegion.extent.height * srcRowPitchBytes;
+ size_t copySize = copyExtent.height * srcRowPitchBytes;
ASSERT((srcMem + copySize) < end());
ASSERT((dstMem + copySize) < dst->end());
memcpy(dstMem, srcMem, copySize);
}
else if(isEntirePlane) // Copy multiple planes
{
- size_t copySize = pRegion.extent.depth * srcSlicePitchBytes;
+ size_t copySize = copyExtent.depth * srcSlicePitchBytes;
ASSERT((srcMem + copySize) < end());
ASSERT((dstMem + copySize) < dst->end());
memcpy(dstMem, srcMem, copySize);
}
else if(isEntireLine) // Copy plane by plane
{
- size_t copySize = pRegion.extent.height * srcRowPitchBytes;
+ size_t copySize = copyExtent.height * srcRowPitchBytes;
- for(uint32_t z = 0; z < pRegion.extent.depth; z++, dstMem += dstSlicePitchBytes, srcMem += srcSlicePitchBytes)
+ for(uint32_t z = 0; z < copyExtent.depth; z++, dstMem += dstSlicePitchBytes, srcMem += srcSlicePitchBytes)
{
ASSERT((srcMem + copySize) < end());
ASSERT((dstMem + copySize) < dst->end());
@@ -205,11 +216,11 @@
}
else // Copy line by line
{
- size_t copySize = pRegion.extent.width * srcBytesPerTexel;
+ size_t copySize = copyExtent.width * srcBytesPerBlock;
- for(uint32_t z = 0; z < pRegion.extent.depth; z++)
+ for(uint32_t z = 0; z < copyExtent.depth; z++)
{
- for(uint32_t y = 0; y < pRegion.extent.height; y++, dstMem += dstRowPitchBytes, srcMem += srcRowPitchBytes)
+ for(uint32_t y = 0; y < copyExtent.height; y++, dstMem += dstRowPitchBytes, srcMem += srcRowPitchBytes)
{
ASSERT((srcMem + copySize) < end());
ASSERT((dstMem + copySize) < dst->end());
@@ -230,26 +241,26 @@
VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(region.imageSubresource.aspectMask);
+ Format copyFormat = getFormat(aspect);
VkExtent3D mipLevelExtent = getMipLevelExtent(region.imageSubresource.mipLevel);
- int imageBytesPerTexel = bytesPerTexel(aspect);
+ VkExtent3D imageExtent = imageExtentInBlocks(region.imageExtent, aspect);
+ VkExtent2D bufferExtent = bufferExtentInBlocks({ imageExtent.width, imageExtent.height }, region);
+ int imageBytesPerBlock = copyFormat.bytesPerBlock();
int imageRowPitchBytes = rowPitchBytes(aspect, region.imageSubresource.mipLevel);
int imageSlicePitchBytes = slicePitchBytes(aspect, region.imageSubresource.mipLevel);
- int bufferRowPitchBytes = ((region.bufferRowLength == 0) ? region.imageExtent.width : region.bufferRowLength) *
- imageBytesPerTexel;
- int bufferSlicePitchBytes = (((region.bufferImageHeight == 0) || (region.bufferRowLength == 0))) ?
- region.imageExtent.height * bufferRowPitchBytes :
- (region.bufferImageHeight * region.bufferRowLength) * imageBytesPerTexel;
+ int bufferRowPitchBytes = bufferExtent.width * imageBytesPerBlock;
+ int bufferSlicePitchBytes = bufferExtent.height * bufferRowPitchBytes;
int srcSlicePitchBytes = bufferIsSource ? bufferSlicePitchBytes : imageSlicePitchBytes;
int dstSlicePitchBytes = bufferIsSource ? imageSlicePitchBytes : bufferSlicePitchBytes;
int srcRowPitchBytes = bufferIsSource ? bufferRowPitchBytes : imageRowPitchBytes;
int dstRowPitchBytes = bufferIsSource ? imageRowPitchBytes : bufferRowPitchBytes;
- bool isSinglePlane = (region.imageExtent.depth == 1);
- bool isSingleLine = (region.imageExtent.height == 1) && isSinglePlane;
- bool isEntireLine = (region.imageExtent.width == mipLevelExtent.width) &&
+ bool isSinglePlane = (imageExtent.depth == 1);
+ bool isSingleLine = (imageExtent.height == 1) && isSinglePlane;
+ bool isEntireLine = (imageExtent.width == mipLevelExtent.width) &&
(imageRowPitchBytes == bufferRowPitchBytes);
- bool isEntirePlane = isEntireLine && (region.imageExtent.height == mipLevelExtent.height) &&
+ bool isEntirePlane = isEntireLine && (imageExtent.height == mipLevelExtent.height) &&
(imageSlicePitchBytes == bufferSlicePitchBytes);
Buffer* buffer = Cast(buf);
@@ -262,28 +273,28 @@
VkDeviceSize bufferLayerSize = 0;
if(isSingleLine)
{
- copySize = region.imageExtent.width * imageBytesPerTexel;
+ copySize = imageExtent.width * imageBytesPerBlock;
bufferLayerSize = copySize;
}
else if(isEntireLine && isSinglePlane)
{
- copySize = region.imageExtent.height * imageRowPitchBytes;
+ copySize = imageExtent.height * imageRowPitchBytes;
bufferLayerSize = copySize;
}
else if(isEntirePlane)
{
- copySize = region.imageExtent.depth * imageSlicePitchBytes; // Copy multiple planes
+ copySize = imageExtent.depth * imageSlicePitchBytes; // Copy multiple planes
bufferLayerSize = copySize;
}
else if(isEntireLine) // Copy plane by plane
{
- copySize = region.imageExtent.height * imageRowPitchBytes;
- bufferLayerSize = copySize * region.imageExtent.depth;
+ copySize = imageExtent.height * imageRowPitchBytes;
+ bufferLayerSize = copySize * imageExtent.depth;
}
else // Copy line by line
{
- copySize = region.imageExtent.width * imageBytesPerTexel;
- bufferLayerSize = copySize * region.imageExtent.depth * region.imageExtent.height;
+ copySize = imageExtent.width * imageBytesPerBlock;
+ bufferLayerSize = copySize * imageExtent.depth * imageExtent.height;
}
VkDeviceSize imageLayerSize = getLayerSize(aspect);
@@ -302,7 +313,7 @@
{
uint8_t* srcPlaneMemory = srcMemory;
uint8_t* dstPlaneMemory = dstMemory;
- for(uint32_t z = 0; z < region.imageExtent.depth; z++)
+ for(uint32_t z = 0; z < imageExtent.depth; z++)
{
ASSERT(((bufferIsSource ? dstPlaneMemory : srcPlaneMemory) + copySize) < end());
ASSERT(((bufferIsSource ? srcPlaneMemory : dstPlaneMemory) + copySize) < buffer->end());
@@ -315,11 +326,11 @@
{
uint8_t* srcLayerMemory = srcMemory;
uint8_t* dstLayerMemory = dstMemory;
- for(uint32_t z = 0; z < region.imageExtent.depth; z++)
+ for(uint32_t z = 0; z < imageExtent.depth; z++)
{
uint8_t* srcPlaneMemory = srcLayerMemory;
uint8_t* dstPlaneMemory = dstLayerMemory;
- for(uint32_t y = 0; y < region.imageExtent.height; y++)
+ for(uint32_t y = 0; y < imageExtent.height; y++)
{
ASSERT(((bufferIsSource ? dstPlaneMemory : srcPlaneMemory) + copySize) < end());
ASSERT(((bufferIsSource ? srcPlaneMemory : dstPlaneMemory) + copySize) < buffer->end());
@@ -354,12 +365,79 @@
getMemoryOffset(aspect, subresource.mipLevel, subresource.baseArrayLayer));
}
+VkExtent3D Image::imageExtentInBlocks(const VkExtent3D& extent, VkImageAspectFlagBits aspect) const
+{
+ VkExtent3D adjustedExtent = extent;
+ Format usedFormat = getFormat(aspect);
+ if(usedFormat.isCompressed())
+ {
+ // When using a compressed format, we use the block as the base unit, instead of the texel
+ int blockWidth = usedFormat.blockWidth();
+ int blockHeight = usedFormat.blockHeight();
+
+ ASSERT(((extent.width % blockWidth) == 0) && ((extent.height % blockHeight) == 0)); // We can't offset within a block
+
+ adjustedExtent.width /= blockWidth;
+ adjustedExtent.height /= blockHeight;
+ }
+ return adjustedExtent;
+}
+
+VkOffset3D Image::imageOffsetInBlocks(const VkOffset3D& offset, VkImageAspectFlagBits aspect) const
+{
+ VkOffset3D adjustedOffset = offset;
+ Format usedFormat = getFormat(aspect);
+ if(usedFormat.isCompressed())
+ {
+ // When using a compressed format, we use the block as the base unit, instead of the texel
+ int blockWidth = usedFormat.blockWidth();
+ int blockHeight = usedFormat.blockHeight();
+
+ ASSERT(((offset.x % blockWidth) == 0) && ((offset.y % blockHeight) == 0)); // We can't offset within a block
+
+ adjustedOffset.x /= blockWidth;
+ adjustedOffset.y /= blockHeight;
+ }
+ return adjustedOffset;
+}
+
+VkExtent2D Image::bufferExtentInBlocks(const VkExtent2D& extent, const VkBufferImageCopy& region) const
+{
+ VkExtent2D adjustedExtent = extent;
+ VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(region.imageSubresource.aspectMask);
+ Format usedFormat = getFormat(aspect);
+ if(region.bufferRowLength != 0)
+ {
+ adjustedExtent.width = region.bufferRowLength;
+
+ if(usedFormat.isCompressed())
+ {
+ int blockWidth = usedFormat.blockWidth();
+ ASSERT((adjustedExtent.width % blockWidth) == 0);
+ adjustedExtent.width /= blockWidth;
+ }
+ }
+ if(region.bufferImageHeight != 0)
+ {
+ adjustedExtent.height = region.bufferImageHeight;
+
+ if(usedFormat.isCompressed())
+ {
+ int blockHeight = usedFormat.blockHeight();
+ ASSERT((adjustedExtent.height % blockHeight) == 0);
+ adjustedExtent.height /= blockHeight;
+ }
+ }
+ return adjustedExtent;
+}
+
VkDeviceSize Image::texelOffsetBytesInStorage(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const
{
VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(subresource.aspectMask);
- return offset.z * slicePitchBytes(aspect, subresource.mipLevel) +
- (offset.y + (isCube() ? 1 : 0)) * rowPitchBytes(aspect, subresource.mipLevel) +
- (offset.x + (isCube() ? 1 : 0)) * bytesPerTexel(aspect);
+ VkOffset3D adjustedOffset = imageOffsetInBlocks(offset, aspect);
+ return adjustedOffset.z * slicePitchBytes(aspect, subresource.mipLevel) +
+ (adjustedOffset.y + (isCube() ? 1 : 0)) * rowPitchBytes(aspect, subresource.mipLevel) +
+ (adjustedOffset.x + (isCube() ? 1 : 0)) * getFormat(aspect).bytesPerBlock();
}
VkExtent3D Image::getMipLevelExtent(uint32_t mipLevel) const
@@ -388,7 +466,7 @@
{
// Depth and Stencil pitch should be computed separately
ASSERT((aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) !=
- (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
+ (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
return getFormat(aspect).pitchB(getMipLevelExtent(mipLevel).width, isCube() ? 1 : 0, true);
}
@@ -396,7 +474,7 @@
{
// Depth and Stencil slice should be computed separately
ASSERT((aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) !=
- (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
+ (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
VkExtent3D mipLevelExtent = getMipLevelExtent(mipLevel);
return getFormat(aspect).sliceB(mipLevelExtent.width, mipLevelExtent.height, isCube() ? 1 : 0, true);
}
@@ -405,7 +483,7 @@
{
// Depth and Stencil bytes should be computed separately
ASSERT((aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) !=
- (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
+ (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
return getFormat(aspect).bytes();
}
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index cee6b8e..71e061f 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -76,6 +76,9 @@
VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;
VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect) const;
+ VkExtent3D imageExtentInBlocks(const VkExtent3D& extent, VkImageAspectFlagBits aspect) const;
+ VkOffset3D imageOffsetInBlocks(const VkOffset3D& offset, VkImageAspectFlagBits aspect) const;
+ VkExtent2D bufferExtentInBlocks(const VkExtent2D& extent, const VkBufferImageCopy& region) const;
int bytesPerTexel(VkImageAspectFlagBits flags) const;
VkFormat getClearFormat() const;
void clear(void* pixelData, VkFormat format, const VkImageSubresourceRange& subresourceRange, const VkRect2D& renderArea);