More precise memory tracking for vkCmdCopyImageToBuffer

This change tracks the exact memory regions of the buffer accessed in
`vkCmdCopyBufferToImage` and `vkCmdCopyImageToBuffer`. In the case of
`vkCmdCopyImageToBuffer`, using the exact memory regions allows the buffer write
to be treated as a `CompleteWrite` (instead of `PartialWrite`).
This commit is contained in:
Benson Joeris
2019-02-15 11:34:50 -05:00
committed by Baldur Karlsson
parent d6b706b132
commit a7b21ae2d2
3 changed files with 162 additions and 68 deletions
+151 -44
View File
@@ -1702,32 +1702,42 @@ uint32_t GetPlaneByteSize(uint32_t Width, uint32_t Height, uint32_t Depth, VkFor
return GetByteSize(Width, Height, Depth, Format, mip);
}
struct TexelShape
// The shape of blocks in (a plane of) an image format.
// Non-block formats are considered to have 1x1 blocks.
// For some planar formats, the block shape depends on the plane--
// e.g. VK_FORMAT_G8_B8R8_2PLANE_422_UNORM has 8 bits per 1x1 block in plane 0, but 16 bits per 1x1
// block in plane 1.
struct BlockShape
{
// the width of a block, in texels (or 1 for non-block formats)
uint32_t width;
// the height of a block, in texels (or 1 for non-block formats)
uint32_t height;
// the number of bytes used to encode the block
uint32_t bytes;
};
TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
BlockShape GetBlockShape(VkFormat Format, uint32_t plane)
{
switch(Format)
{
case VK_FORMAT_R64G64B64A64_UINT:
case VK_FORMAT_R64G64B64A64_SINT:
case VK_FORMAT_R64G64B64A64_SFLOAT: return TexelShape{1, 1, 32};
case VK_FORMAT_R64G64B64A64_SFLOAT: return {1, 1, 32};
case VK_FORMAT_R64G64B64_UINT:
case VK_FORMAT_R64G64B64_SINT:
case VK_FORMAT_R64G64B64_SFLOAT: return TexelShape{1, 1, 24};
case VK_FORMAT_R64G64B64_SFLOAT: return {1, 1, 24};
case VK_FORMAT_R32G32B32A32_UINT:
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
case VK_FORMAT_R64G64_UINT:
case VK_FORMAT_R64G64_SINT:
case VK_FORMAT_R64G64_SFLOAT: return TexelShape{1, 1, 16};
case VK_FORMAT_R64G64_SFLOAT: return {1, 1, 16};
case VK_FORMAT_R32G32B32_UINT:
case VK_FORMAT_R32G32B32_SINT:
case VK_FORMAT_R32G32B32_SFLOAT: return TexelShape{1, 1, 12};
case VK_FORMAT_R32G32B32_SFLOAT: return {1, 1, 12};
case VK_FORMAT_R16G16B16A16_UNORM:
case VK_FORMAT_R16G16B16A16_SNORM:
case VK_FORMAT_R16G16B16A16_USCALED:
@@ -1740,15 +1750,15 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_R32G32_SFLOAT:
case VK_FORMAT_R64_UINT:
case VK_FORMAT_R64_SINT:
case VK_FORMAT_R64_SFLOAT: return TexelShape{1, 1, 8};
case VK_FORMAT_R64_SFLOAT: return {1, 1, 8};
case VK_FORMAT_R16G16B16_UNORM:
case VK_FORMAT_R16G16B16_SNORM:
case VK_FORMAT_R16G16B16_USCALED:
case VK_FORMAT_R16G16B16_SSCALED:
case VK_FORMAT_R16G16B16_UINT:
case VK_FORMAT_R16G16B16_SINT:
case VK_FORMAT_R16G16B16_SFLOAT: return TexelShape{1, 1, 6};
case VK_FORMAT_D32_SFLOAT_S8_UINT: return TexelShape{1, 1, 8};
case VK_FORMAT_R16G16B16_SFLOAT: return {1, 1, 6};
case VK_FORMAT_D32_SFLOAT_S8_UINT: return {1, 1, 8};
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8B8_SNORM:
case VK_FORMAT_R8G8B8_USCALED:
@@ -1762,7 +1772,7 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_B8G8R8_SSCALED:
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
case VK_FORMAT_B8G8R8_SRGB: return TexelShape{1, 1, 3};
case VK_FORMAT_B8G8R8_SRGB: return {1, 1, 3};
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
@@ -1810,8 +1820,8 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_X8_D24_UNORM_PACK32:
case VK_FORMAT_D24_UNORM_S8_UINT:
case VK_FORMAT_D32_SFLOAT:
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return TexelShape{1, 1, 4};
case VK_FORMAT_D16_UNORM_S8_UINT: return TexelShape{1, 1, 4};
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: return {1, 1, 4};
case VK_FORMAT_D16_UNORM_S8_UINT: return {1, 1, 4};
case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_R8G8_SNORM:
case VK_FORMAT_R8G8_USCALED:
@@ -1833,7 +1843,7 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
case VK_FORMAT_B5G6R5_UNORM_PACK16:
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return TexelShape{1, 1, 2};
case VK_FORMAT_B4G4R4A4_UNORM_PACK16: return {1, 1, 2};
case VK_FORMAT_R4G4_UNORM_PACK8:
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
@@ -1842,7 +1852,7 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
case VK_FORMAT_R8_SRGB:
case VK_FORMAT_S8_UINT: return TexelShape{1, 1, 1};
case VK_FORMAT_S8_UINT: return {1, 1, 1};
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
@@ -1856,7 +1866,7 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
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: return TexelShape{4, 4, 8};
case VK_FORMAT_EAC_R11_SNORM_BLOCK: return {4, 4, 8};
case VK_FORMAT_BC2_UNORM_BLOCK:
case VK_FORMAT_BC2_SRGB_BLOCK:
case VK_FORMAT_BC3_UNORM_BLOCK:
@@ -1868,44 +1878,44 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_BC7_UNORM_BLOCK:
case VK_FORMAT_BC7_SRGB_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return TexelShape{4, 4, 16};
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return {4, 4, 16};
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return TexelShape{4, 4, 16};
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return {4, 4, 16};
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return TexelShape{5, 4, 16};
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return {5, 4, 16};
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return TexelShape{5, 5, 16};
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return {5, 5, 16};
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return TexelShape{6, 5, 16};
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return {6, 5, 16};
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return TexelShape{6, 6, 16};
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return {6, 6, 16};
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return TexelShape{8, 5, 16};
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return {8, 5, 16};
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return TexelShape{8, 6, 16};
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return {8, 6, 16};
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return TexelShape{8, 8, 16};
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return {8, 8, 16};
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return TexelShape{10, 5, 16};
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return {10, 5, 16};
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return TexelShape{10, 6, 16};
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return {10, 6, 16};
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return TexelShape{10, 8, 16};
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return {10, 8, 16};
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return TexelShape{10, 10, 16};
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return {10, 10, 16};
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return TexelShape{12, 10, 16};
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return {12, 10, 16};
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return TexelShape{12, 12, 16};
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return {12, 12, 16};
case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return TexelShape{8, 4, 8};
case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return {8, 4, 8};
case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
return TexelShape{4, 4, 8};
return {4, 4, 8};
/*
* YUV planar/packed subsampled textures.
@@ -1987,33 +1997,33 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_B8G8R8G8_422_UNORM:
// 4:2:2 packed 8-bit, so 1 byte per pixel for luma and 1 byte per pixel for chroma (2 chroma
// samples, with 50% subsampling = 1 byte per pixel)
return TexelShape{2, 1, 4};
return {2, 1, 4};
case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM:
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: return TexelShape{1, 1, 1};
case VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM: return {1, 1, 1};
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
case VK_FORMAT_G8_B8R8_2PLANE_422_UNORM:
if(plane == 0)
return TexelShape{1, 1, 1};
return {1, 1, 1};
else if(plane == 1)
return TexelShape{1, 1, 2};
return {1, 1, 2};
else
RDCERR("Invalid plane %d in 2-plane format", plane);
case VK_FORMAT_R10X6_UNORM_PACK16:
case VK_FORMAT_R12X4_UNORM_PACK16:
// basically just 16-bit format with only top 10-bits used
// 10-bit and 12-bit formats are stored identically to 16-bit formats
return TexelShape{1, 1, 2};
return {1, 1, 2};
case VK_FORMAT_R10X6G10X6_UNORM_2PACK16:
case VK_FORMAT_R12X4G12X4_UNORM_2PACK16:
// just a 16-bit format with only top N-bits used
// 10-bit and 12-bit formats are stored identically to 16-bit formats
return TexelShape{1, 1, 4};
return {1, 1, 4};
case VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16:
case VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16:
// just a 16-bit format with only top N-bits used
// 10-bit and 12-bit formats are stored identically to 16-bit formats
return TexelShape{1, 1, 8};
return {1, 1, 8};
case VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16:
case VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16:
case VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16:
@@ -2022,7 +2032,7 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_B16G16R16G16_422_UNORM:
// 10-bit and 12-bit formats are stored identically to 16-bit formats
// 4:2:2 packed 16-bit
return TexelShape{2, 1, 8};
return {2, 1, 8};
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM:
@@ -2031,7 +2041,7 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM:
case VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16:
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return TexelShape{1, 1, 2};
case VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM: return {1, 1, 2};
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16:
case VK_FORMAT_G16_B16R16_2PLANE_420_UNORM:
@@ -2039,14 +2049,17 @@ TexelShape GetTexelShape(VkFormat Format, uint32_t plane)
case VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16:
case VK_FORMAT_G16_B16R16_2PLANE_422_UNORM:
if(plane == 0)
return TexelShape{1, 1, 2};
return {1, 1, 2};
else if(plane == 1)
return TexelShape{1, 1, 4};
return {1, 1, 4};
else
RDCERR("Invalid plane %d in 2-plane format", plane);
default: RDCERR("Unrecognised Vulkan Format: %d", Format); return TexelShape{};
default: RDCERR("Unrecognised Vulkan Format: %d", Format);
}
return BlockShape();
}
ResourceFormat MakeResourceFormat(VkFormat fmt)
{
ResourceFormat ret;
@@ -3401,6 +3414,100 @@ void VkResourceRecord::MarkBufferFrameReferenced(VkResourceRecord *buf, VkDevice
MarkMemoryFrameReferenced(buf->baseResource, buf->memOffset + offset, size, refType);
}
void VkResourceRecord::MarkBufferImageCopyFrameReferenced(
VkResourceRecord *buf, VkResourceRecord *img, const ImageLayouts &layout, uint32_t regionCount,
const VkBufferImageCopy *regions, FrameRefType bufRefType, FrameRefType imgRefType)
{
MarkResourceFrameReferenced(img->GetResourceID(), imgRefType);
MarkResourceFrameReferenced(img->baseResource, imgRefType);
// mark buffer just as read
MarkResourceFrameReferenced(buf->GetResourceID(), eFrameRef_Read);
for(uint32_t ri = 0; ri < regionCount; ri++)
{
const VkBufferImageCopy &region = regions[ri];
VkFormat regionFormat = layout.format;
uint32_t plane = 0;
switch(region.imageSubresource.aspectMask)
{
case VK_IMAGE_ASPECT_STENCIL_BIT: regionFormat = VK_FORMAT_S8_UINT; break;
case VK_IMAGE_ASPECT_DEPTH_BIT: regionFormat = GetDepthOnlyFormat(layout.format); break;
case VK_IMAGE_ASPECT_PLANE_1_BIT: plane = 1; break;
case VK_IMAGE_ASPECT_PLANE_2_BIT: plane = 2; break;
default: break;
}
// The shape of the texel blocks;
// non-block formats are treated as having 1x1 blocks
BlockShape blockShape = GetBlockShape(regionFormat, plane);
// width of copied region, in blocks
uint32_t widthInBlocks = (region.imageExtent.width + blockShape.width - 1) / blockShape.width;
// width of copied region, in bytes (in the buffer);
uint32_t widthInBytes = blockShape.bytes * widthInBlocks;
// height of copied region, in blocks
uint32_t heightInBlocks = (region.imageExtent.height + blockShape.height - 1) / blockShape.height;
// total number of depth slices to be copied.
uint32_t sliceCount = region.imageExtent.depth * region.imageSubresource.layerCount;
// stride_y: number of bytes in the buffer between the start of one row of
// blocks and the next. The buffer may have space for more blocks per row than
// are actually being copied (specified by bufferRowLength).
uint32_t stride_y;
if(region.bufferRowLength == 0)
stride_y = widthInBytes;
else
stride_y = blockShape.bytes * region.bufferRowLength;
// stride_z: number of bytes in the buffer between the start of one depth
// slice and the next. The buffer may have space for more rows per slice
// than are actually being copied (specified by bufferImageHeight).
uint32_t stride_z;
if(region.bufferImageHeight == 0)
stride_z = stride_y * heightInBlocks;
else
stride_z = stride_y * region.bufferImageHeight;
// memory offset of the first byte to be copied to/from the buffer
VkDeviceSize startRegion = buf->memOffset + region.bufferOffset;
if(stride_z == widthInBytes * heightInBlocks)
{
// no gaps between slices nor between rows; single copy for entire region
MarkMemoryFrameReferenced(buf->baseResource, startRegion,
widthInBytes * heightInBlocks * sliceCount, bufRefType);
}
else if(stride_y == widthInBytes)
{
// gaps between slices, but no gaps between rows; separate copies per slice
for(uint32_t z = 0; z < sliceCount; z++)
{
VkDeviceSize startSlice = startRegion + z * stride_z;
MarkMemoryFrameReferenced(buf->baseResource, startSlice, widthInBytes * heightInBlocks,
bufRefType);
}
}
else
{
// gaps between rows; separate copies for each row in each slice
for(uint32_t z = 0; z < sliceCount; z++)
{
VkDeviceSize startSlice = startRegion + z * stride_z;
for(uint32_t y = 0; y < heightInBlocks; y++)
{
VkDeviceSize startRow = startSlice + y * stride_y;
MarkMemoryFrameReferenced(buf->baseResource, startRow, widthInBytes, bufRefType);
}
}
}
}
}
void VkResourceRecord::MarkBufferViewFrameReferenced(VkResourceRecord *bufView, FrameRefType refType)
{
// mark the VkBufferView and VkBuffer as read
+5
View File
@@ -1075,6 +1075,7 @@ inline FrameRefType MarkMemoryReferenced(std::map<ResourceId, MemRefs> &memRefs,
}
struct DescUpdateTemplate;
struct ImageLayouts;
struct VkResourceRecord : public ResourceRecord
{
@@ -1206,6 +1207,10 @@ public:
FrameRefType refType);
void MarkBufferFrameReferenced(VkResourceRecord *buf, VkDeviceSize offset, VkDeviceSize size,
FrameRefType refType);
void MarkBufferImageCopyFrameReferenced(VkResourceRecord *buf, VkResourceRecord *img,
const ImageLayouts &layout, uint32_t regionCount,
const VkBufferImageCopy *regions, FrameRefType bufRefType,
FrameRefType imgRefType);
void MarkBufferViewFrameReferenced(VkResourceRecord *buf, FrameRefType refType);
// these are all disjoint, so only a record of the right type will have each
// Note some of these need to be deleted in the constructor, so we check the
@@ -1769,16 +1769,9 @@ void WrappedVulkan::vkCmdCopyBufferToImage(VkCommandBuffer commandBuffer, VkBuff
regionCount, pRegions);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(srcBuffer), eFrameRef_Read);
record->MarkResourceFrameReferenced(GetRecord(srcBuffer)->baseResource, eFrameRef_Read);
record->MarkResourceFrameReferenced(GetResID(destImage), eFrameRef_PartialWrite);
record->MarkResourceFrameReferenced(GetRecord(destImage)->baseResource, eFrameRef_Read);
record->cmdInfo->dirtied.insert(GetResID(destImage));
if(GetRecord(srcBuffer)->resInfo)
record->cmdInfo->sparse.insert(GetRecord(srcBuffer)->resInfo);
if(GetRecord(destImage)->resInfo)
record->cmdInfo->sparse.insert(GetRecord(destImage)->resInfo);
record->MarkBufferImageCopyFrameReferenced(GetRecord(srcBuffer), GetRecord(destImage),
m_ImageLayouts[GetResID(destImage)], regionCount,
pRegions, eFrameRef_Read, eFrameRef_PartialWrite);
}
}
@@ -1884,20 +1877,9 @@ void WrappedVulkan::vkCmdCopyImageToBuffer(VkCommandBuffer commandBuffer, VkImag
regionCount, pRegions);
record->AddChunk(scope.Get());
record->MarkResourceFrameReferenced(GetResID(srcImage), eFrameRef_Read);
record->MarkResourceFrameReferenced(GetRecord(srcImage)->baseResource, eFrameRef_Read);
VkResourceRecord *buf = GetRecord(destBuffer);
// mark buffer just as read, and memory behind as write & dirtied
record->MarkResourceFrameReferenced(buf->GetResourceID(), eFrameRef_Read);
record->MarkResourceFrameReferenced(buf->baseResource, eFrameRef_PartialWrite);
if(buf->baseResource != ResourceId())
record->cmdInfo->dirtied.insert(buf->baseResource);
if(GetRecord(srcImage)->resInfo)
record->cmdInfo->sparse.insert(GetRecord(srcImage)->resInfo);
if(buf->resInfo)
record->cmdInfo->sparse.insert(buf->resInfo);
record->MarkBufferImageCopyFrameReferenced(GetRecord(destBuffer), GetRecord(srcImage),
m_ImageLayouts[GetResID(srcImage)], regionCount,
pRegions, eFrameRef_CompleteWrite, eFrameRef_Read);
}
}