11/*
2- * Copyright 2019-2024 Diligent Graphics LLC
2+ * Copyright 2019-2025 Diligent Graphics LLC
33 * Copyright 2015-2019 Egor Yusov
44 *
55 * Licensed under the Apache License, Version 2.0 (the "License");
3838namespace Diligent
3939{
4040
41+ namespace
42+ {
43+
4144VkImageCreateInfo TextureDescToVkImageCreateInfo (const TextureDesc& Desc, const RenderDeviceVkImpl* pRenderDeviceVk) noexcept
4245{
4346 const bool IsMemoryless = (Desc.MiscFlags & MISC_TEXTURE_FLAG_MEMORYLESS) != 0 ;
@@ -143,6 +146,60 @@ VkImageCreateInfo TextureDescToVkImageCreateInfo(const TextureDesc& Desc, const
143146 return ImageCI;
144147}
145148
149+ VkImageLayout VkImageLayoutFromUsage (VkImageUsageFlags Usage)
150+ {
151+ if ((Usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) != 0 )
152+ return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
153+
154+ if ((Usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT) != 0 )
155+ return VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT;
156+
157+ if ((Usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR) != 0 )
158+ return VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR;
159+
160+ if ((Usage & VK_IMAGE_USAGE_STORAGE_BIT) != 0 )
161+ return VK_IMAGE_LAYOUT_GENERAL;
162+
163+ if ((Usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0 )
164+ return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
165+
166+ if ((Usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0 )
167+ return VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL_KHR;
168+
169+ VERIFY ((Usage & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) != 0 , " TRANSFER_SRC_BIT should always be set" );
170+ return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
171+ }
172+
173+ bool CheckHostImageCopySupport (const VulkanUtilities::VulkanLogicalDevice& LogicalDevice,
174+ const VulkanUtilities::VulkanPhysicalDevice& PhysicalDevice,
175+ const VkImageCreateInfo& ImageCI)
176+ {
177+ if (!LogicalDevice.GetEnabledExtFeatures ().HostImageCopy .hostImageCopy )
178+ return false ;
179+
180+ VkFormatProperties3 vkFormatProps3{};
181+ VkFormatProperties VkFormatProps = PhysicalDevice.GetPhysicalDeviceFormatProperties (ImageCI.format , &vkFormatProps3);
182+ (void )VkFormatProps;
183+
184+ if ((vkFormatProps3.optimalTilingFeatures & VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT) == 0 )
185+ return false ;
186+
187+ const VulkanUtilities::VulkanPhysicalDevice::ExtensionProperties& ExtProps = PhysicalDevice.GetExtProperties ();
188+ const VkPhysicalDeviceHostImageCopyPropertiesEXT& HostImageCopyProps = ExtProps.HostImageCopy ;
189+
190+ const VkImageLayout DstLayout = VkImageLayoutFromUsage (ImageCI.usage );
191+
192+ auto LayoutSupported = [](const VkImageLayout* pLayouts, uint32_t LayoutCount, VkImageLayout Layout) {
193+ return std::find (pLayouts, pLayouts + LayoutCount, Layout) != pLayouts + LayoutCount;
194+ };
195+
196+ if (!LayoutSupported (HostImageCopyProps.pCopyDstLayouts , HostImageCopyProps.copyDstLayoutCount , DstLayout))
197+ return false ;
198+
199+ return true ;
200+ }
201+
202+ } // namespace
146203
147204TextureVkImpl::TextureVkImpl (IReferenceCounters* pRefCounters,
148205 FixedBlockMemoryAllocator& TexViewObjAllocator,
@@ -179,6 +236,11 @@ TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters,
179236
180237 VkImageCreateInfo ImageCI = TextureDescToVkImageCreateInfo (m_Desc, pRenderDeviceVk);
181238
239+ const bool InitContent = pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0 ;
240+ const bool UseHostImageCopy = InitContent && CheckHostImageCopySupport (LogicalDevice, pRenderDeviceVk->GetPhysicalDevice (), ImageCI);
241+ if (UseHostImageCopy)
242+ ImageCI.usage |= VK_IMAGE_USAGE_HOST_TRANSFER_BIT;
243+
182244 const std::vector<uint32_t > QueueFamilyIndices = PlatformMisc::CountOneBits (m_Desc.ImmediateContextMask ) > 1 ?
183245 GetDevice ()->ConvertCmdQueueIdsToQueueFamilies (m_Desc.ImmediateContextMask ) :
184246 std::vector<uint32_t >{};
@@ -223,10 +285,23 @@ TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters,
223285 VkResult err = LogicalDevice.BindImageMemory (m_VulkanImage, Memory, AlignedOffset);
224286 CHECK_VK_ERROR_AND_THROW (err, " Failed to bind image memory" );
225287
226- if (pInitData != nullptr && pInitData->pSubResources != nullptr && pInitData->NumSubresources > 0 )
227- InitializeTextureContent (*pInitData, FmtAttribs, ImageCI);
288+ if (InitContent)
289+ {
290+ bool InitializedOnHost = false ;
291+ if (UseHostImageCopy)
292+ {
293+ InitializedOnHost = InitializeContentOnHost (*pInitData, FmtAttribs, ImageCI);
294+ }
295+
296+ if (!InitializedOnHost)
297+ {
298+ InitializeContentOnDevice (*pInitData, FmtAttribs, ImageCI);
299+ }
300+ }
228301 else
302+ {
229303 SetState (RESOURCE_STATE_UNDEFINED);
304+ }
230305 }
231306 }
232307 else if (m_Desc.Usage == USAGE_STAGING)
@@ -241,9 +316,100 @@ TextureVkImpl::TextureVkImpl(IReferenceCounters* pRefCounters,
241316 VERIFY_EXPR (IsInKnownState ());
242317}
243318
244- void TextureVkImpl::InitializeTextureContent (const TextureData& InitData,
245- const TextureFormatAttribs& FmtAttribs,
246- const VkImageCreateInfo& ImageCI) noexcept (false )
319+ bool TextureVkImpl::InitializeContentOnHost (const TextureData& InitData,
320+ const TextureFormatAttribs& FmtAttribs,
321+ const VkImageCreateInfo& ImageCI) noexcept (false )
322+ {
323+ const VulkanUtilities::VulkanLogicalDevice& LogicalDevice = GetDevice ()->GetLogicalDevice ();
324+ VERIFY_EXPR (LogicalDevice.GetEnabledExtFeatures ().HostImageCopy .hostImageCopy );
325+
326+ Uint32 ExpectedNumSubresources = ImageCI.mipLevels * ImageCI.arrayLayers ;
327+ if (InitData.NumSubresources != ExpectedNumSubresources)
328+ LOG_ERROR_AND_THROW (" Incorrect number of subresources in init data. " , ExpectedNumSubresources, " expected, while " , InitData.NumSubresources , " provided" );
329+
330+ VERIFY (FmtAttribs.ComponentType != COMPONENT_TYPE_DEPTH_STENCIL, " Initializing depth-stencil texture is currently not supported." );
331+ const VkImageAspectFlags aspectMask = ComponentTypeToVkAspectMask (FmtAttribs.ComponentType );
332+
333+ std::vector<VkMemoryToImageCopyEXT> vkCopyRegions (InitData.NumSubresources );
334+
335+ Uint32 subres = 0 ;
336+ for (Uint32 layer = 0 ; layer < ImageCI.arrayLayers ; ++layer)
337+ {
338+ for (Uint32 mip = 0 ; mip < ImageCI.mipLevels ; ++mip)
339+ {
340+ const TextureSubResData& SubResData = InitData.pSubResources [subres];
341+ VkMemoryToImageCopyEXT& vkCopyInfo = vkCopyRegions[subres];
342+ MipLevelProperties MipInfo = GetMipLevelProperties (m_Desc, mip);
343+
344+ vkCopyInfo.sType = VK_STRUCTURE_TYPE_MEMORY_TO_IMAGE_COPY_EXT;
345+ vkCopyInfo.pNext = nullptr ;
346+ vkCopyInfo.pHostPointer = SubResData.pData ;
347+
348+ const Uint32 PixelSize = FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED ?
349+ Uint32{FmtAttribs.ComponentSize } :
350+ Uint32{FmtAttribs.ComponentSize } * Uint32{FmtAttribs.NumComponents };
351+ if ((SubResData.Stride % PixelSize) != 0 )
352+ {
353+ LOG_DVP_WARNING_MESSAGE (" Unable to initialize texture '" , m_Desc.Name , " ' on host: subresource " , subres, " has stride " , SubResData.Stride ,
354+ " that is not multiple of pixel size " , PixelSize, " . The content will be initialized on device." );
355+ return false ;
356+ }
357+ vkCopyInfo.memoryRowLength = FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED ?
358+ static_cast <uint32_t >(SubResData.Stride * FmtAttribs.BlockWidth / FmtAttribs.ComponentSize ) :
359+ static_cast <uint32_t >(SubResData.Stride / PixelSize);
360+
361+ if ((SubResData.DepthStride % SubResData.Stride ) != 0 )
362+ {
363+ LOG_DVP_WARNING_MESSAGE (" Unable to initialize texture '" , m_Desc.Name , " ' on host: subresource " , subres, " has depth stride " , SubResData.DepthStride ,
364+ " that is not multiple of row stride " , SubResData.Stride , " . The content will be initialized on device." );
365+ return false ;
366+ }
367+ vkCopyInfo.memoryImageHeight = static_cast <uint32_t >(SubResData.DepthStride * FmtAttribs.BlockHeight / SubResData.Stride );
368+
369+ vkCopyInfo.imageSubresource .aspectMask = aspectMask;
370+ vkCopyInfo.imageSubresource .mipLevel = mip;
371+ vkCopyInfo.imageSubresource .baseArrayLayer = layer;
372+ vkCopyInfo.imageSubresource .layerCount = 1 ;
373+
374+ vkCopyInfo.imageOffset = {0 , 0 , 0 };
375+ vkCopyInfo.imageExtent = {MipInfo.LogicalWidth , MipInfo.LogicalHeight , MipInfo.Depth };
376+
377+ ++subres;
378+ }
379+ }
380+
381+ VkHostImageLayoutTransitionInfoEXT vkLayoutTransitionInfo{};
382+ vkLayoutTransitionInfo.sType = VK_STRUCTURE_TYPE_HOST_IMAGE_LAYOUT_TRANSITION_INFO_EXT;
383+ vkLayoutTransitionInfo.image = m_VulkanImage;
384+ vkLayoutTransitionInfo.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
385+ vkLayoutTransitionInfo.newLayout = VkImageLayoutFromUsage (ImageCI.usage );
386+
387+ vkLayoutTransitionInfo.subresourceRange .aspectMask = aspectMask;
388+ vkLayoutTransitionInfo.subresourceRange .baseArrayLayer = 0 ;
389+ vkLayoutTransitionInfo.subresourceRange .layerCount = VK_REMAINING_ARRAY_LAYERS;
390+ vkLayoutTransitionInfo.subresourceRange .baseMipLevel = 0 ;
391+ vkLayoutTransitionInfo.subresourceRange .levelCount = VK_REMAINING_MIP_LEVELS;
392+ LogicalDevice.HostTransitionImageLayout (vkLayoutTransitionInfo);
393+
394+ VkCopyMemoryToImageInfoEXT vkCopyInfo{};
395+ vkCopyInfo.sType = VK_STRUCTURE_TYPE_COPY_MEMORY_TO_IMAGE_INFO_EXT;
396+ vkCopyInfo.pNext = nullptr ;
397+ vkCopyInfo.flags = 0 ;
398+ vkCopyInfo.dstImage = m_VulkanImage;
399+ vkCopyInfo.dstImageLayout = vkLayoutTransitionInfo.newLayout ;
400+ vkCopyInfo.regionCount = static_cast <uint32_t >(vkCopyRegions.size ());
401+ vkCopyInfo.pRegions = vkCopyRegions.data ();
402+
403+ LogicalDevice.CopyMemoryToImage (vkCopyInfo);
404+
405+ SetState (VkImageLayoutToResourceState (vkCopyInfo.dstImageLayout ));
406+
407+ return true ;
408+ }
409+
410+ void TextureVkImpl::InitializeContentOnDevice (const TextureData& InitData,
411+ const TextureFormatAttribs& FmtAttribs,
412+ const VkImageCreateInfo& ImageCI) noexcept (false )
247413{
248414 const VulkanUtilities::VulkanLogicalDevice& LogicalDevice = GetDevice ()->GetLogicalDevice ();
249415
@@ -258,18 +424,8 @@ void TextureVkImpl::InitializeTextureContent(const TextureData& InitDat
258424 VulkanUtilities::VulkanCommandBuffer CmdBuffer;
259425 GetDevice ()->AllocateTransientCmdPool (CmdQueueInd, CmdPool, CmdBuffer, " Transient command pool to copy staging data to a device buffer" );
260426
261- VkImageAspectFlags aspectMask = 0 ;
262- if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH)
263- aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
264- else if (FmtAttribs.ComponentType == COMPONENT_TYPE_DEPTH_STENCIL)
265- {
266- UNSUPPORTED (" Initializing depth-stencil texture is not currently supported" );
267- // Only single aspect bit must be specified when copying texture data
268-
269- aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
270- }
271- else
272- aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
427+ VERIFY (FmtAttribs.ComponentType != COMPONENT_TYPE_DEPTH_STENCIL, " Initializing depth-stencil texture is currently not supported." );
428+ const VkImageAspectFlags aspectMask = ComponentTypeToVkAspectMask (FmtAttribs.ComponentType );
273429
274430 // For either clear or copy command, dst layout must be VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
275431 VkImageSubresourceRange SubresRange;
0 commit comments