From 619b9b12c265d82efd340135bb258a17b81b1043 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 6 Sep 2024 01:27:00 +0200 Subject: [PATCH] [dxvk] Implement dynamically growing chunk size May reduce memory footprint for launchers. --- src/dxvk/dxvk_memory.cpp | 46 +++++++++++++++++++++++++--------------- src/dxvk/dxvk_memory.h | 28 ++++++++++++++++++------ 2 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index e516e358e..0d576242a 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -183,8 +183,7 @@ namespace dxvk { DxvkMemoryAllocator::DxvkMemoryAllocator(DxvkDevice* device) : m_device (device), - m_memProps (device->adapter()->memoryProperties()), - m_maxChunkSize (determineMaxChunkSize(device)) { + m_memProps (device->adapter()->memoryProperties()) { for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { m_memHeaps[i].properties = m_memProps.memoryHeaps[i]; m_memHeaps[i].stats = DxvkMemoryStats { 0, 0 }; @@ -195,6 +194,7 @@ namespace dxvk { m_memTypes[i].heapId = m_memProps.memoryTypes[i].heapIndex; m_memTypes[i].memType = m_memProps.memoryTypes[i]; m_memTypes[i].memTypeId = i; + m_memTypes[i].chunkSize = MinChunkSize; } if (device->features().core.features.sparseBinding) @@ -313,7 +313,7 @@ namespace dxvk { VkDeviceSize align, const DxvkMemoryProperties& info, DxvkMemoryFlags hints) { - VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, hints); + VkDeviceSize chunkSize = pickChunkSize(type->memTypeId, size, hints); DxvkMemory memory; @@ -349,6 +349,8 @@ namespace dxvk { memory = chunk->alloc(info.flags, size, align, hints); type->chunks.push_back(std::move(chunk)); + + adjustChunkSize(type->memTypeId, devMem.memSize, hints); } } } @@ -489,12 +491,14 @@ namespace dxvk { } - VkDeviceSize DxvkMemoryAllocator::pickChunkSize(uint32_t memTypeId, DxvkMemoryFlags hints) const { + VkDeviceSize DxvkMemoryAllocator::pickChunkSize(uint32_t memTypeId, VkDeviceSize requiredSize, DxvkMemoryFlags hints) const { VkMemoryType type = m_memProps.memoryTypes[memTypeId]; VkMemoryHeap heap = m_memProps.memoryHeaps[type.heapIndex]; - // Default to a chunk size of 256 MiB - VkDeviceSize chunkSize = m_maxChunkSize; + VkDeviceSize chunkSize = m_memTypes[memTypeId].chunkSize; + + while (chunkSize < requiredSize) + chunkSize <<= 1u; if (hints.test(DxvkMemoryFlag::Small)) chunkSize = std::min(chunkSize, 16 << 20); @@ -513,6 +517,20 @@ namespace dxvk { } + void DxvkMemoryAllocator::adjustChunkSize( + uint32_t memTypeId, + VkDeviceSize allocatedSize, + DxvkMemoryFlags hints) { + VkDeviceSize chunkSize = m_memTypes[memTypeId].chunkSize; + + // Don't bump chunk size if we reached the maximum or if + // we already were unable to allocate a full chunk. + if (chunkSize < MaxChunkSize && chunkSize <= allocatedSize + && chunkSize <= m_memTypes[memTypeId].heap->stats.memoryAllocated) + m_memTypes[memTypeId].chunkSize <<= 1u; + } + + bool DxvkMemoryAllocator::shouldFreeChunk( const DxvkMemoryType* type, const Rc& chunk) const { @@ -520,6 +538,11 @@ namespace dxvk { if (this->shouldFreeEmptyChunks(type->heap, 0)) return true; + // Free chunks that are below the current chunk size since it probably + // not going to be able to serve enough allocations to be useful. + if (chunk->size() < type->chunkSize) + return true; + // Only keep a small number of chunks of each type around to save memory. uint32_t numEmptyChunks = 0; @@ -635,17 +658,6 @@ namespace dxvk { } - VkDeviceSize DxvkMemoryAllocator::determineMaxChunkSize( - DxvkDevice* device) const { - int32_t option = device->config().maxChunkSize; - - if (option <= 0) - option = 256; - - return VkDeviceSize(option) << 20; - } - - 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 26ae85f49..74835a522 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -83,6 +83,8 @@ namespace dxvk { VkMemoryType memType; uint32_t memTypeId; + VkDeviceSize chunkSize; + std::vector> chunks; }; @@ -211,6 +213,14 @@ namespace dxvk { ~DxvkMemoryChunk(); + /** + * \brief Queries chunk size + * \returns Chunk size + */ + VkDeviceSize size() const { + return m_memory.memSize; + } + /** * \brief Allocates memory from the chunk * @@ -304,6 +314,9 @@ namespace dxvk { friend class DxvkMemoryChunk; constexpr static VkDeviceSize SmallAllocationThreshold = 256 << 10; + + constexpr static VkDeviceSize MinChunkSize = 4ull << 20; + constexpr static VkDeviceSize MaxChunkSize = 256ull << 20; public: DxvkMemoryAllocator(DxvkDevice* device); @@ -348,10 +361,8 @@ namespace dxvk { VkPhysicalDeviceMemoryProperties m_memProps; dxvk::mutex m_mutex; - std::array m_memHeaps; - std::array m_memTypes; - - VkDeviceSize m_maxChunkSize; + std::array m_memHeaps = { }; + std::array m_memTypes = { }; uint32_t m_sparseMemoryTypes = 0u; @@ -388,8 +399,14 @@ namespace dxvk { VkDeviceSize pickChunkSize( uint32_t memTypeId, + VkDeviceSize requiredSize, DxvkMemoryFlags hints) const; + void adjustChunkSize( + uint32_t memTypeId, + VkDeviceSize allocatedSize, + DxvkMemoryFlags hints); + bool shouldFreeChunk( const DxvkMemoryType* type, const Rc& chunk) const; @@ -404,9 +421,6 @@ namespace dxvk { uint32_t determineSparseMemoryTypes( DxvkDevice* device) const; - VkDeviceSize determineMaxChunkSize( - DxvkDevice* device) const; - void logMemoryError( const VkMemoryRequirements& req) const;