From 3cd7e3efb593964616ea4a31c7945d3677f9adc8 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 23 Sep 2024 23:38:18 +0200 Subject: [PATCH] [dxvk] Tweak memory allocation behaviour on mapped memory types --- src/dxvk/dxvk_memory.cpp | 24 ++++++++++++++++-------- src/dxvk/dxvk_memory.h | 2 -- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index c393c9f8a..51bc19f18 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -209,7 +209,16 @@ namespace dxvk { // If the allocation is very large, use a dedicated allocation instead // of creating a new chunk. This way we avoid excessive fragmentation, // especially when a multiple such resources are created at once. - if (size * MinResourcesPerChunk > selectedPool.maxChunkSize) { + uint32_t minResourcesPerChunk = (properties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? 1u : 4u; + + // If we're on a mapped memory type and we're about to lose an entire chunk + // worth of memory to huge resources causing fragmentation, use dedicated + // allocations anyway and hope that the app doesn't do this every frame. + if (minResourcesPerChunk == 1u && size > selectedPool.maxChunkSize / 2u + && type.stats.memoryAllocated - type.stats.memoryUsed + selectedPool.maxChunkSize - size >= selectedPool.maxChunkSize) + minResourcesPerChunk = 2u; + + if (size * minResourcesPerChunk > selectedPool.maxChunkSize) { DxvkDeviceMemory memory = allocateDeviceMemory(type, requirements.size, nullptr); if (!memory.memory) @@ -223,7 +232,7 @@ namespace dxvk { // multiple resources of the type we're tying to allocate. VkDeviceSize desiredSize = selectedPool.nextChunkSize; - while (desiredSize < size * MinResourcesPerChunk) + while (desiredSize < size * minResourcesPerChunk) desiredSize *= 2u; if (allocateChunkInPool(type, selectedPool, properties, size, desiredSize)) { @@ -484,14 +493,13 @@ namespace dxvk { VkDeviceSize allocationSize, high_resolution_clock::time_point time) { // Allow for one unused max-size chunk on device-local memory types. - // For system memory allocations, we need to be more lenient since - // applications will frequently allocate staging buffers. + // For mapped memory allocations, we need to be more lenient since + // applications will frequently allocate staging buffers or dynamic + // resources. VkDeviceSize maxUnusedMemory = pool.maxChunkSize; - if (!(type.properties.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) - && (type.properties.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) - && (&pool == &type.mappedPool)) - maxUnusedMemory *= env::is32BitHostPlatform() ? 2u : 4u; + if (&pool == &type.mappedPool) + maxUnusedMemory *= 4u; // Factor current memory allocation into the decision to free chunks VkDeviceSize heapBudget = (type.heap->properties.size * 4) / 5; diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index 43d8ba3d3..4e55b4ed6 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -335,8 +335,6 @@ namespace dxvk { constexpr static VkDeviceSize MinChunkSize = 4ull << 20; constexpr static VkDeviceSize MaxChunkSize = 256ull << 20; - - constexpr static VkDeviceSize MinResourcesPerChunk = 4u; public: DxvkMemoryAllocator(DxvkDevice* device);