summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineVulkan
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2019-04-05 15:00:59 +0000
committerEgor Yusov <egor.yusov@gmail.com>2019-04-05 15:00:59 +0000
commitceb1bc219e2eb5a4151130c2b5fb2833854a8b45 (patch)
tree0cc40adb02d6018d04e0844e8fd291b99ff60c8e /Graphics/GraphicsEngineVulkan
parentFixed rotation matrices (diff)
downloadDiligentCore-ceb1bc219e2eb5a4151130c2b5fb2833854a8b45.tar.gz
DiligentCore-ceb1bc219e2eb5a4151130c2b5fb2833854a8b45.zip
Implemented staging textures in Vk backend
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
-rw-r--r--Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h25
-rw-r--r--Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h31
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h16
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.h3
-rw-r--r--Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp2
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp366
-rw-r--r--Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp775
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp10
8 files changed, 787 insertions, 441 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
index 09eaa1a5..52b6a7aa 100644
--- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
@@ -296,14 +296,23 @@ private:
Uint32 MipLevel,
const Box& Region)const;
- void CopyBufferToTexture(VkBuffer vkBuffer,
- Uint32 BufferOffset,
- Uint32 BufferRowStrideInTexels,
- const Box& Region,
- TextureVkImpl& TextureVk,
- Uint32 MipLevel,
- Uint32 ArraySlice,
- RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode);
+ void CopyBufferToTexture(VkBuffer vkSrcBuffer,
+ Uint32 SrcBufferOffset,
+ Uint32 SrcBufferRowStrideInTexels,
+ TextureVkImpl& DstTextureVk,
+ const Box& DstRegion,
+ Uint32 DstMipLevel,
+ Uint32 DstArraySlice,
+ RESOURCE_STATE_TRANSITION_MODE DstTextureTransitionMode);
+
+ void CopyTextureToBuffer(TextureVkImpl& SrcTextureVk,
+ const Box& SrcRegion,
+ Uint32 SrcMipLevel,
+ Uint32 SrcArraySlice,
+ RESOURCE_STATE_TRANSITION_MODE SrcTextureTransitionMode,
+ VkBuffer vkDstBuffer,
+ Uint32 DstBufferOffset,
+ Uint32 DstBufferRowStrideInTexels);
void DvpLogRenderPass_PSOMismatch();
diff --git a/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h b/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h
index 4d19fe91..33f07b79 100644
--- a/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/TextureVkImpl.h
@@ -38,6 +38,18 @@ namespace Diligent
class FixedBlockMemoryAllocator;
+struct MipLevelProperties
+{
+ Uint32 Width = 0;
+ Uint32 Height = 0;
+ Uint32 Depth = 1;
+ Uint32 RowSize = 0;
+ Uint32 MipSize = 0;
+};
+
+MipLevelProperties GetMipLevelProperties(const TextureDesc& TexDesc, Uint32 MipLevel);
+Uint32 GetStagingDataOffset(const TextureDesc& TexDesc, Uint32 ArraySlice, Uint32 MipLevel);
+
/// Base implementation of the Diligent::ITextureVk interface
class TextureVkImpl final : public TextureBase<ITextureVk, RenderDeviceVkImpl, TextureViewVkImpl, FixedBlockMemoryAllocator>
{
@@ -85,14 +97,31 @@ public:
void SetLayout(VkImageLayout Layout)override final;
VkImageLayout GetLayout()const override final;
+ VkBuffer GetVkStagingBuffer()const
+ {
+ return m_StagingBuffer;
+ }
+
+ uint8_t* GetStagingDataCPUAddress()const
+ {
+ auto* StagingDataCPUAddress = reinterpret_cast<uint8_t*>(m_MemoryAllocation.Page->GetCPUMemory());
+ VERIFY_EXPR(StagingDataCPUAddress != nullptr);
+ StagingDataCPUAddress += m_StagingDataAlignedOffset;
+ return StagingDataCPUAddress;
+ }
+
+ void InvalidateStagingRange(VkDeviceSize Offset, VkDeviceSize Size);
+
protected:
void CreateViewInternal( const struct TextureViewDesc& ViewDesc, ITextureView** ppView, bool bIsDefaultView )override;
//void PrepareVkInitData(const TextureData &InitData, Uint32 NumSubresources, std::vector<Vk_SUBRESOURCE_DATA> &VkInitData);
VulkanUtilities::ImageViewWrapper CreateImageView(TextureViewDesc &ViewDesc);
- VulkanUtilities::ImageWrapper m_VulkanImage;
+ VulkanUtilities::ImageWrapper m_VulkanImage;
+ VulkanUtilities::BufferWrapper m_StagingBuffer;
VulkanUtilities::VulkanMemoryAllocation m_MemoryAllocation;
+ VkDeviceSize m_StagingDataAlignedOffset;
// Texture views needed for mipmap generation
std::vector<std::unique_ptr<TextureViewVkImpl, STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator> > > m_MipLevelSRV;
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h
index d27a5594..3d35c581 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h
@@ -378,6 +378,22 @@ namespace VulkanUtilities
vkCmdCopyBufferToImage(m_VkCmdBuffer, srcBuffer, dstImage, dstImageLayout, regionCount, pRegions);
}
+ void CopyImageToBuffer(VkImage srcImage,
+ VkImageLayout srcImageLayout,
+ VkBuffer dstBuffer,
+ uint32_t regionCount,
+ const VkBufferImageCopy* pRegions)
+ {
+ VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE);
+ if (m_State.RenderPass != VK_NULL_HANDLE)
+ {
+ // Copy operations must be performed outside of render pass.
+ EndRenderPass();
+ }
+
+ vkCmdCopyImageToBuffer(m_VkCmdBuffer, srcImage, srcImageLayout, dstBuffer, regionCount, pRegions);
+ }
+
void FlushBarriers();
void SetVkCmdBuffer(VkCommandBuffer VkCmdBuffer)
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.h
index 0cd3bed0..1e7f2daa 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanLogicalDevice.h
@@ -130,6 +130,9 @@ namespace VulkanUtilities
VkResult MapMemory(VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)const;
void UnmapMemory(VkDeviceMemory memory)const;
+ VkResult InvalidateMappedMemoryRanges(uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)const;
+ VkResult FlushMappedMemoryRanges(uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)const;
+
VkResult GetFenceStatus(VkFence fence)const;
VkResult ResetFence(VkFence fence)const;
VkResult WaitForFences(uint32_t fenceCount,
diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
index c7967e8d..87170a74 100644
--- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
@@ -192,7 +192,7 @@ BufferVkImpl :: BufferVkImpl(IReferenceCounters* pRefCounters,
VkBufferCreateInfo VkStaginBuffCI = VkBuffCI;
VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- std::string StagingBufferName = "Staging buffer for '";
+ std::string StagingBufferName = "Upload buffer for '";
StagingBufferName += m_Desc.Name;
StagingBufferName += '\'';
VulkanUtilities::BufferWrapper StagingBuffer = LogicalDevice.CreateBuffer(VkStaginBuffCI, StagingBufferName.c_str());
diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
index eeb30132..20522acb 100644
--- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
@@ -1322,54 +1322,119 @@ namespace Diligent
auto* pSrcTexVk = ValidatedCast<TextureVkImpl>( CopyAttribs.pSrcTexture );
auto* pDstTexVk = ValidatedCast<TextureVkImpl>( CopyAttribs.pDstTexture );
+ const auto& SrcTexDesc = pSrcTexVk->GetDesc();
const auto& DstTexDesc = pDstTexVk->GetDesc();
- VkImageCopy CopyRegion = {};
- if (auto* pSrcBox = CopyAttribs.pSrcBox)
+ auto* pSrcBox = CopyAttribs.pSrcBox;
+ Box FullMipBox;
+ if (pSrcBox == nullptr)
{
+ FullMipBox.MaxX = std::max(DstTexDesc.Width >> CopyAttribs.SrcMipLevel, 1u);
+ FullMipBox.MaxY = std::max(DstTexDesc.Height >> CopyAttribs.SrcMipLevel, 1u);
+ if(DstTexDesc.Type == RESOURCE_DIM_TEX_3D)
+ FullMipBox.MaxZ = std::max(DstTexDesc.Depth >> CopyAttribs.SrcMipLevel, 1u);
+ else
+ FullMipBox.MaxZ = 1;
+ pSrcBox = &FullMipBox;
+ }
+ const auto& FmtAttribs = GetDevice()->GetTextureFormatInfo(DstTexDesc.Format);
+ if (SrcTexDesc.Usage != USAGE_STAGING && DstTexDesc.Usage != USAGE_STAGING)
+ {
+ VkImageCopy CopyRegion = {};
CopyRegion.srcOffset.x = pSrcBox->MinX;
CopyRegion.srcOffset.y = pSrcBox->MinY;
CopyRegion.srcOffset.z = pSrcBox->MinZ;
CopyRegion.extent.width = pSrcBox->MaxX - pSrcBox->MinX;
CopyRegion.extent.height = std::max(pSrcBox->MaxY - pSrcBox->MinY, 1u);
CopyRegion.extent.depth = std::max(pSrcBox->MaxZ - pSrcBox->MinZ, 1u);
- }
- else
- {
- CopyRegion.srcOffset = VkOffset3D{0,0,0};
- CopyRegion.extent.width = std::max(DstTexDesc.Width >> CopyAttribs.SrcMipLevel, 1u);
- CopyRegion.extent.height = std::max(DstTexDesc.Height >> CopyAttribs.SrcMipLevel, 1u);
- if(DstTexDesc.Type == RESOURCE_DIM_TEX_3D)
- CopyRegion.extent.depth = std::max(DstTexDesc.Depth >> CopyAttribs.SrcMipLevel, 1u);
+
+ VkImageAspectFlags aspectMask = 0;
+ if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
+ aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
+ {
+ aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ }
else
- CopyRegion.extent.depth = 1;
- }
+ aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
- const auto& FmtAttribs = GetDevice()->GetTextureFormatInfo(DstTexDesc.Format);
- VkImageAspectFlags aspectMask = 0;
- if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
- aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
- else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
- {
- aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ CopyRegion.srcSubresource.baseArrayLayer = CopyAttribs.SrcSlice;
+ CopyRegion.srcSubresource.layerCount = 1;
+ CopyRegion.srcSubresource.mipLevel = CopyAttribs.SrcMipLevel;
+ CopyRegion.srcSubresource.aspectMask = aspectMask;
+
+ CopyRegion.dstSubresource.baseArrayLayer = CopyAttribs.DstSlice;
+ CopyRegion.dstSubresource.layerCount = 1;
+ CopyRegion.dstSubresource.mipLevel = CopyAttribs.DstMipLevel;
+ CopyRegion.dstSubresource.aspectMask = aspectMask;
+
+ CopyRegion.dstOffset.x = CopyAttribs.DstX;
+ CopyRegion.dstOffset.y = CopyAttribs.DstY;
+ CopyRegion.dstOffset.z = CopyAttribs.DstZ;
+
+ CopyTextureRegion(pSrcTexVk, CopyAttribs.SrcTextureTransitionMode, pDstTexVk, CopyAttribs.DstTextureTransitionMode, CopyRegion);
+ }
+ else if (SrcTexDesc.Usage == USAGE_STAGING && DstTexDesc.Usage != USAGE_STAGING)
+ {
+ DEV_CHECK_ERR((SrcTexDesc.CPUAccessFlags & CPU_ACCESS_WRITE), "Attempting to copy from staging texture that was not created with CPU_ACCESS_WRITE flag");
+ DEV_CHECK_ERR(pSrcTexVk->GetState() == RESOURCE_STATE_COPY_SOURCE, "Source staging texture must permanently be in RESOURCE_STATE_COPY_SOURCE state");
+
+ auto SrcBufferOffset = GetStagingDataOffset(SrcTexDesc, CopyAttribs.SrcSlice, CopyAttribs.SrcMipLevel);
+ auto SrcMipLevelAttribs = GetMipLevelProperties(SrcTexDesc, CopyAttribs.SrcMipLevel);
+ // address of (x,y,z) = region->bufferOffset + (((z * imageHeight) + y) * rowLength + x) * texelBlockSize; (18.4.1)
+ SrcBufferOffset +=
+ (pSrcBox->MinZ * SrcMipLevelAttribs.Height + pSrcBox->MinY) * SrcMipLevelAttribs.RowSize +
+ (FmtAttribs.ComponentType != COMPONENT_TYPE_COMPRESSED ?
+ pSrcBox->MinX * FmtAttribs.ComponentSize * FmtAttribs.NumComponents :
+ pSrcBox->MinX / FmtAttribs.BlockWidth * FmtAttribs.ComponentSize);
+
+ Box DstBox;
+ DstBox.MinX = CopyAttribs.DstX;
+ DstBox.MinY = CopyAttribs.DstY;
+ DstBox.MinZ = CopyAttribs.DstZ;
+ DstBox.MaxX = DstBox.MinX + pSrcBox->MaxX - pSrcBox->MinX;
+ DstBox.MaxY = DstBox.MinY + pSrcBox->MaxY - pSrcBox->MinY;
+ DstBox.MaxZ = DstBox.MinZ + pSrcBox->MaxZ - pSrcBox->MinZ;
+
+ CopyBufferToTexture(
+ pSrcTexVk->GetVkStagingBuffer(),
+ SrcBufferOffset,
+ SrcMipLevelAttribs.Width,
+ *pDstTexVk,
+ DstBox,
+ CopyAttribs.DstMipLevel,
+ CopyAttribs.DstSlice,
+ CopyAttribs.DstTextureTransitionMode
+ );
+ }
+ else if (SrcTexDesc.Usage != USAGE_STAGING && DstTexDesc.Usage == USAGE_STAGING)
+ {
+ DEV_CHECK_ERR((DstTexDesc.CPUAccessFlags & CPU_ACCESS_READ), "Attempting to copy to staging texture that was not created with CPU_ACCESS_READ flag");
+ DEV_CHECK_ERR(pDstTexVk->GetState() == RESOURCE_STATE_COPY_DEST, "Destination staging texture must permanently be in RESOURCE_STATE_COPY_DEST state");
+
+ auto DstBufferOffset = GetStagingDataOffset(DstTexDesc, CopyAttribs.DstSlice, CopyAttribs.DstMipLevel);
+ auto DstMipLevelAttribs = GetMipLevelProperties(DstTexDesc, CopyAttribs.DstMipLevel);
+ // address of (x,y,z) = region->bufferOffset + (((z * imageHeight) + y) * rowLength + x) * texelBlockSize; (18.4.1)
+ DstBufferOffset +=
+ (CopyAttribs.DstZ * DstMipLevelAttribs.Height + CopyAttribs.DstY) * DstMipLevelAttribs.RowSize *
+ (FmtAttribs.ComponentType != COMPONENT_TYPE_COMPRESSED ?
+ CopyAttribs.DstX * FmtAttribs.ComponentSize * FmtAttribs.NumComponents :
+ CopyAttribs.DstX / FmtAttribs.BlockWidth * FmtAttribs.ComponentSize);
+
+ CopyTextureToBuffer(
+ *pSrcTexVk,
+ *pSrcBox,
+ CopyAttribs.SrcMipLevel,
+ CopyAttribs.SrcSlice,
+ CopyAttribs.SrcTextureTransitionMode,
+ pDstTexVk->GetVkStagingBuffer(),
+ DstBufferOffset,
+ DstMipLevelAttribs.Width
+ );
}
else
- aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-
- CopyRegion.srcSubresource.baseArrayLayer = CopyAttribs.SrcSlice;
- CopyRegion.srcSubresource.layerCount = 1;
- CopyRegion.srcSubresource.mipLevel = CopyAttribs.SrcMipLevel;
- CopyRegion.srcSubresource.aspectMask = aspectMask;
-
- CopyRegion.dstSubresource.baseArrayLayer = CopyAttribs.DstSlice;
- CopyRegion.dstSubresource.layerCount = 1;
- CopyRegion.dstSubresource.mipLevel = CopyAttribs.DstMipLevel;
- CopyRegion.dstSubresource.aspectMask = aspectMask;
-
- CopyRegion.dstOffset.x = CopyAttribs.DstX;
- CopyRegion.dstOffset.y = CopyAttribs.DstY;
- CopyRegion.dstOffset.z = CopyAttribs.DstZ;
-
- CopyTextureRegion(pSrcTexVk, CopyAttribs.SrcTextureTransitionMode, pDstTexVk, CopyAttribs.DstTextureTransitionMode, CopyRegion);
+ {
+ UNSUPPORTED("Copying data between staging textures is not supported and is likely not want you really want to do");
+ }
}
void DeviceContextVkImpl::CopyTextureRegion(TextureVkImpl* pSrcTexture,
@@ -1504,8 +1569,8 @@ namespace Diligent
CopyBufferToTexture(Allocation.vkBuffer,
static_cast<Uint32>(Allocation.AlignedOffset),
CopyInfo.StrideInTexels,
- CopyInfo.Region,
TextureVk,
+ CopyInfo.Region,
MipLevel,
Slice,
TextureTransitionMode);
@@ -1517,19 +1582,13 @@ namespace Diligent
m_GenerateMipsHelper->GenerateMips(*ValidatedCast<TextureViewVkImpl>(pTexView), *this, *m_GenerateMipsSRB);
}
- void DeviceContextVkImpl::CopyBufferToTexture(VkBuffer vkBuffer,
- Uint32 BufferOffset,
- Uint32 BufferRowStrideInTexels,
- const Box& Region,
- TextureVkImpl& TextureVk,
- Uint32 MipLevel,
- Uint32 ArraySlice,
- RESOURCE_STATE_TRANSITION_MODE TextureTransitionMode)
+ static VkBufferImageCopy GetBufferImageCopyInfo(Uint32 BufferOffset,
+ Uint32 BufferRowStrideInTexels,
+ const TextureDesc& TexDesc,
+ const Box& Region,
+ Uint32 MipLevel,
+ Uint32 ArraySlice)
{
- EnsureVkCmdBuffer();
- TransitionOrVerifyTextureState(TextureVk, TextureTransitionMode, RESOURCE_STATE_COPY_DEST, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
- "Using texture as copy destination (DeviceContextVkImpl::CopyBufferToTexture)");
-
VkBufferImageCopy CopyRegion = {};
VERIFY( (BufferOffset % 4) == 0, "Source buffer offset must be multiple of 4 (18.4)");
CopyRegion.bufferOffset = BufferOffset; // must be a multiple of 4 (18.4)
@@ -1537,10 +1596,9 @@ namespace Diligent
// bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion of a larger two- or
// three-dimensional image, and control the addressing calculations of data in buffer memory. If either of these
// values is zero, that aspect of the buffer memory is considered to be tightly packed according to the imageExtent (18.4).
- CopyRegion.bufferRowLength = BufferRowStrideInTexels;
+ CopyRegion.bufferRowLength = BufferRowStrideInTexels;
CopyRegion.bufferImageHeight = 0;
- const auto& TexDesc = TextureVk.GetDesc();
const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
// The aspectMask member of imageSubresource must only have a single bit set (18.4)
if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
@@ -1558,8 +1616,8 @@ namespace Diligent
CopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
CopyRegion.imageSubresource.baseArrayLayer = ArraySlice;
- CopyRegion.imageSubresource.layerCount = 1;
- CopyRegion.imageSubresource.mipLevel = MipLevel;
+ CopyRegion.imageSubresource.layerCount = 1;
+ CopyRegion.imageSubresource.mipLevel = MipLevel;
// - imageOffset.x and (imageExtent.width + imageOffset.x) must both be greater than or equal to 0 and
// less than or equal to the image subresource width (18.4)
// - imageOffset.y and (imageExtent.height + imageOffset.y) must both be greater than or equal to 0 and
@@ -1580,15 +1638,59 @@ namespace Diligent
static_cast<uint32_t>(Region.MaxY - Region.MinY),
static_cast<uint32_t>(Region.MaxZ - Region.MinZ)
};
-
+
+ return CopyRegion;
+ }
+
+ void DeviceContextVkImpl::CopyBufferToTexture(VkBuffer vkSrcBuffer,
+ Uint32 SrcBufferOffset,
+ Uint32 SrcBufferRowStrideInTexels,
+ TextureVkImpl& DstTextureVk,
+ const Box& DstRegion,
+ Uint32 DstMipLevel,
+ Uint32 DstArraySlice,
+ RESOURCE_STATE_TRANSITION_MODE DstTextureTransitionMode)
+ {
+ EnsureVkCmdBuffer();
+ TransitionOrVerifyTextureState(DstTextureVk, DstTextureTransitionMode, RESOURCE_STATE_COPY_DEST, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ "Using texture as copy destination (DeviceContextVkImpl::CopyBufferToTexture)");
+
+ const auto& TexDesc = DstTextureVk.GetDesc();
+ VkBufferImageCopy BuffImgCopy = GetBufferImageCopyInfo(SrcBufferOffset, SrcBufferRowStrideInTexels, TexDesc, DstRegion, DstMipLevel, DstArraySlice);
+
m_CommandBuffer.CopyBufferToImage(
- vkBuffer,
- TextureVk.GetVkImage(),
+ vkSrcBuffer,
+ DstTextureVk.GetVkImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL (18.4)
1,
- &CopyRegion);
+ &BuffImgCopy);
+ }
+
+ void DeviceContextVkImpl::CopyTextureToBuffer(TextureVkImpl& SrcTextureVk,
+ const Box& SrcRegion,
+ Uint32 SrcMipLevel,
+ Uint32 SrcArraySlice,
+ RESOURCE_STATE_TRANSITION_MODE SrcTextureTransitionMode,
+ VkBuffer vkDstBuffer,
+ Uint32 DstBufferOffset,
+ Uint32 DstBufferRowStrideInTexels)
+ {
+ EnsureVkCmdBuffer();
+ TransitionOrVerifyTextureState(SrcTextureVk, SrcTextureTransitionMode, RESOURCE_STATE_COPY_SOURCE, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ "Using texture as source destination (DeviceContextVkImpl::CopyTextureToBuffer)");
+
+ const auto& TexDesc = SrcTextureVk.GetDesc();
+ VkBufferImageCopy BuffImgCopy = GetBufferImageCopyInfo(DstBufferOffset, DstBufferRowStrideInTexels, TexDesc, SrcRegion, SrcMipLevel, SrcArraySlice);
+
+ m_CommandBuffer.CopyImageToBuffer(
+ SrcTextureVk.GetVkImage(),
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // must be VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL (18.4)
+ vkDstBuffer,
+ 1,
+ &BuffImgCopy);
}
+
void DeviceContextVkImpl::MapTextureSubresource( ITexture* pTexture,
Uint32 MipLevel,
Uint32 ArraySlice,
@@ -1601,16 +1703,7 @@ namespace Diligent
TextureVkImpl& TextureVk = *ValidatedCast<TextureVkImpl>(pTexture);
const auto& TexDesc = TextureVk.GetDesc();
-
- if (MapType != MAP_WRITE)
- {
- LOG_ERROR("Textures can currently only be mapped for writing in Vulkan backend");
- MappedData = MappedTextureSubresource{};
- return;
- }
-
- if ((MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_DO_NOT_SYNCHRONIZE)) != 0)
- LOG_WARNING_MESSAGE_ONCE("Mapping textures with flags MAP_FLAG_DISCARD or MAP_FLAG_DO_NOT_SYNCHRONIZE has no effect in Vulkan backend");
+ const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
Box FullExtentBox;
if (pMapRegion == nullptr)
@@ -1621,27 +1714,84 @@ namespace Diligent
FullExtentBox.MaxZ = std::max(TexDesc.Depth >> MipLevel, 1u);
pMapRegion = &FullExtentBox;
}
-
- auto CopyInfo = GetBufferToTextureCopyInfo(TexDesc, MipLevel, *pMapRegion);
- const auto& DeviceLimits = m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetPhysicalDevice().GetProperties().limits;
- // Source buffer offset must be multiple of 4 (18.4)
- auto Alignment = std::max(DeviceLimits.optimalBufferCopyOffsetAlignment, VkDeviceSize{4});
- // If the calling command's VkImage parameter is a compressed image, bufferOffset must be a multiple of
- // the compressed texel block size in bytes (18.4)
- const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
- if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED)
+
+ if (TexDesc.Usage == USAGE_DYNAMIC)
{
- Alignment = std::max(Alignment, VkDeviceSize{FmtAttribs.ComponentSize});
+ if (MapType != MAP_WRITE)
+ {
+ LOG_ERROR("Textures can currently only be mapped for writing in Vulkan backend");
+ MappedData = MappedTextureSubresource{};
+ return;
+ }
+
+ if ((MapFlags & (MAP_FLAG_DISCARD | MAP_FLAG_DO_NOT_SYNCHRONIZE)) != 0)
+ LOG_INFO_MESSAGE_ONCE("Mapping textures with flags MAP_FLAG_DISCARD or MAP_FLAG_DO_NOT_SYNCHRONIZE has no effect in Vulkan backend");
+
+ auto CopyInfo = GetBufferToTextureCopyInfo(TexDesc, MipLevel, *pMapRegion);
+ const auto& DeviceLimits = m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetPhysicalDevice().GetProperties().limits;
+ // Source buffer offset must be multiple of 4 (18.4)
+ auto Alignment = std::max(DeviceLimits.optimalBufferCopyOffsetAlignment, VkDeviceSize{4});
+ // If the calling command's VkImage parameter is a compressed image, bufferOffset must be a multiple of
+ // the compressed texel block size in bytes (18.4)
+ if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED)
+ {
+ Alignment = std::max(Alignment, VkDeviceSize{FmtAttribs.ComponentSize});
+ }
+ auto Allocation = AllocateDynamicSpace(CopyInfo.MemorySize, static_cast<Uint32>(Alignment));
+
+ MappedData.pData = reinterpret_cast<Uint8*>(Allocation.pDynamicMemMgr->GetCPUAddress()) + Allocation.AlignedOffset;
+ MappedData.Stride = CopyInfo.Stride;
+ MappedData.DepthStride = CopyInfo.DepthStride;
+
+ auto it = m_MappedTextures.emplace(MappedTextureKey{&TextureVk, MipLevel, ArraySlice}, MappedTexture{CopyInfo, std::move(Allocation)});
+ if(!it.second)
+ LOG_ERROR_MESSAGE("Mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "' has already been mapped");
}
- auto Allocation = AllocateDynamicSpace(CopyInfo.MemorySize, static_cast<Uint32>(Alignment));
+ else if (TexDesc.Usage == USAGE_STAGING)
+ {
+ if( (MapFlags & MAP_FLAG_DO_NOT_SYNCHRONIZE) == 0 )
+ {
+ LOG_WARNING_MESSAGE_ONCE("Mapping staging textures is never synchronized in Vulkan backend. "
+ "Application must use fences or other synchronization methods to explicitly synchronize "
+ "access and map texture with MAP_FLAG_DO_NOT_SYNCHRONIZE flag.");
+ }
+
+ auto SubresourceOffset = GetStagingDataOffset(TexDesc, ArraySlice, MipLevel);
+ auto MipLevelAttribs = GetMipLevelProperties(TexDesc, MipLevel);
+ // address of (x,y,z) = region->bufferOffset + (((z * imageHeight) + y) * rowLength + x) * texelBlockSize; (18.4.1)
+ auto MapStartOffset = SubresourceOffset +
+ (pMapRegion->MinZ * MipLevelAttribs.Height + pMapRegion->MinY) * MipLevelAttribs.RowSize +
+ (FmtAttribs.ComponentType != COMPONENT_TYPE_COMPRESSED ?
+ pMapRegion->MinX * FmtAttribs.ComponentSize * FmtAttribs.NumComponents :
+ pMapRegion->MinX / FmtAttribs.BlockWidth * FmtAttribs.ComponentSize);
- MappedData.pData = reinterpret_cast<Uint8*>(Allocation.pDynamicMemMgr->GetCPUAddress()) + Allocation.AlignedOffset;
- MappedData.Stride = CopyInfo.Stride;
- MappedData.DepthStride = CopyInfo.DepthStride;
+ MappedData.pData = TextureVk.GetStagingDataCPUAddress() + MapStartOffset;
+ MappedData.Stride = MipLevelAttribs.RowSize;
+ MappedData.DepthStride = MipLevelAttribs.RowSize * MipLevelAttribs.Height;
- auto it = m_MappedTextures.emplace(MappedTextureKey{&TextureVk, MipLevel, ArraySlice}, MappedTexture{CopyInfo, std::move(Allocation)});
- if(!it.second)
- LOG_ERROR_MESSAGE("Mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "' has already been mapped");
+ if (MapType == MAP_READ)
+ {
+ DEV_CHECK_ERR((TexDesc.CPUAccessFlags & CPU_ACCESS_READ), "Texture '", TexDesc.Name, "' was not created with CPU_ACCESS_READ flag and can't be mapped for reading");
+ // Reaback memory is not created with HOST_COHERENT flag, so we have to explicitly invalidate the mapped range
+ // to make device writes visible to CPU reads
+ VERIFY_EXPR(pMapRegion->MaxZ >= 1 && pMapRegion->MaxY >= 1);
+ auto MapEndOffset = SubresourceOffset +
+ ((pMapRegion->MaxZ-1) * MipLevelAttribs.Height + (pMapRegion->MaxY-1)) * MipLevelAttribs.RowSize +
+ (FmtAttribs.ComponentType != COMPONENT_TYPE_COMPRESSED ?
+ pMapRegion->MaxX * FmtAttribs.ComponentSize * FmtAttribs.NumComponents :
+ pMapRegion->MaxX / FmtAttribs.BlockWidth * FmtAttribs.ComponentSize);
+ TextureVk.InvalidateStagingRange(MapStartOffset, MapEndOffset - MapStartOffset);
+ }
+ else if (MapType == MAP_WRITE)
+ {
+ DEV_CHECK_ERR((TexDesc.CPUAccessFlags & CPU_ACCESS_WRITE), "Texture '", TexDesc.Name, "' was not created with CPU_ACCESS_WRITE flag and can't be mapped for writing");
+ // Nothing needs to be done when mapping texture for writing
+ }
+ }
+ else
+ {
+ UNSUPPORTED(GetUsageString(TexDesc.Usage), " textures cannot currently be mapped in Vulkan back-end");
+ }
}
void DeviceContextVkImpl::UnmapTextureSubresource(ITexture* pTexture,
@@ -1652,23 +1802,43 @@ namespace Diligent
TextureVkImpl& TextureVk = *ValidatedCast<TextureVkImpl>(pTexture);
const auto& TexDesc = TextureVk.GetDesc();
- auto UploadSpaceIt = m_MappedTextures.find(MappedTextureKey{&TextureVk, MipLevel, ArraySlice});
- if(UploadSpaceIt != m_MappedTextures.end())
- {
- auto& MappedTex = UploadSpaceIt->second;
- CopyBufferToTexture(MappedTex.Allocation.pDynamicMemMgr->GetVkBuffer(),
- static_cast<Uint32>(MappedTex.Allocation.AlignedOffset),
- MappedTex.CopyInfo.StrideInTexels,
- MappedTex.CopyInfo.Region,
- TextureVk,
- MipLevel,
- ArraySlice,
- RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
- m_MappedTextures.erase(UploadSpaceIt);
+
+ if (TexDesc.Usage == USAGE_DYNAMIC)
+ {
+ auto UploadSpaceIt = m_MappedTextures.find(MappedTextureKey{&TextureVk, MipLevel, ArraySlice});
+ if(UploadSpaceIt != m_MappedTextures.end())
+ {
+ auto& MappedTex = UploadSpaceIt->second;
+ CopyBufferToTexture(MappedTex.Allocation.pDynamicMemMgr->GetVkBuffer(),
+ static_cast<Uint32>(MappedTex.Allocation.AlignedOffset),
+ MappedTex.CopyInfo.StrideInTexels,
+ TextureVk,
+ MappedTex.CopyInfo.Region,
+ MipLevel,
+ ArraySlice,
+ RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ m_MappedTextures.erase(UploadSpaceIt);
+ }
+ else
+ {
+ LOG_ERROR_MESSAGE("Failed to unmap mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "'. The texture has either been unmapped already or has not been mapped");
+ }
+ }
+ else if (TexDesc.Usage == USAGE_STAGING)
+ {
+ if (TexDesc.CPUAccessFlags & CPU_ACCESS_READ)
+ {
+ // Nothing needs to be done
+ }
+ else if (TexDesc.CPUAccessFlags & CPU_ACCESS_WRITE)
+ {
+ // Upload textures are currently created with VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, so
+ // there is no need to explicitly flush the mapped range.
+ }
}
else
{
- LOG_ERROR_MESSAGE("Failed to unmap mip level ", MipLevel, ", slice ", ArraySlice, " of texture '", TexDesc.Name, "'. The texture has either been unmapped already or has not been mapped");
+ UNSUPPORTED(GetUsageString(TexDesc.Usage), " textures cannot currently be mapped in Vulkan back-end");
}
}
diff --git a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp
index 1c77c4b3..6c4e08a0 100644
--- a/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/TextureVkImpl.cpp
@@ -31,11 +31,33 @@
#include "EngineMemory.h"
#include "StringTools.h"
-using namespace Diligent;
-
namespace Diligent
{
+MipLevelProperties GetMipLevelProperties(const TextureDesc& TexDesc, Uint32 MipLevel)
+{
+ MipLevelProperties MipProps;
+ const auto& FmtAttribs = GetTextureFormatAttribs(TexDesc.Format);
+
+ MipProps.Width = std::max(TexDesc.Width >> MipLevel, 1u);
+ MipProps.Height = std::max(TexDesc.Height >> MipLevel, 1u);
+ MipProps.Depth = (TexDesc.Type == RESOURCE_DIM_TEX_3D) ? std::max(TexDesc.Depth >> MipLevel, 1u) : 1u;
+ if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED)
+ {
+ VERIFY_EXPR(FmtAttribs.BlockWidth > 1 && FmtAttribs.BlockHeight > 1);
+ MipProps.Width = (MipProps.Width + FmtAttribs.BlockWidth -1) / FmtAttribs.BlockWidth;
+ MipProps.Height = (MipProps.Height + FmtAttribs.BlockHeight-1) / FmtAttribs.BlockHeight;
+ MipProps.RowSize = MipProps.Width * Uint32{FmtAttribs.ComponentSize}; // ComponentSize is the block size
+ }
+ else
+ {
+ MipProps.RowSize = MipProps.Width * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents};
+ }
+ MipProps.MipSize = MipProps.RowSize * MipProps.Height * MipProps.Depth;
+
+ return MipProps;
+}
+
TextureVkImpl :: TextureVkImpl(IReferenceCounters* pRefCounters,
FixedBlockMemoryAllocator& TexViewObjAllocator,
RenderDeviceVkImpl* pRenderDeviceVk,
@@ -52,394 +74,465 @@ TextureVkImpl :: TextureVkImpl(IReferenceCounters* pRefCounters,
const auto& LogicalDevice = pRenderDeviceVk->GetLogicalDevice();
- VkImageCreateInfo ImageCI = {};
- ImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
- ImageCI.pNext = nullptr;
- ImageCI.flags = 0;
- if(m_Desc.Type == RESOURCE_DIM_TEX_CUBE || m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
- ImageCI.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
- if(FmtAttribs.IsTypeless)
- ImageCI.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; // Specifies that the image can be used to create a
- // VkImageView with a different format from the image.
-
- if (m_Desc.Type == RESOURCE_DIM_TEX_1D || m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY)
- ImageCI.imageType = VK_IMAGE_TYPE_1D;
- else if (m_Desc.Type == RESOURCE_DIM_TEX_2D || m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY || m_Desc.Type == RESOURCE_DIM_TEX_CUBE || m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
- ImageCI.imageType = VK_IMAGE_TYPE_2D;
- else if (m_Desc.Type == RESOURCE_DIM_TEX_3D)
+ if (m_Desc.Usage == USAGE_STATIC || m_Desc.Usage == USAGE_DEFAULT || m_Desc.Usage == USAGE_DYNAMIC)
{
- ImageCI.imageType = VK_IMAGE_TYPE_3D;
- ImageCI.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
- }
- else
- {
- LOG_ERROR_AND_THROW("Unknown texture type");
- }
+ VkImageCreateInfo ImageCI = {};
+ ImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ ImageCI.pNext = nullptr;
+ ImageCI.flags = 0;
+ if(m_Desc.Type == RESOURCE_DIM_TEX_CUBE || m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
+ ImageCI.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+ if(FmtAttribs.IsTypeless)
+ ImageCI.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; // Specifies that the image can be used to create a
+ // VkImageView with a different format from the image.
+
+ if (m_Desc.Type == RESOURCE_DIM_TEX_1D || m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY)
+ ImageCI.imageType = VK_IMAGE_TYPE_1D;
+ else if (m_Desc.Type == RESOURCE_DIM_TEX_2D || m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY || m_Desc.Type == RESOURCE_DIM_TEX_CUBE || m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
+ ImageCI.imageType = VK_IMAGE_TYPE_2D;
+ else if (m_Desc.Type == RESOURCE_DIM_TEX_3D)
+ {
+ ImageCI.imageType = VK_IMAGE_TYPE_3D;
+ ImageCI.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
+ }
+ else
+ {
+ LOG_ERROR_AND_THROW("Unknown texture type");
+ }
- if (FmtAttribs.IsTypeless)
- {
- TEXTURE_VIEW_TYPE DefaultTexView;
- if(m_Desc.BindFlags & BIND_DEPTH_STENCIL)
- DefaultTexView = TEXTURE_VIEW_DEPTH_STENCIL;
- else if (m_Desc.BindFlags & BIND_UNORDERED_ACCESS)
- DefaultTexView = TEXTURE_VIEW_UNORDERED_ACCESS;
- else if (m_Desc.BindFlags & BIND_RENDER_TARGET)
- DefaultTexView = TEXTURE_VIEW_RENDER_TARGET;
+ if (FmtAttribs.IsTypeless)
+ {
+ TEXTURE_VIEW_TYPE DefaultTexView;
+ if(m_Desc.BindFlags & BIND_DEPTH_STENCIL)
+ DefaultTexView = TEXTURE_VIEW_DEPTH_STENCIL;
+ else if (m_Desc.BindFlags & BIND_UNORDERED_ACCESS)
+ DefaultTexView = TEXTURE_VIEW_UNORDERED_ACCESS;
+ else if (m_Desc.BindFlags & BIND_RENDER_TARGET)
+ DefaultTexView = TEXTURE_VIEW_RENDER_TARGET;
+ else
+ DefaultTexView = TEXTURE_VIEW_SHADER_RESOURCE;
+ auto DefaultViewFormat = GetDefaultTextureViewFormat(m_Desc, DefaultTexView);
+ ImageCI.format = TexFormatToVkFormat(DefaultViewFormat);
+ }
else
- DefaultTexView = TEXTURE_VIEW_SHADER_RESOURCE;
- auto DefaultViewFormat = GetDefaultTextureViewFormat(m_Desc, DefaultTexView);
- ImageCI.format = TexFormatToVkFormat(DefaultViewFormat);
- }
- else
- {
- ImageCI.format = TexFormatToVkFormat(m_Desc.Format);
- }
+ {
+ ImageCI.format = TexFormatToVkFormat(m_Desc.Format);
+ }
- ImageCI.extent.width = m_Desc.Width;
- ImageCI.extent.height = (m_Desc.Type == RESOURCE_DIM_TEX_1D || m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY) ? 1 : m_Desc.Height;
- ImageCI.extent.depth = (m_Desc.Type == RESOURCE_DIM_TEX_3D) ? m_Desc.Depth : 1;
+ ImageCI.extent.width = m_Desc.Width;
+ ImageCI.extent.height = (m_Desc.Type == RESOURCE_DIM_TEX_1D || m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY) ? 1 : m_Desc.Height;
+ ImageCI.extent.depth = (m_Desc.Type == RESOURCE_DIM_TEX_3D) ? m_Desc.Depth : 1;
- ImageCI.mipLevels = m_Desc.MipLevels;
- if (m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY ||
- m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY ||
- m_Desc.Type == RESOURCE_DIM_TEX_CUBE ||
- m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
- ImageCI.arrayLayers = m_Desc.ArraySize;
- else
- ImageCI.arrayLayers = 1;
+ ImageCI.mipLevels = m_Desc.MipLevels;
+ if (m_Desc.Type == RESOURCE_DIM_TEX_1D_ARRAY ||
+ m_Desc.Type == RESOURCE_DIM_TEX_2D_ARRAY ||
+ m_Desc.Type == RESOURCE_DIM_TEX_CUBE ||
+ m_Desc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
+ ImageCI.arrayLayers = m_Desc.ArraySize;
+ else
+ ImageCI.arrayLayers = 1;
- ImageCI.samples = static_cast<VkSampleCountFlagBits>(1 << (m_Desc.SampleCount-1));
- ImageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
+ ImageCI.samples = static_cast<VkSampleCountFlagBits>(1 << (m_Desc.SampleCount-1));
+ ImageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
- ImageCI.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- if (m_Desc.BindFlags & BIND_RENDER_TARGET)
- {
- // VK_IMAGE_USAGE_TRANSFER_DST_BIT is required for vkCmdClearColorImage()
- ImageCI.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- }
- if (m_Desc.BindFlags & BIND_DEPTH_STENCIL)
- {
- // VK_IMAGE_USAGE_TRANSFER_DST_BIT is required for vkCmdClearDepthStencilImage()
- ImageCI.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
- }
- if ((m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || (m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS))
- {
- ImageCI.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
- }
- if (m_Desc.BindFlags & BIND_SHADER_RESOURCE)
- {
- ImageCI.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
- }
+ ImageCI.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ if (m_Desc.BindFlags & BIND_RENDER_TARGET)
+ {
+ // VK_IMAGE_USAGE_TRANSFER_DST_BIT is required for vkCmdClearColorImage()
+ ImageCI.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ }
+ if (m_Desc.BindFlags & BIND_DEPTH_STENCIL)
+ {
+ // VK_IMAGE_USAGE_TRANSFER_DST_BIT is required for vkCmdClearDepthStencilImage()
+ ImageCI.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ }
+ if ((m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || (m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS))
+ {
+ ImageCI.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
+ }
+ if (m_Desc.BindFlags & BIND_SHADER_RESOURCE)
+ {
+ ImageCI.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
+ }
- ImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- ImageCI.queueFamilyIndexCount = 0;
- ImageCI.pQueueFamilyIndices = nullptr;
+ ImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ ImageCI.queueFamilyIndexCount = 0;
+ ImageCI.pQueueFamilyIndices = nullptr;
- // initialLayout must be either VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_PREINITIALIZED (11.4)
- // If it is VK_IMAGE_LAYOUT_PREINITIALIZED, then the image data can be preinitialized by the host
- // while using this layout, and the transition away from this layout will preserve that data.
- // If it is VK_IMAGE_LAYOUT_UNDEFINED, then the contents of the data are considered to be undefined,
- // and the transition away from this layout is not guaranteed to preserve that data.
- ImageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ // initialLayout must be either VK_IMAGE_LAYOUT_UNDEFINED or VK_IMAGE_LAYOUT_PREINITIALIZED (11.4)
+ // If it is VK_IMAGE_LAYOUT_PREINITIALIZED, then the image data can be preinitialized by the host
+ // while using this layout, and the transition away from this layout will preserve that data.
+ // If it is VK_IMAGE_LAYOUT_UNDEFINED, then the contents of the data are considered to be undefined,
+ // and the transition away from this layout is not guaranteed to preserve that data.
+ ImageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
- bool bInitializeTexture = (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0);
+ bool bInitializeTexture = (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0);
- m_VulkanImage = LogicalDevice.CreateImage(ImageCI, m_Desc.Name);
+ m_VulkanImage = LogicalDevice.CreateImage(ImageCI, m_Desc.Name);
- VkMemoryRequirements MemReqs = LogicalDevice.GetImageMemoryRequirements(m_VulkanImage);
+ VkMemoryRequirements MemReqs = LogicalDevice.GetImageMemoryRequirements(m_VulkanImage);
- VkMemoryPropertyFlags ImageMemoryFlags = 0;
- if (m_Desc.Usage == USAGE_STAGING)
- ImageMemoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
- else
- ImageMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ VkMemoryPropertyFlags ImageMemoryFlags = 0;
+ if (m_Desc.Usage == USAGE_STAGING)
+ ImageMemoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ else
+ ImageMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
- VERIFY( IsPowerOfTwo(MemReqs.alignment), "Alignment is not power of 2!");
- m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs, ImageMemoryFlags);
- auto AlignedOffset = Align(m_MemoryAllocation.UnalignedOffset, MemReqs.alignment);
- VERIFY_EXPR(m_MemoryAllocation.Size >= MemReqs.size + (AlignedOffset - m_MemoryAllocation.UnalignedOffset));
- auto Memory = m_MemoryAllocation.Page->GetVkMemory();
- auto err = LogicalDevice.BindImageMemory(m_VulkanImage, Memory, AlignedOffset);
- CHECK_VK_ERROR_AND_THROW(err, "Failed to bind image memory");
+ VERIFY( IsPowerOfTwo(MemReqs.alignment), "Alignment is not power of 2!");
+ m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs, ImageMemoryFlags);
+ auto AlignedOffset = Align(m_MemoryAllocation.UnalignedOffset, MemReqs.alignment);
+ VERIFY_EXPR(m_MemoryAllocation.Size >= MemReqs.size + (AlignedOffset - m_MemoryAllocation.UnalignedOffset));
+ auto Memory = m_MemoryAllocation.Page->GetVkMemory();
+ auto err = LogicalDevice.BindImageMemory(m_VulkanImage, Memory, AlignedOffset);
+ CHECK_VK_ERROR_AND_THROW(err, "Failed to bind image memory");
- // Vulkan validation layers do not like uninitialized memory, so if no initial data
- // is provided, we will clear the memory
+ // Vulkan validation layers do not like uninitialized memory, so if no initial data
+ // is provided, we will clear the memory
- VulkanUtilities::CommandPoolWrapper CmdPool;
- VkCommandBuffer vkCmdBuff;
- pRenderDeviceVk->AllocateTransientCmdPool(CmdPool, vkCmdBuff, "Transient command pool to copy staging data to a device buffer");
+ VulkanUtilities::CommandPoolWrapper CmdPool;
+ VkCommandBuffer vkCmdBuff;
+ pRenderDeviceVk->AllocateTransientCmdPool(CmdPool, vkCmdBuff, "Transient command pool to copy staging data to a device buffer");
- VkImageAspectFlags aspectMask = 0;
- if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
- aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
- else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
- {
- if(bInitializeTexture)
- {
- UNSUPPORTED("Initializing depth-stencil texture is not currently supported");
- // Only single aspect bit must be specified when copying texture data
- }
- aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
- }
- else
- aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
-
- // For either clear or copy command, dst layout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
- VkImageSubresourceRange SubresRange;
- SubresRange.aspectMask = aspectMask;
- SubresRange.baseArrayLayer = 0;
- SubresRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
- SubresRange.baseMipLevel = 0;
- SubresRange.levelCount = VK_REMAINING_MIP_LEVELS;
- auto EnabledGraphicsShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages();
- VulkanUtilities::VulkanCommandBuffer::TransitionImageLayout(vkCmdBuff, m_VulkanImage, ImageCI.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, SubresRange, EnabledGraphicsShaderStages);
- SetState(RESOURCE_STATE_COPY_DEST);
- const auto CurrentLayout = GetLayout();
- VERIFY_EXPR(CurrentLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
-
- if(bInitializeTexture)
- {
- Uint32 ExpectedNumSubresources = ImageCI.mipLevels * ImageCI.arrayLayers;
- if (pInitData->NumSubresources != ExpectedNumSubresources )
- LOG_ERROR_AND_THROW("Incorrect number of subresources in init data. ", ExpectedNumSubresources, " expected, while ", pInitData->NumSubresources, " provided");
-
- std::vector<VkBufferImageCopy> Regions(pInitData->NumSubresources);
-
- Uint64 uploadBufferSize = 0;
- Uint32 subres = 0;
- for(Uint32 layer = 0; layer < ImageCI.arrayLayers; ++layer)
+ VkImageAspectFlags aspectMask = 0;
+ if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
+ aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
+ else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
{
- for(Uint32 mip = 0; mip < ImageCI.mipLevels; ++mip)
+ if(bInitializeTexture)
{
- const auto& SubResData = pInitData->pSubResources[subres]; (void)SubResData;
- auto& CopyRegion = Regions[subres];
-
- auto MipWidth = std::max(m_Desc.Width >> mip, 1u);
- auto MipHeight = std::max(m_Desc.Height >> mip, 1u);
- auto MipDepth = (m_Desc.Type == RESOURCE_DIM_TEX_3D) ? std::max(m_Desc.Depth >> mip, 1u) : 1u;
-
- CopyRegion.bufferOffset = uploadBufferSize; // offset in bytes from the start of the buffer object
- // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion
- // of a larger two- or three-dimensional image, and control the addressing calculations of
- // data in buffer memory. If either of these values is zero, that aspect of the buffer memory
- // is considered to be tightly packed according to the imageExtent. (18.4)
- CopyRegion.bufferRowLength = 0;
- CopyRegion.bufferImageHeight = 0;
- // For block-compression formats, all parameters are still specified in texels rather than compressed texel blocks (18.4.1)
- CopyRegion.imageOffset = VkOffset3D{0, 0, 0};
- CopyRegion.imageExtent = VkExtent3D{MipWidth, MipHeight, MipDepth};
-
- CopyRegion.imageSubresource.aspectMask = aspectMask;
- CopyRegion.imageSubresource.mipLevel = mip;
- CopyRegion.imageSubresource.baseArrayLayer = layer;
- CopyRegion.imageSubresource.layerCount = 1;
-
- Uint32 RowSize = 0;
- if(FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED)
- {
- VERIFY_EXPR(FmtAttribs.BlockWidth > 1 && FmtAttribs.BlockHeight > 1);
- MipWidth = (MipWidth + FmtAttribs.BlockWidth -1) / FmtAttribs.BlockWidth;
- MipHeight = (MipHeight + FmtAttribs.BlockHeight-1) / FmtAttribs.BlockHeight;
- RowSize = MipWidth * Uint32{FmtAttribs.ComponentSize}; // ComponentSize is the block size
- }
- else
- {
- RowSize = MipWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents};
- }
- auto MipSize = RowSize * MipHeight * MipDepth;
- VERIFY(SubResData.Stride == 0 || SubResData.Stride >= RowSize, "Stride is too small");
- VERIFY(SubResData.DepthStride == 0 || SubResData.DepthStride >= RowSize * MipHeight, "Depth stride is too small");
-
- // bufferOffset must be a multiple of 4 (18.4)
- // If the calling command's VkImage parameter is a compressed image, bufferOffset
- // must be a multiple of the compressed texel block size in bytes (18.4). This
- // is automatically guaranteed as MipWidth and MipHeight are rounded to block size
- uploadBufferSize += (MipSize + 3) & (~3);
- ++subres;
+ UNSUPPORTED("Initializing depth-stencil texture is not currently supported");
+ // Only single aspect bit must be specified when copying texture data
}
+ aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
}
- VERIFY_EXPR(subres == pInitData->NumSubresources);
+ else
+ aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+
+ // For either clear or copy command, dst layout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
+ VkImageSubresourceRange SubresRange;
+ SubresRange.aspectMask = aspectMask;
+ SubresRange.baseArrayLayer = 0;
+ SubresRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ SubresRange.baseMipLevel = 0;
+ SubresRange.levelCount = VK_REMAINING_MIP_LEVELS;
+ auto EnabledGraphicsShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages();
+ VulkanUtilities::VulkanCommandBuffer::TransitionImageLayout(vkCmdBuff, m_VulkanImage, ImageCI.initialLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, SubresRange, EnabledGraphicsShaderStages);
+ SetState(RESOURCE_STATE_COPY_DEST);
+ const auto CurrentLayout = GetLayout();
+ VERIFY_EXPR(CurrentLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
- VkBufferCreateInfo VkStaginBuffCI = {};
- VkStaginBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
- VkStaginBuffCI.pNext = nullptr;
- VkStaginBuffCI.flags = 0;
- VkStaginBuffCI.size = uploadBufferSize;
- VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
- VkStaginBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
- VkStaginBuffCI.queueFamilyIndexCount = 0;
- VkStaginBuffCI.pQueueFamilyIndices = nullptr;
+ if(bInitializeTexture)
+ {
+ Uint32 ExpectedNumSubresources = ImageCI.mipLevels * ImageCI.arrayLayers;
+ if (pInitData->NumSubresources != ExpectedNumSubresources )
+ LOG_ERROR_AND_THROW("Incorrect number of subresources in init data. ", ExpectedNumSubresources, " expected, while ", pInitData->NumSubresources, " provided");
- std::string StagingBufferName = "Staging buffer for '";
- StagingBufferName += m_Desc.Name;
- StagingBufferName += '\'';
- VulkanUtilities::BufferWrapper StagingBuffer = LogicalDevice.CreateBuffer(VkStaginBuffCI, StagingBufferName.c_str());
+ std::vector<VkBufferImageCopy> Regions(pInitData->NumSubresources);
- VkMemoryRequirements StagingBufferMemReqs = LogicalDevice.GetBufferMemoryRequirements(StagingBuffer);
- VERIFY( IsPowerOfTwo(StagingBufferMemReqs.alignment), "Alignment is not power of 2!");
- // VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit specifies that the host cache management commands vkFlushMappedMemoryRanges
- // and vkInvalidateMappedMemoryRanges are NOT needed to flush host writes to the device or make device writes visible
- // to the host (10.2)
- auto StagingMemoryAllocation = pRenderDeviceVk->AllocateMemory(StagingBufferMemReqs, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
- auto StagingBufferMemory = StagingMemoryAllocation.Page->GetVkMemory();
- auto AlignedStagingMemOffset = Align(StagingMemoryAllocation.UnalignedOffset, StagingBufferMemReqs.alignment);
- VERIFY_EXPR(StagingMemoryAllocation.Size >= StagingBufferMemReqs.size + (AlignedStagingMemOffset - StagingMemoryAllocation.UnalignedOffset));
-
- auto *StagingData = reinterpret_cast<uint8_t*>(StagingMemoryAllocation.Page->GetCPUMemory());
- VERIFY_EXPR(StagingData != nullptr);
- StagingData += AlignedStagingMemOffset;
-
- subres = 0;
- for(Uint32 layer = 0; layer < ImageCI.arrayLayers; ++layer)
- {
- for(Uint32 mip = 0; mip < ImageCI.mipLevels; ++mip)
+ Uint64 uploadBufferSize = 0;
+ Uint32 subres = 0;
+ for(Uint32 layer = 0; layer < ImageCI.arrayLayers; ++layer)
{
- const auto &SubResData = pInitData->pSubResources[subres];
- const auto &CopyRegion = Regions[subres];
-
- auto MipWidth = CopyRegion.imageExtent.width;
- auto MipHeight = CopyRegion.imageExtent.height;
- auto MipDepth = CopyRegion.imageExtent.depth;
- Uint32 RowSize = 0;
- if(FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED)
+ for(Uint32 mip = 0; mip < ImageCI.mipLevels; ++mip)
{
- VERIFY_EXPR(FmtAttribs.BlockWidth > 1 && FmtAttribs.BlockHeight > 1);
- MipWidth = (MipWidth + FmtAttribs.BlockWidth -1) / FmtAttribs.BlockWidth;
- MipHeight = (MipHeight + FmtAttribs.BlockHeight-1) / FmtAttribs.BlockHeight;
- RowSize = MipWidth * Uint32{FmtAttribs.ComponentSize}; // ComponentSize is the block size
+ const auto& SubResData = pInitData->pSubResources[subres]; (void)SubResData;
+ auto& CopyRegion = Regions[subres];
+
+ auto MipInfo = GetMipLevelProperties(m_Desc, mip);
+
+ CopyRegion.bufferOffset = uploadBufferSize; // offset in bytes from the start of the buffer object
+ // bufferRowLength and bufferImageHeight specify the data in buffer memory as a subregion
+ // of a larger two- or three-dimensional image, and control the addressing calculations of
+ // data in buffer memory. If either of these values is zero, that aspect of the buffer memory
+ // is considered to be tightly packed according to the imageExtent. (18.4)
+ CopyRegion.bufferRowLength = 0;
+ CopyRegion.bufferImageHeight = 0;
+ // For block-compression formats, all parameters are still specified in texels rather than compressed texel blocks (18.4.1)
+ CopyRegion.imageOffset = VkOffset3D{0, 0, 0};
+ CopyRegion.imageExtent = VkExtent3D{MipInfo.Width, MipInfo.Height, MipInfo.Depth};
+
+ CopyRegion.imageSubresource.aspectMask = aspectMask;
+ CopyRegion.imageSubresource.mipLevel = mip;
+ CopyRegion.imageSubresource.baseArrayLayer = layer;
+ CopyRegion.imageSubresource.layerCount = 1;
+
+ VERIFY(SubResData.Stride == 0 || SubResData.Stride >= MipInfo.RowSize, "Stride is too small");
+ VERIFY(SubResData.DepthStride == 0 || SubResData.DepthStride >= MipInfo.RowSize * MipInfo.Height, "Depth stride is too small");
+
+ // bufferOffset must be a multiple of 4 (18.4)
+ // If the calling command's VkImage parameter is a compressed image, bufferOffset
+ // must be a multiple of the compressed texel block size in bytes (18.4). This
+ // is automatically guaranteed as MipWidth and MipHeight are rounded to block size
+ uploadBufferSize += (MipInfo.MipSize + 3) & (~3);
+ ++subres;
}
- else
+ }
+ VERIFY_EXPR(subres == pInitData->NumSubresources);
+
+ VkBufferCreateInfo VkStaginBuffCI = {};
+ VkStaginBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ VkStaginBuffCI.pNext = nullptr;
+ VkStaginBuffCI.flags = 0;
+ VkStaginBuffCI.size = uploadBufferSize;
+ VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ VkStaginBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VkStaginBuffCI.queueFamilyIndexCount = 0;
+ VkStaginBuffCI.pQueueFamilyIndices = nullptr;
+
+ std::string StagingBufferName = "Upload buffer for '";
+ StagingBufferName += m_Desc.Name;
+ StagingBufferName += '\'';
+ VulkanUtilities::BufferWrapper StagingBuffer = LogicalDevice.CreateBuffer(VkStaginBuffCI, StagingBufferName.c_str());
+
+ VkMemoryRequirements StagingBufferMemReqs = LogicalDevice.GetBufferMemoryRequirements(StagingBuffer);
+ VERIFY( IsPowerOfTwo(StagingBufferMemReqs.alignment), "Alignment is not power of 2!");
+ // VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit specifies that the host cache management commands vkFlushMappedMemoryRanges
+ // and vkInvalidateMappedMemoryRanges are NOT needed to flush host writes to the device or make device writes visible
+ // to the host (10.2)
+ auto StagingMemoryAllocation = pRenderDeviceVk->AllocateMemory(StagingBufferMemReqs, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+ auto StagingBufferMemory = StagingMemoryAllocation.Page->GetVkMemory();
+ auto AlignedStagingMemOffset = Align(StagingMemoryAllocation.UnalignedOffset, StagingBufferMemReqs.alignment);
+ VERIFY_EXPR(StagingMemoryAllocation.Size >= StagingBufferMemReqs.size + (AlignedStagingMemOffset - StagingMemoryAllocation.UnalignedOffset));
+
+ auto *StagingData = reinterpret_cast<uint8_t*>(StagingMemoryAllocation.Page->GetCPUMemory());
+ VERIFY_EXPR(StagingData != nullptr);
+ StagingData += AlignedStagingMemOffset;
+
+ subres = 0;
+ for(Uint32 layer = 0; layer < ImageCI.arrayLayers; ++layer)
+ {
+ for(Uint32 mip = 0; mip < ImageCI.mipLevels; ++mip)
{
- RowSize = MipWidth * Uint32{FmtAttribs.ComponentSize} * Uint32{FmtAttribs.NumComponents};
- }
- VERIFY(SubResData.Stride == 0 || SubResData.Stride >= RowSize, "Stride is too small");
- VERIFY(SubResData.DepthStride == 0 || SubResData.DepthStride >= RowSize * MipHeight, "Depth stride is too small");
+ const auto &SubResData = pInitData->pSubResources[subres];
+ const auto &CopyRegion = Regions[subres];
- for(Uint32 z=0; z < MipDepth; ++z)
- {
- for(Uint32 y=0; y < MipHeight; ++y)
+ auto MipInfo = GetMipLevelProperties(m_Desc, mip);
+
+ VERIFY_EXPR(MipInfo.Width == CopyRegion.imageExtent.width);
+ VERIFY_EXPR(MipInfo.Height == CopyRegion.imageExtent.height);
+ VERIFY_EXPR(MipInfo.Depth == CopyRegion.imageExtent.depth);
+
+ VERIFY(SubResData.Stride == 0 || SubResData.Stride >= MipInfo.RowSize, "Stride is too small");
+ VERIFY(SubResData.DepthStride == 0 || SubResData.DepthStride >= MipInfo.RowSize * MipInfo.Height, "Depth stride is too small");
+
+ for(Uint32 z=0; z < MipInfo.Depth; ++z)
{
- memcpy(StagingData + CopyRegion.bufferOffset + (y + z * MipHeight) * RowSize,
- reinterpret_cast<const uint8_t*>(SubResData.pData) + y * SubResData.Stride + z * SubResData.DepthStride,
- RowSize);
+ for(Uint32 y=0; y < MipInfo.Height; ++y)
+ {
+ memcpy(StagingData + CopyRegion.bufferOffset + (y + z * MipInfo.Height) * MipInfo.RowSize,
+ reinterpret_cast<const uint8_t*>(SubResData.pData) + y * SubResData.Stride + z * SubResData.DepthStride,
+ MipInfo.RowSize);
+ }
}
- }
- ++subres;
+ ++subres;
+ }
}
- }
- VERIFY_EXPR(subres == pInitData->NumSubresources);
+ VERIFY_EXPR(subres == pInitData->NumSubresources);
- err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, AlignedStagingMemOffset);
- CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory");
+ err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, AlignedStagingMemOffset);
+ CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory");
- VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledGraphicsShaderStages);
+ VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledGraphicsShaderStages);
- // Copy commands MUST be recorded outside of a render pass instance. This is OK here
- // as copy will be the only command in the cmd buffer
- vkCmdCopyBufferToImage(vkCmdBuff, StagingBuffer, m_VulkanImage,
- CurrentLayout, // dstImageLayout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL (18.4)
- static_cast<uint32_t>(Regions.size()), Regions.data());
+ // Copy commands MUST be recorded outside of a render pass instance. This is OK here
+ // as copy will be the only command in the cmd buffer
+ vkCmdCopyBufferToImage(vkCmdBuff, StagingBuffer, m_VulkanImage,
+ CurrentLayout, // dstImageLayout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL (18.4)
+ static_cast<uint32_t>(Regions.size()), Regions.data());
- Uint32 QueueIndex = 0;
- pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(QueueIndex, vkCmdBuff, std::move(CmdPool));
+ Uint32 QueueIndex = 0;
+ pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(QueueIndex, vkCmdBuff, std::move(CmdPool));
- // After command buffer is submitted, safe-release resources. This strategy
- // is little overconservative as the resources will be released after the first
- // command buffer submitted through the immediate context will be completed
- pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingBuffer), Uint64{1} << Uint64{QueueIndex});
- pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingMemoryAllocation), Uint64{1} << Uint64{QueueIndex});
- }
- else
- {
- VkImageSubresourceRange Subresource;
- Subresource.aspectMask = aspectMask;
- Subresource.baseMipLevel = 0;
- Subresource.levelCount = VK_REMAINING_MIP_LEVELS;
- Subresource.baseArrayLayer = 0;
- Subresource.layerCount = VK_REMAINING_ARRAY_LAYERS;
- if(aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
+ // After command buffer is submitted, safe-release resources. This strategy
+ // is little overconservative as the resources will be released after the first
+ // command buffer submitted through the immediate context will be completed
+ pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingBuffer), Uint64{1} << Uint64{QueueIndex});
+ pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingMemoryAllocation), Uint64{1} << Uint64{QueueIndex});
+ }
+ else
{
- if(FmtAttribs.ComponentType != COMPONENT_TYPE_COMPRESSED)
+ VkImageSubresourceRange Subresource;
+ Subresource.aspectMask = aspectMask;
+ Subresource.baseMipLevel = 0;
+ Subresource.levelCount = VK_REMAINING_MIP_LEVELS;
+ Subresource.baseArrayLayer = 0;
+ Subresource.layerCount = VK_REMAINING_ARRAY_LAYERS;
+ if(aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
{
- VkClearColorValue ClearColor = {};
- vkCmdClearColorImage(vkCmdBuff, m_VulkanImage,
+ if(FmtAttribs.ComponentType != COMPONENT_TYPE_COMPRESSED)
+ {
+ VkClearColorValue ClearColor = {};
+ vkCmdClearColorImage(vkCmdBuff, m_VulkanImage,
+ CurrentLayout, // must be VK_IMAGE_LAYOUT_GENERAL or VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
+ &ClearColor, 1, &Subresource);
+ }
+ }
+ else if(aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT ||
+ aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) )
+ {
+ VkClearDepthStencilValue ClearValue = {};
+ vkCmdClearDepthStencilImage(vkCmdBuff, m_VulkanImage,
CurrentLayout, // must be VK_IMAGE_LAYOUT_GENERAL or VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
- &ClearColor, 1, &Subresource);
+ &ClearValue, 1, &Subresource);
+ }
+ else
+ {
+ UNEXPECTED("Unexpected aspect mask");
}
+ Uint32 QueueIndex = 0;
+ pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(QueueIndex, vkCmdBuff, std::move(CmdPool));
}
- else if(aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT ||
- aspectMask == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) )
+
+
+ if(m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS)
{
- VkClearDepthStencilValue ClearValue = {};
- vkCmdClearDepthStencilImage(vkCmdBuff, m_VulkanImage,
- CurrentLayout, // must be VK_IMAGE_LAYOUT_GENERAL or VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
- &ClearValue, 1, &Subresource);
+ if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY)
+ {
+ LOG_ERROR_AND_THROW("Mipmap generation is only supported for 2D textures and texture arrays");
+ }
+
+ m_MipLevelUAV.reserve(m_Desc.MipLevels);
+ for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel)
+ {
+ // Create mip level UAV
+ TextureViewDesc UAVDesc;
+ std::stringstream name_ss;
+ name_ss << "Mip " << MipLevel << " UAV for texture '" << m_Desc.Name << "'";
+ auto name = name_ss.str();
+ UAVDesc.Name = name.c_str();
+ // Always create texture array UAV
+ UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY;
+ UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS;
+ UAVDesc.FirstArraySlice = 0;
+ UAVDesc.NumArraySlices = m_Desc.ArraySize;
+ UAVDesc.MostDetailedMip = MipLevel;
+ if (m_Desc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB)
+ UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM;
+ ITextureView* pMipUAV = nullptr;
+ CreateViewInternal( UAVDesc, &pMipUAV, true );
+ m_MipLevelUAV.emplace_back(ValidatedCast<TextureViewVkImpl>(pMipUAV), STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator>(TexViewObjAllocator));
+ }
+ VERIFY_EXPR(m_MipLevelUAV.size() == m_Desc.MipLevels);
+
+ m_MipLevelSRV.reserve(m_Desc.MipLevels);
+ for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel)
+ {
+ // Create mip level SRV
+ TextureViewDesc TexArraySRVDesc;
+ std::stringstream name_ss;
+ name_ss << "Mip " << MipLevel << " SRV for texture '" << m_Desc.Name << "'";
+ auto name = name_ss.str();
+ TexArraySRVDesc.Name = name.c_str();
+ // Alaways create texture array view
+ TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY;
+ TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE;
+ TexArraySRVDesc.FirstArraySlice = 0;
+ TexArraySRVDesc.NumArraySlices = m_Desc.ArraySize;
+ TexArraySRVDesc.MostDetailedMip = MipLevel;
+ TexArraySRVDesc.NumMipLevels = 1;
+ ITextureView* pMipLevelSRV = nullptr;
+ CreateViewInternal( TexArraySRVDesc, &pMipLevelSRV, true );
+ m_MipLevelSRV.emplace_back(ValidatedCast<TextureViewVkImpl>(pMipLevelSRV), STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator>(TexViewObjAllocator));
+ }
+ VERIFY_EXPR(m_MipLevelSRV.size() == m_Desc.MipLevels);
}
- else
+ }
+ else if(m_Desc.Usage == USAGE_STAGING)
+ {
+ VkBufferCreateInfo VkStaginBuffCI = {};
+ VkStaginBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ VkStaginBuffCI.pNext = nullptr;
+ VkStaginBuffCI.flags = 0;
+ VkStaginBuffCI.size = GetStagingDataOffset(m_Desc, m_Desc.ArraySize, 0);
+
+ DEV_CHECK_ERR( (m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) == CPU_ACCESS_READ ||
+ (m_Desc.CPUAccessFlags & (CPU_ACCESS_READ | CPU_ACCESS_WRITE)) == CPU_ACCESS_WRITE,
+ "Exactly one of CPU_ACCESS_READ or CPU_ACCESS_WRITE flags must be specified");
+ VkMemoryPropertyFlags MemProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ if (m_Desc.CPUAccessFlags & CPU_ACCESS_READ)
+ {
+ VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ MemProperties |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+ SetState(RESOURCE_STATE_COPY_DEST);
+ }
+ else if (m_Desc.CPUAccessFlags & CPU_ACCESS_WRITE)
{
- UNEXPECTED("Unexpected aspect mask");
+ VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+ // VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit specifies that the host cache management commands vkFlushMappedMemoryRanges
+ // and vkInvalidateMappedMemoryRanges are NOT needed to flush host writes to the device or make device writes visible
+ // to the host (10.2)
+ MemProperties |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ SetState(RESOURCE_STATE_COPY_SOURCE);
}
- Uint32 QueueIndex = 0;
- pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(QueueIndex, vkCmdBuff, std::move(CmdPool));
+ else
+ UNEXPECTED("Unexpected CPU access");
+
+ VkStaginBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VkStaginBuffCI.queueFamilyIndexCount = 0;
+ VkStaginBuffCI.pQueueFamilyIndices = nullptr;
+
+ std::string StagingBufferName = "Staging buffer for '";
+ StagingBufferName += m_Desc.Name;
+ StagingBufferName += '\'';
+ m_StagingBuffer = LogicalDevice.CreateBuffer(VkStaginBuffCI, StagingBufferName.c_str());
+
+ VkMemoryRequirements StagingBufferMemReqs = LogicalDevice.GetBufferMemoryRequirements(m_StagingBuffer);
+ VERIFY( IsPowerOfTwo(StagingBufferMemReqs.alignment), "Alignment is not power of 2!");
+
+ m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(StagingBufferMemReqs, MemProperties);
+ auto StagingBufferMemory = m_MemoryAllocation.Page->GetVkMemory();
+ auto AlignedStagingMemOffset = Align(m_MemoryAllocation.UnalignedOffset, StagingBufferMemReqs.alignment);
+ VERIFY_EXPR(m_MemoryAllocation.Size >= StagingBufferMemReqs.size + (AlignedStagingMemOffset - m_MemoryAllocation.UnalignedOffset));
+
+ auto err = LogicalDevice.BindBufferMemory(m_StagingBuffer, StagingBufferMemory, AlignedStagingMemOffset);
+ CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory");
+
+ m_StagingDataAlignedOffset = AlignedStagingMemOffset;
+ }
+ else
+ {
+ UNSUPPORTED("Unsupported usage ", GetUsageString(m_Desc.Usage));
}
+ VERIFY_EXPR(IsInKnownState());
+}
- if(m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS)
+Uint32 GetStagingDataOffset(const TextureDesc& TexDesc, Uint32 ArraySlice, Uint32 MipLevel)
+{
+ VERIFY_EXPR(ArraySlice < TexDesc.ArraySize && MipLevel < TexDesc.MipLevels || ArraySlice == TexDesc.ArraySize && MipLevel == 0);
+
+ Uint32 Offset = 0;
+ if (ArraySlice > 0)
{
- if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY)
+ Uint32 ArraySliceSize = 0;
+ for (Uint32 mip = 0; mip < TexDesc.MipLevels; ++mip)
{
- LOG_ERROR_AND_THROW("Mipmap generation is only supported for 2D textures and texture arrays");
+ auto MipInfo = GetMipLevelProperties(TexDesc, mip);
+ // bufferOffset must be a multiple of 4 (18.4)
+ // If the calling command's VkImage parameter is a compressed image, bufferOffset
+ // must be a multiple of the compressed texel block size in bytes (18.4). This
+ // is automatically guaranteed as MipWidth and MipHeight are rounded to block size
+ ArraySliceSize += (MipInfo.MipSize + 3) & (~3);
}
- m_MipLevelUAV.reserve(m_Desc.MipLevels);
- for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel)
- {
- // Create mip level UAV
- TextureViewDesc UAVDesc;
- std::stringstream name_ss;
- name_ss << "Mip " << MipLevel << " UAV for texture '" << m_Desc.Name << "'";
- auto name = name_ss.str();
- UAVDesc.Name = name.c_str();
- // Always create texture array UAV
- UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY;
- UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS;
- UAVDesc.FirstArraySlice = 0;
- UAVDesc.NumArraySlices = m_Desc.ArraySize;
- UAVDesc.MostDetailedMip = MipLevel;
- if (m_Desc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB)
- UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM;
- ITextureView* pMipUAV = nullptr;
- CreateViewInternal( UAVDesc, &pMipUAV, true );
- m_MipLevelUAV.emplace_back(ValidatedCast<TextureViewVkImpl>(pMipUAV), STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator>(TexViewObjAllocator));
- }
- VERIFY_EXPR(m_MipLevelUAV.size() == m_Desc.MipLevels);
+ Offset = ArraySliceSize;
+ if (TexDesc.Type == RESOURCE_DIM_TEX_1D_ARRAY ||
+ TexDesc.Type == RESOURCE_DIM_TEX_2D_ARRAY ||
+ TexDesc.Type == RESOURCE_DIM_TEX_CUBE ||
+ TexDesc.Type == RESOURCE_DIM_TEX_CUBE_ARRAY)
+ Offset *= TexDesc.ArraySize;
+ }
- m_MipLevelSRV.reserve(m_Desc.MipLevels);
- for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel)
- {
- // Create mip level SRV
- TextureViewDesc TexArraySRVDesc;
- std::stringstream name_ss;
- name_ss << "Mip " << MipLevel << " SRV for texture '" << m_Desc.Name << "'";
- auto name = name_ss.str();
- TexArraySRVDesc.Name = name.c_str();
- // Alaways create texture array view
- TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY;
- TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE;
- TexArraySRVDesc.FirstArraySlice = 0;
- TexArraySRVDesc.NumArraySlices = m_Desc.ArraySize;
- TexArraySRVDesc.MostDetailedMip = MipLevel;
- TexArraySRVDesc.NumMipLevels = 1;
- ITextureView* pMipLevelSRV = nullptr;
- CreateViewInternal( TexArraySRVDesc, &pMipLevelSRV, true );
- m_MipLevelSRV.emplace_back(ValidatedCast<TextureViewVkImpl>(pMipLevelSRV), STDDeleter<TextureViewVkImpl, FixedBlockMemoryAllocator>(TexViewObjAllocator));
- }
- VERIFY_EXPR(m_MipLevelSRV.size() == m_Desc.MipLevels);
+ for (Uint32 mip = 0; mip < MipLevel; ++mip)
+ {
+ auto MipInfo = GetMipLevelProperties(TexDesc, mip);
+ // bufferOffset must be a multiple of 4 (18.4)
+ Offset += (MipInfo.MipSize + 3) & (~3);
}
- VERIFY_EXPR(IsInKnownState());
+ return Offset;
}
TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters,
@@ -494,7 +587,10 @@ TextureVkImpl :: ~TextureVkImpl()
{
// Vk object can only be destroyed when it is no longer used by the GPU
// Wrappers for external texture will not be destroyed as they are created with null device pointer
- m_pDevice->SafeReleaseDeviceObject(std::move(m_VulkanImage), m_Desc.CommandQueueMask);
+ if (m_VulkanImage)
+ m_pDevice->SafeReleaseDeviceObject(std::move(m_VulkanImage), m_Desc.CommandQueueMask);
+ if (m_StagingBuffer)
+ m_pDevice->SafeReleaseDeviceObject(std::move(m_StagingBuffer), m_Desc.CommandQueueMask);
m_pDevice->SafeReleaseDeviceObject(std::move(m_MemoryAllocation), m_Desc.CommandQueueMask);
}
@@ -650,4 +746,17 @@ VkImageLayout TextureVkImpl::GetLayout()const
return ResourceStateToVkImageLayout(GetState());
}
+void TextureVkImpl::InvalidateStagingRange(VkDeviceSize Offset, VkDeviceSize Size)
+{
+ const auto& LogicalDevice = m_pDevice->GetLogicalDevice();
+ VkMappedMemoryRange InvalidateRange = {};
+ InvalidateRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ InvalidateRange.pNext = nullptr;
+ InvalidateRange.memory = m_MemoryAllocation.Page->GetVkMemory();
+ InvalidateRange.offset = m_StagingDataAlignedOffset + Offset;
+ InvalidateRange.size = Size;
+ auto err = LogicalDevice.InvalidateMappedMemoryRanges(1, &InvalidateRange);
+ DEV_CHECK_ERR(err == VK_SUCCESS, "Failed to invalidated mapped texture memory range");
+}
+
}
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp
index 01155f6f..fbdb37a8 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanLogicalDevice.cpp
@@ -415,6 +415,16 @@ namespace VulkanUtilities
vkUnmapMemory(m_VkDevice, memory);
}
+ VkResult VulkanLogicalDevice::InvalidateMappedMemoryRanges(uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)const
+ {
+ return vkInvalidateMappedMemoryRanges(m_VkDevice, memoryRangeCount, pMemoryRanges);
+ }
+
+ VkResult VulkanLogicalDevice::FlushMappedMemoryRanges(uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)const
+ {
+ return vkFlushMappedMemoryRanges(m_VkDevice, memoryRangeCount, pMemoryRanges);
+ }
+
VkResult VulkanLogicalDevice::GetFenceStatus(VkFence fence)const
{
return vkGetFenceStatus(m_VkDevice, fence);