diff options
| author | assiduous <assiduous@diligentgraphics.com> | 2020-08-23 03:26:49 +0000 |
|---|---|---|
| committer | assiduous <assiduous@diligentgraphics.com> | 2020-08-23 03:26:49 +0000 |
| commit | 2771c71fad9cc4311c287ad119d078662ef52c29 (patch) | |
| tree | a0cf76cd433d6cba40b8a1478ed9d415a2517481 /Graphics/GraphicsEngineVulkan | |
| parent | D3D12 backend: improved command list version detection (diff) | |
| download | DiligentCore-2771c71fad9cc4311c287ad119d078662ef52c29.tar.gz DiligentCore-2771c71fad9cc4311c287ad119d078662ef52c29.zip | |
Added USAGE_UNIFIED usage type (API 240066)
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
4 files changed, 178 insertions, 102 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp index 38bd4893..1adf71ee 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.hpp @@ -163,6 +163,13 @@ public: { return m_MemoryMgr.Allocate(MemReqs, MemoryProperties); } + VulkanUtilities::VulkanMemoryAllocation AllocateMemory(VkDeviceSize Size, VkDeviceSize Alignment, uint32_t MemoryTypeIndex) + { + const auto& MemoryProps = m_PhysicalDevice->GetMemoryProperties(); + VERIFY_EXPR(MemoryTypeIndex < MemoryProps.memoryTypeCount); + const auto MemoryFlags = MemoryProps.memoryTypes[MemoryTypeIndex].propertyFlags; + return m_MemoryMgr.Allocate(Size, Alignment, MemoryTypeIndex, (MemoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0); + } VulkanUtilities::VulkanMemoryManager& GetGlobalMemoryManager() { return m_MemoryMgr; } VulkanDynamicMemoryManager& GetDynamicMemoryManager() { return m_DynamicMemoryManager; } diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp index 24212950..c54541c6 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanPhysicalDevice.hpp @@ -53,13 +53,14 @@ public: bool CheckPresentSupport (uint32_t queueFamilyIndex, VkSurfaceKHR VkSurface) const; // clang-format on - static constexpr uint32_t InvalidMemoryTypeIndex = static_cast<uint32_t>(-1); + static constexpr uint32_t InvalidMemoryTypeIndex = ~uint32_t{0}; uint32_t GetMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties) const; - const VkPhysicalDeviceProperties& GetProperties() const { return m_Properties; } - const VkPhysicalDeviceFeatures& GetFeatures() const { return m_Features; } - VkFormatProperties GetPhysicalDeviceFormatProperties(VkFormat imageFormat) const; + const VkPhysicalDeviceProperties& GetProperties() const { return m_Properties; } + const VkPhysicalDeviceFeatures& GetFeatures() const { return m_Features; } + const VkPhysicalDeviceMemoryProperties& GetMemoryProperties() const { return m_MemoryProperties; } + VkFormatProperties GetPhysicalDeviceFormatProperties(VkFormat imageFormat) const; private: VulkanPhysicalDevice(VkPhysicalDevice vkDevice); diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp index 1e64e97b..9ff268dd 100644 --- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp @@ -57,29 +57,26 @@ BufferVkImpl::BufferVkImpl(IReferenceCounters* pRefCounters, m_DynamicAllocations(STD_ALLOCATOR_RAW_MEM(VulkanDynamicAllocation, GetRawAllocator(), "Allocator for vector<VulkanDynamicAllocation>")) // clang-format on { -#define LOG_BUFFER_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Buffer \"", BuffDesc.Name ? BuffDesc.Name : "", "\": ", ##__VA_ARGS__) + ValidateBufferInitData(BuffDesc, pBuffData); - if (m_Desc.Usage == USAGE_STATIC && (pBuffData == nullptr || pBuffData->pData == nullptr)) - LOG_BUFFER_ERROR_AND_THROW("Static buffer must be initialized with data at creation time"); - - if (m_Desc.Usage == USAGE_DYNAMIC && pBuffData != nullptr && pBuffData->pData != nullptr) - LOG_BUFFER_ERROR_AND_THROW("Dynamic buffer must be initialized via Map()"); + if (m_Desc.Usage == USAGE_STATIC) + VERIFY(pBuffData != nullptr && pBuffData->pData != nullptr, "Initial data must not be null for static buffers"); + if (m_Desc.Usage == USAGE_DYNAMIC) + VERIFY(pBuffData == nullptr || pBuffData->pData == nullptr, "Initial data must be null for dynamic buffers"); if (m_Desc.Usage == USAGE_STAGING) { - if (m_Desc.CPUAccessFlags != CPU_ACCESS_WRITE && m_Desc.CPUAccessFlags != CPU_ACCESS_READ) - LOG_BUFFER_ERROR_AND_THROW("Exactly one of the CPU_ACCESS_WRITE or CPU_ACCESS_READ flags must be specified for a cpu-accessible buffer"); + VERIFY(m_Desc.CPUAccessFlags == CPU_ACCESS_WRITE || m_Desc.CPUAccessFlags == CPU_ACCESS_READ, + "Exactly one of the CPU_ACCESS_WRITE or CPU_ACCESS_READ flags must be specified for a staging buffer"); if (m_Desc.CPUAccessFlags == CPU_ACCESS_WRITE) - { - if (pBuffData != nullptr && pBuffData->pData != nullptr) - LOG_BUFFER_ERROR_AND_THROW("CPU-writable staging buffers must be updated via map"); - } + VERIFY(pBuffData == nullptr || pBuffData->pData == nullptr, "CPU-writable staging buffers must be updated via map"); } - const auto& LogicalDevice = pRenderDeviceVk->GetLogicalDevice(); - const auto& DeviceLimits = pRenderDeviceVk->GetPhysicalDevice().GetProperties().limits; - m_DynamicOffsetAlignment = std::max(Uint32{4}, static_cast<Uint32>(DeviceLimits.optimalBufferCopyOffsetAlignment)); + const auto& LogicalDevice = pRenderDeviceVk->GetLogicalDevice(); + const auto& PhysicalDevice = pRenderDeviceVk->GetPhysicalDevice(); + const auto& DeviceLimits = PhysicalDevice.GetProperties().limits; + m_DynamicOffsetAlignment = std::max(Uint32{4}, static_cast<Uint32>(DeviceLimits.optimalBufferCopyOffsetAlignment)); VkBufferCreateInfo VkBuffCI = {}; VkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -184,14 +181,56 @@ BufferVkImpl::BufferVkImpl(IReferenceCounters* pRefCounters, VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VulkanBuffer); - VkMemoryPropertyFlags BufferMemoryFlags = 0; - if (m_Desc.Usage == USAGE_STAGING) - BufferMemoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - else - BufferMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + uint32_t MemoryTypeIndex = VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex; + if (m_Desc.Usage == USAGE_UNIFIED) + { + // Attempt to find device-local and CPU-visible memory type + // clang-format off + MemoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex( + MemReqs.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + (((m_Desc.CPUAccessFlags & CPU_ACCESS_READ) != 0) ? VK_MEMORY_PROPERTY_HOST_CACHED_BIT : 0) | + (((m_Desc.CPUAccessFlags & CPU_ACCESS_WRITE) != 0) ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT : 0) + ); + // clang-format on + if (MemoryTypeIndex == VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex) + { + DecayUnifiedBuffer(); + } + } + + if (MemoryTypeIndex == VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex) + { + VkMemoryPropertyFlags vkMemoryFlags = 0; + switch (m_Desc.Usage) + { + case USAGE_STATIC: + case USAGE_DEFAULT: + case USAGE_DYNAMIC: // Dynamic buffer with SRV or UAV bind flag + vkMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + + case USAGE_STAGING: + // clang-format off + vkMemoryFlags = + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + (((m_Desc.CPUAccessFlags & CPU_ACCESS_READ) != 0) ? VK_MEMORY_PROPERTY_HOST_CACHED_BIT : 0) | + (((m_Desc.CPUAccessFlags & CPU_ACCESS_WRITE) != 0) ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT : 0); + // clang-format on + break; + + default: + UNEXPECTED("Unexpected usage"); + } + MemoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, vkMemoryFlags); + } + + if (MemoryTypeIndex == VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex) + LOG_ERROR_AND_THROW("Failed to find suitable memory type for buffer '", m_Desc.Name, '\''); VERIFY(IsPowerOfTwo(MemReqs.alignment), "Alignment is not power of 2!"); - m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs, BufferMemoryFlags); + m_MemoryAllocation = pRenderDeviceVk->AllocateMemory(MemReqs.size, MemReqs.alignment, MemoryTypeIndex); m_BufferMemoryAlignedOffset = Align(VkDeviceSize{m_MemoryAllocation.UnalignedOffset}, MemReqs.alignment); VERIFY(m_MemoryAllocation.Size >= MemReqs.size + (m_BufferMemoryAlignedOffset - m_MemoryAllocation.UnalignedOffset), "Size of memory allocation is too small"); @@ -203,82 +242,111 @@ BufferVkImpl::BufferVkImpl(IReferenceCounters* pRefCounters, RESOURCE_STATE InitialState = RESOURCE_STATE_UNDEFINED; if (bInitializeBuffer) { - VkBufferCreateInfo VkStaginBuffCI = VkBuffCI; - VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - - 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(VkDeviceSize{StagingMemoryAllocation.UnalignedOffset}, StagingBufferMemReqs.alignment); - VERIFY_EXPR(StagingMemoryAllocation.Size >= StagingBufferMemReqs.size + (AlignedStagingMemOffset - StagingMemoryAllocation.UnalignedOffset)); - - auto* StagingData = reinterpret_cast<uint8_t*>(StagingMemoryAllocation.Page->GetCPUMemory()); - if (StagingData == nullptr) - LOG_BUFFER_ERROR_AND_THROW("Failed to allocate staging data"); - memcpy(StagingData + AlignedStagingMemOffset, pBuffData->pData, pBuffData->DataSize); - - err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, AlignedStagingMemOffset); - CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory"); - - VulkanUtilities::CommandPoolWrapper CmdPool; - VkCommandBuffer vkCmdBuff; - pRenderDeviceVk->AllocateTransientCmdPool(CmdPool, vkCmdBuff, "Transient command pool to copy staging data to a device buffer"); - - auto EnabledGraphicsShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages(); - VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledGraphicsShaderStages); - InitialState = RESOURCE_STATE_COPY_DEST; - VkAccessFlags AccessFlags = ResourceStateFlagsToVkAccessFlags(InitialState); - VERIFY_EXPR(AccessFlags == VK_ACCESS_TRANSFER_WRITE_BIT); - VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, m_VulkanBuffer, 0, AccessFlags, 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 - VkBufferCopy BuffCopy = {}; - BuffCopy.srcOffset = 0; - BuffCopy.dstOffset = 0; - BuffCopy.size = VkBuffCI.size; - vkCmdCopyBuffer(vkCmdBuff, StagingBuffer, m_VulkanBuffer, 1, &BuffCopy); - - Uint32 QueueIndex = 0; - pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(QueueIndex, vkCmdBuff, std::move(CmdPool)); - - - // After command buffer is submitted, safe-release staging resources. This strategy - // is little overconservative as the resources will only be released after the - // first command buffer submitted through the immediate context is complete - - // Next Cmd Buff| Next Fence | This Thread | Immediate Context - // | | | - // N | F | | - // | | | - // | | ExecuteAndDisposeTransientCmdBuff(vkCmdBuff) | - // | | - SubmittedCmdBuffNumber = N | - // | | - SubmittedFenceValue = F | - // N+1 - - | - F+1 - | | - // | | Release(StagingBuffer) | - // | | - {N+1, StagingBuffer} -> Stale Objects | - // | | | - // | | | - // | | | ExecuteCommandBuffer() - // | | | - SubmittedCmdBuffNumber = N+1 - // | | | - SubmittedFenceValue = F+1 - // N+2 - - | - F+2 - | - - - - - - - - - - - - | - // | | | - DiscardStaleVkObjects(N+1, F+1) - // | | | - {F+1, StagingBuffer} -> Release Queue - // | | | - - pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingBuffer), Uint64{1} << Uint64{QueueIndex}); - pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingMemoryAllocation), Uint64{1} << Uint64{QueueIndex}); + const auto& MemoryProps = PhysicalDevice.GetMemoryProperties(); + VERIFY_EXPR(MemoryTypeIndex < MemoryProps.memoryTypeCount); + const auto MemoryPropFlags = MemoryProps.memoryTypes[MemoryTypeIndex].propertyFlags; + if ((MemoryPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + // Memory is directly accessible by CPU + auto AlignedMemOffset = Align(VkDeviceSize{m_MemoryAllocation.UnalignedOffset}, MemReqs.alignment); + VERIFY_EXPR(m_MemoryAllocation.Size >= MemReqs.size + (AlignedMemOffset - m_MemoryAllocation.UnalignedOffset)); + + auto* pData = reinterpret_cast<uint8_t*>(m_MemoryAllocation.Page->GetCPUMemory()); + VERIFY_EXPR(pData != nullptr); + memcpy(pData + AlignedMemOffset, pBuffData->pData, pBuffData->DataSize); + + if ((MemoryPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) + { + // Explicit flush is required + VkMappedMemoryRange FlushRange = {}; + + FlushRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + FlushRange.pNext = nullptr; + FlushRange.memory = m_MemoryAllocation.Page->GetVkMemory(); + FlushRange.offset = AlignedMemOffset; + FlushRange.size = MemReqs.size; + LogicalDevice.FlushMappedMemoryRanges(1, &FlushRange); + } + } + else + { + VkBufferCreateInfo VkStaginBuffCI = VkBuffCI; + VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + 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(VkDeviceSize{StagingMemoryAllocation.UnalignedOffset}, StagingBufferMemReqs.alignment); + VERIFY_EXPR(StagingMemoryAllocation.Size >= StagingBufferMemReqs.size + (AlignedStagingMemOffset - StagingMemoryAllocation.UnalignedOffset)); + + auto* StagingData = reinterpret_cast<uint8_t*>(StagingMemoryAllocation.Page->GetCPUMemory()); + if (StagingData == nullptr) + LOG_ERROR_AND_THROW("Failed to allocate staging data for buffer '", m_Desc.Name, '\''); + memcpy(StagingData + AlignedStagingMemOffset, pBuffData->pData, pBuffData->DataSize); + + err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, AlignedStagingMemOffset); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory"); + + VulkanUtilities::CommandPoolWrapper CmdPool; + VkCommandBuffer vkCmdBuff; + pRenderDeviceVk->AllocateTransientCmdPool(CmdPool, vkCmdBuff, "Transient command pool to copy staging data to a device buffer"); + + auto EnabledGraphicsShaderStages = LogicalDevice.GetEnabledGraphicsShaderStages(); + VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, StagingBuffer, 0, VK_ACCESS_TRANSFER_READ_BIT, EnabledGraphicsShaderStages); + InitialState = RESOURCE_STATE_COPY_DEST; + VkAccessFlags AccessFlags = ResourceStateFlagsToVkAccessFlags(InitialState); + VERIFY_EXPR(AccessFlags == VK_ACCESS_TRANSFER_WRITE_BIT); + VulkanUtilities::VulkanCommandBuffer::BufferMemoryBarrier(vkCmdBuff, m_VulkanBuffer, 0, AccessFlags, 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 + VkBufferCopy BuffCopy = {}; + BuffCopy.srcOffset = 0; + BuffCopy.dstOffset = 0; + BuffCopy.size = VkBuffCI.size; + vkCmdCopyBuffer(vkCmdBuff, StagingBuffer, m_VulkanBuffer, 1, &BuffCopy); + + Uint32 QueueIndex = 0; + pRenderDeviceVk->ExecuteAndDisposeTransientCmdBuff(QueueIndex, vkCmdBuff, std::move(CmdPool)); + + + // After command buffer is submitted, safe-release staging resources. This strategy + // is little overconservative as the resources will only be released after the + // first command buffer submitted through the immediate context is complete + + // Next Cmd Buff| Next Fence | This Thread | Immediate Context + // | | | + // N | F | | + // | | | + // | | ExecuteAndDisposeTransientCmdBuff(vkCmdBuff) | + // | | - SubmittedCmdBuffNumber = N | + // | | - SubmittedFenceValue = F | + // N+1 - - | - F+1 - | | + // | | Release(StagingBuffer) | + // | | - {N+1, StagingBuffer} -> Stale Objects | + // | | | + // | | | + // | | | ExecuteCommandBuffer() + // | | | - SubmittedCmdBuffNumber = N+1 + // | | | - SubmittedFenceValue = F+1 + // N+2 - - | - F+2 - | - - - - - - - - - - - - | + // | | | - DiscardStaleVkObjects(N+1, F+1) + // | | | - {F+1, StagingBuffer} -> Release Queue + // | | | + + pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingBuffer), Uint64{1} << Uint64{QueueIndex}); + pRenderDeviceVk->SafeReleaseDeviceObject(std::move(StagingMemoryAllocation), Uint64{1} << Uint64{QueueIndex}); + } } SetState(InitialState); diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp index a125233f..f9cdcdea 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp @@ -130,7 +130,7 @@ VulkanMemoryAllocation VulkanMemoryManager::Allocate(const VkMemoryRequirements& "at least one bit set corresponding to a VkMemoryType with a propertyFlags that has the " "VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT bit set (11.6)"); } - else if ((MemoryProps & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + else if (MemoryProps == (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { DEV_CHECK_ERR(MemoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, "Vulkan spec requires that for a VkBuffer not created with the VK_BUFFER_CREATE_SPARSE_BINDING_BIT " |
