From f1f8d45fcdcf0018941fbd1f43460cd0a793786c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 22 Sep 2022 16:37:41 +0200 Subject: [PATCH] [dxvk] Rework allocation logic for large resources This may reduce internal fragmentation with very large resources. We previously changed behaviour to not do this in order to reduce memory pressure in the average case, however by trying to suballocate from existing chunks and falling back to a dedicated allocation on failure, rather than allocating a new chunk, we can mostly avoid that situation. --- src/dxvk/dxvk_memory.cpp | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index aca64296..af4760b5 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -326,19 +326,25 @@ namespace dxvk { DxvkMemory memory; - if (size >= chunkSize || info.dedicated.buffer || info.dedicated.image) { - if (this->shouldFreeEmptyChunks(type->heap, size)) - this->freeEmptyChunks(type->heap); + // Require dedicated allocations for resources that use the Vulkan dedicated + // allocation bits, or are too large to fit into a single full-sized chunk + bool needsDedicatedAlocation = size >= chunkSize || info.dedicated.buffer || info.dedicated.image; - DxvkDeviceMemory devMem = this->tryAllocDeviceMemory(type, size, info, hints); + // Prefer a dedicated allocation for very large resources in order to + // reduce fragmentation if a large number of those resources are in use + bool wantsDedicatedAllocation = 3 * size >= chunkSize; - if (devMem.memHandle != VK_NULL_HANDLE) - memory = DxvkMemory(this, nullptr, type, devMem.memHandle, 0, size, devMem.memPointer); - } else { + // Try to reuse existing memory as much as possible in case the heap is nearly full + bool heapBudgedExceeded = 5 * type->heap->stats.memoryUsed + size > 4 * type->heap->properties.size; + + if (!needsDedicatedAlocation && (!wantsDedicatedAllocation || heapBudgedExceeded)) { + // Attempt to suballocate from existing chunks first for (uint32_t i = 0; i < type->chunks.size() && !memory; i++) memory = type->chunks[i]->alloc(info.flags, size, align, hints); - if (!memory) { + // If no existing chunk can accomodate the allocation, and if a dedicated + // allocation is not preferred, create a new chunk and suballocate from it + if (!memory && !wantsDedicatedAllocation) { DxvkDeviceMemory devMem; if (this->shouldFreeEmptyChunks(type->heap, chunkSize)) @@ -356,6 +362,18 @@ namespace dxvk { } } + // If a dedicated allocation is required or preferred and we haven't managed + // to suballocate any memory before, try to create a dedicated allocation + if (!memory && (needsDedicatedAlocation || wantsDedicatedAllocation)) { + if (this->shouldFreeEmptyChunks(type->heap, size)) + this->freeEmptyChunks(type->heap); + + DxvkDeviceMemory devMem = this->tryAllocDeviceMemory(type, size, info, hints); + + if (devMem.memHandle != VK_NULL_HANDLE) + memory = DxvkMemory(this, nullptr, type, devMem.memHandle, 0, size, devMem.memPointer); + } + if (memory) type->heap->stats.memoryUsed += memory.m_length;