From 5813e7c031252bb3e4f9c7c3775a799c275badf5 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 23 Sep 2024 00:22:21 +0200 Subject: [PATCH] [dxvk] Add function to create buffer resource --- src/dxvk/dxvk_memory.cpp | 124 +++++++++++++++++++++++++++++++++++++++ src/dxvk/dxvk_memory.h | 16 +++++ 2 files changed, 140 insertions(+) diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index 8e7e634e..333b2c11 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -416,6 +416,120 @@ namespace dxvk { } + Rc DxvkMemoryAllocator::createBufferResource( + const VkBufferCreateInfo& createInfo, + VkMemoryPropertyFlags properties) { + Rc allocation; + + if (likely(!createInfo.flags && createInfo.sharingMode == VK_SHARING_MODE_EXCLUSIVE)) { + VkMemoryRequirements memoryRequirements = { }; + memoryRequirements.size = createInfo.size; + memoryRequirements.alignment = GlobalBufferAlignment; + memoryRequirements.memoryTypeBits = m_globalBufferMemoryTypes; + + if (unlikely(createInfo.usage & ~m_globalBufferUsageFlags)) + memoryRequirements.memoryTypeBits = findGlobalBufferMemoryTypeMask(createInfo.usage); + + // If there is at least one memory type that supports the required + // buffer usage flags and requested memory properties, suballocate + // from a global buffer. + if (likely(memoryRequirements.memoryTypeBits)) { + allocation = allocateMemory(memoryRequirements, properties); + + if (likely(allocation && allocation->m_buffer)) + return allocation; + + if (!allocation && (properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + allocation = allocateMemory(memoryRequirements, + properties & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + if (likely(allocation && allocation->m_buffer)) + return allocation; + } + + // If we end up here with an allocation but no buffer, something + // is weird, but we can keep the allocation around for now. + if (allocation && !allocation->m_buffer) { + Logger::err(str::format("Got allocation from memory type ", + allocation->m_type->index, " without global buffer")); + } + } + } + + // If we can't suballocate from an existing global buffer + // for any reason, create a dedicated buffer resource. + auto vk = m_device->vkd(); + + VkBuffer buffer = VK_NULL_HANDLE; + VkResult vr = vk->vkCreateBuffer(vk->device(), + &createInfo, nullptr, &buffer); + + if (vr != VK_SUCCESS) { + throw DxvkError(str::format("Failed to create buffer: ", vr, + "\n size: ", createInfo.size, + "\n usage: ", std::hex, createInfo.usage, + "\n flags: ", createInfo.flags)); + } + + if (!(createInfo.flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT)) { + VkBufferMemoryRequirementsInfo2 requirementInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 }; + requirementInfo.buffer = buffer; + + VkMemoryRequirements2 requirements = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 }; + vk->vkGetBufferMemoryRequirements2(vk->device(), &requirementInfo, &requirements); + + // If we have an existing global allocation from earlier, make sure it is suitable + if (!allocation || !(requirements.memoryRequirements.memoryTypeBits & (1u << allocation->m_type->index)) + || (allocation->m_size < requirements.memoryRequirements.size) + || (allocation->m_address & requirements.memoryRequirements.alignment)) + allocation = allocateMemory(requirements.memoryRequirements, properties); + + if (!allocation && (properties & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + allocation = allocateMemory(requirements.memoryRequirements, + properties & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + } + + if (!allocation) { + logMemoryError(requirements.memoryRequirements); + logMemoryStats(); + } + } else { + // TODO implement sparse + } + + if (!allocation) { + vk->vkDestroyBuffer(vk->device(), buffer, nullptr); + return nullptr; + } + + // Transfer ownership of te Vulkan buffer to the allocation + // and set up all remaining properties. + allocation->m_flags.set(DxvkAllocationFlag::OwnsBuffer); + allocation->m_buffer = buffer; + allocation->m_bufferOffset = 0u; + allocation->m_bufferAddress = 0u; + + // Bind memory if the buffer is not sparse + if (allocation->m_memory) { + vr = vk->vkBindBufferMemory(vk->device(), allocation->m_buffer, + allocation->m_memory, allocation->m_address & DxvkPageAllocator::ChunkAddressMask); + + if (vr != VK_SUCCESS) { + throw DxvkError(str::format("Failed to bind buffer memory: ", vr, + "\n size: ", createInfo.size, + "\n usage: ", std::hex, createInfo.usage, + "\n flags: ", createInfo.flags)); + } + } + + // Query device address after binding memory, or the address would be invalid + if (createInfo.usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) + allocation->m_bufferAddress = getBufferDeviceAddress(buffer); + + return allocation; + } + + DxvkDeviceMemory DxvkMemoryAllocator::allocateDeviceMemory( DxvkMemoryType& type, VkDeviceSize size, @@ -1086,6 +1200,16 @@ namespace dxvk { } + VkDeviceAddress DxvkMemoryAllocator::getBufferDeviceAddress(VkBuffer buffer) const { + auto vk = m_device->vkd(); + + VkBufferDeviceAddressInfo bdaInfo = { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO }; + bdaInfo.buffer = buffer; + + return vk->vkGetBufferDeviceAddress(vk->device(), &bdaInfo); + } + + void DxvkMemoryAllocator::logMemoryError(const VkMemoryRequirements& req) const { std::stringstream sstr; sstr << "DxvkMemoryAllocator: Memory allocation failed" << std::endl diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index 70ec2e25..ed756a8e 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -820,6 +820,19 @@ namespace dxvk { VkMemoryPropertyFlags properties, const void* next); + /** + * \brief Creates buffer resource + * + * Will make use of global buffers whenever possible, but + * may fall back to creating a dedicated Vulkan buffer. + * \param [in] createInfo Buffer create info + * \param [in] properties Memory property flags + * \returns Buffer resource + */ + Rc createBufferResource( + const VkBufferCreateInfo& createInfo, + VkMemoryPropertyFlags properties); + /** * \brief Queries memory stats * @@ -956,6 +969,9 @@ namespace dxvk { void determineMemoryTypesWithPropertyFlags(); + VkDeviceAddress getBufferDeviceAddress( + VkBuffer buffer) const; + void logMemoryError( const VkMemoryRequirements& req) const;