diff --git a/src/dxvk/dxvk_buffer.cpp b/src/dxvk/dxvk_buffer.cpp index 00dde9d7..48d88bbb 100644 --- a/src/dxvk/dxvk_buffer.cpp +++ b/src/dxvk/dxvk_buffer.cpp @@ -88,6 +88,7 @@ namespace dxvk { // Query memory requirements and whether to use a dedicated allocation DxvkMemoryRequirements memoryRequirements = { }; + memoryRequirements.tiling = VK_IMAGE_TILING_LINEAR; memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS }; memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated }; diff --git a/src/dxvk/dxvk_image.cpp b/src/dxvk/dxvk_image.cpp index 69b74547..5d84386f 100644 --- a/src/dxvk/dxvk_image.cpp +++ b/src/dxvk/dxvk_image.cpp @@ -65,6 +65,7 @@ namespace dxvk { // Get memory requirements for the image and ask driver // whether we need to use a dedicated allocation. DxvkMemoryRequirements memoryRequirements = { }; + memoryRequirements.tiling = info.tiling; memoryRequirements.dedicated = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS }; memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &memoryRequirements.dedicated }; @@ -96,17 +97,6 @@ namespace dxvk { memoryProperties.dedicated.image = m_image.image; } - // If there's a chance we won't create the image with a dedicated - // allocation, enforce strict alignment for tiled images to not - // violate the bufferImageGranularity requirement on some GPUs. - if (info.tiling != VK_IMAGE_TILING_LINEAR && !memoryRequirements.dedicated.requiresDedicatedAllocation) { - VkDeviceSize granularity = memAlloc.bufferImageGranularity(); - - auto& core = memoryRequirements.core.memoryRequirements; - core.size = align(core.size, granularity); - core.alignment = align(core.alignment, granularity); - } - // Use high memory priority for GPU-writable resources bool isGpuWritable = (m_info.access & ( VK_ACCESS_SHADER_WRITE_BIT | @@ -136,6 +126,7 @@ namespace dxvk { if (properties.metadataPageCount) { DxvkMemoryRequirements memoryRequirements = { }; + memoryRequirements.tiling = VK_IMAGE_TILING_OPTIMAL; memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 }; m_vkd->vkGetImageMemoryRequirements2(m_vkd->device(), diff --git a/src/dxvk/dxvk_memory.cpp b/src/dxvk/dxvk_memory.cpp index e88dff88..96c10045 100644 --- a/src/dxvk/dxvk_memory.cpp +++ b/src/dxvk/dxvk_memory.cpp @@ -181,7 +181,6 @@ namespace dxvk { DxvkMemoryAllocator::DxvkMemoryAllocator(DxvkDevice* device) : m_device (device), - m_devProps (device->adapter()->deviceProperties()), m_memProps (device->adapter()->memoryProperties()) { for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { m_memHeaps[i].properties = m_memProps.memoryHeaps[i]; @@ -207,7 +206,7 @@ namespace dxvk { DxvkMemory DxvkMemoryAllocator::alloc( - const DxvkMemoryRequirements& req, + DxvkMemoryRequirements req, DxvkMemoryProperties info, DxvkMemoryFlags hints) { std::lock_guard lock(m_mutex); @@ -225,66 +224,80 @@ namespace dxvk { if (info.flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) hints = hints & DxvkMemoryFlag::Transient; - // Try to allocate from a memory type which supports the given flags exactly - DxvkMemory result = this->tryAlloc(req, info, hints); + // If requested, try with a dedicated allocation first. + if (info.dedicated.image || info.dedicated.buffer) { + DxvkMemory result = this->tryAlloc(req, info, hints); - if (!result && !req.dedicated.requiresDedicatedAllocation) { - // If that failed, try without a dedicated allocation - if (info.dedicated.image || info.dedicated.buffer) { - info.dedicated.image = VK_NULL_HANDLE; - info.dedicated.buffer = VK_NULL_HANDLE; - - result = this->tryAlloc(req, info, hints); - } + if (result) + return result; } - if (!result) { + // If possible, retry without a dedicated allocation + if (!req.dedicated.requiresDedicatedAllocation) { + info.dedicated.image = VK_NULL_HANDLE; + info.dedicated.buffer = VK_NULL_HANDLE; + + // If we're allocating tiled image memory, ensure + // that it will not overlap with buffer memory. + if (req.tiling == VK_IMAGE_TILING_OPTIMAL) { + VkDeviceSize granularity = m_device->properties().core.properties.limits.bufferImageGranularity; + req.core.memoryRequirements.size = align(req.core.memoryRequirements.size, granularity); + req.core.memoryRequirements.alignment = align(req.core.memoryRequirements.alignment, granularity); + } + + DxvkMemory result = this->tryAlloc(req, info, hints); + + if (result) + return result; + // Retry without the hint constraints hints.set(DxvkMemoryFlag::IgnoreConstraints); result = this->tryAlloc(req, info, hints); + + if (result) + return result; } - if (!result) { - // If that still didn't work, probe slower memory types as well - VkMemoryPropertyFlags optFlags = info.flags & ( - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | - VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + // If that still didn't work, probe slower memory types as + // well, but re-enable restrictions to decrease fragmentation. + hints.clr(DxvkMemoryFlag::IgnoreConstraints); - while (!result && optFlags) { - VkMemoryPropertyFlags bit = optFlags & -optFlags; - optFlags &= ~bit; + const VkMemoryPropertyFlags optionalFlags = + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT; - info.flags &= ~bit; - result = this->tryAlloc(req, info, hints); - } + if (info.flags & optionalFlags) { + info.flags &= ~optionalFlags; + + DxvkMemory result = this->tryAlloc(req, info, hints); + + if (result) + return result; } - if (!result) { - DxvkAdapterMemoryInfo memHeapInfo = m_device->adapter()->getMemoryHeapInfo(); + // We weren't able to allocate memory for this resource form any type + DxvkAdapterMemoryInfo memHeapInfo = m_device->adapter()->getMemoryHeapInfo(); - Logger::err(str::format( - "DxvkMemoryAllocator: Memory allocation failed", - "\n Size: ", req.core.memoryRequirements.size, - "\n Alignment: ", req.core.memoryRequirements.alignment, - "\n Mem types: ", "0x", std::hex, req.core.memoryRequirements.memoryTypeBits)); + Logger::err(str::format( + "DxvkMemoryAllocator: Memory allocation failed", + "\n Size: ", req.core.memoryRequirements.size, + "\n Alignment: ", req.core.memoryRequirements.alignment, + "\n Mem types: ", "0x", std::hex, req.core.memoryRequirements.memoryTypeBits)); - for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { - Logger::err(str::format("Heap ", i, ": ", - (m_memHeaps[i].stats.memoryAllocated >> 20), " MB allocated, ", - (m_memHeaps[i].stats.memoryUsed >> 20), " MB used, ", - m_device->features().extMemoryBudget - ? str::format( - (memHeapInfo.heaps[i].memoryAllocated >> 20), " MB allocated (driver), ", - (memHeapInfo.heaps[i].memoryBudget >> 20), " MB budget (driver), ", - (m_memHeaps[i].properties.size >> 20), " MB total") - : str::format( - (m_memHeaps[i].properties.size >> 20), " MB total"))); - } - - throw DxvkError("DxvkMemoryAllocator: Memory allocation failed"); + for (uint32_t i = 0; i < m_memProps.memoryHeapCount; i++) { + Logger::err(str::format("Heap ", i, ": ", + (m_memHeaps[i].stats.memoryAllocated >> 20), " MB allocated, ", + (m_memHeaps[i].stats.memoryUsed >> 20), " MB used, ", + m_device->features().extMemoryBudget + ? str::format( + (memHeapInfo.heaps[i].memoryAllocated >> 20), " MB allocated (driver), ", + (memHeapInfo.heaps[i].memoryBudget >> 20), " MB budget (driver), ", + (m_memHeaps[i].properties.size >> 20), " MB total") + : str::format( + (m_memHeaps[i].properties.size >> 20), " MB total"))); } - - return result; + + throw DxvkError("DxvkMemoryAllocator: Memory allocation failed"); } diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index c5caaf88..daeb84bc 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -277,6 +277,7 @@ namespace dxvk { * \brief Memory requirement info */ struct DxvkMemoryRequirements { + VkImageTiling tiling; VkMemoryDedicatedRequirements dedicated; VkMemoryRequirements2 core; }; @@ -309,18 +310,6 @@ namespace dxvk { DxvkMemoryAllocator(DxvkDevice* device); ~DxvkMemoryAllocator(); - /** - * \brief Buffer-image granularity - * - * The granularity between linear and non-linear - * resources in adjacent memory locations. See - * section 11.6 of the Vulkan spec for details. - * \returns Buffer-image granularity - */ - VkDeviceSize bufferImageGranularity() const { - return m_devProps.limits.bufferImageGranularity; - } - /** * \brief Memory type mask for sparse resources * \returns Sparse resource memory types @@ -338,7 +327,7 @@ namespace dxvk { * \returns Allocated memory slice */ DxvkMemory alloc( - const DxvkMemoryRequirements& req, + DxvkMemoryRequirements req, DxvkMemoryProperties info, DxvkMemoryFlags hints); @@ -357,7 +346,6 @@ namespace dxvk { private: DxvkDevice* m_device; - VkPhysicalDeviceProperties m_devProps; VkPhysicalDeviceMemoryProperties m_memProps; dxvk::mutex m_mutex; diff --git a/src/dxvk/dxvk_sparse.cpp b/src/dxvk/dxvk_sparse.cpp index 373906ed..ffdc7814 100644 --- a/src/dxvk/dxvk_sparse.cpp +++ b/src/dxvk/dxvk_sparse.cpp @@ -120,6 +120,7 @@ namespace dxvk { Rc DxvkSparsePageAllocator::allocPage() { DxvkMemoryRequirements memoryRequirements = { }; + memoryRequirements.tiling = VK_IMAGE_TILING_LINEAR; memoryRequirements.core = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 }; // We don't know what kind of resource the memory6