diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.cpp | 67 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.h | 7 |
2 files changed, 43 insertions, 31 deletions
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index fa37aa79a..5edd06ebc 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp | |||
| @@ -53,6 +53,18 @@ struct Range { | |||
| 53 | UNREACHABLE_MSG("Invalid memory usage={}", usage); | 53 | UNREACHABLE_MSG("Invalid memory usage={}", usage); |
| 54 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | 54 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| 55 | } | 55 | } |
| 56 | |||
| 57 | constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{ | ||
| 58 | .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, | ||
| 59 | .pNext = nullptr, | ||
| 60 | #ifdef _WIN32 | ||
| 61 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, | ||
| 62 | #elif __unix__ | ||
| 63 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, | ||
| 64 | #else | ||
| 65 | .handleTypes = 0, | ||
| 66 | #endif | ||
| 67 | }; | ||
| 56 | } // Anonymous namespace | 68 | } // Anonymous namespace |
| 57 | 69 | ||
| 58 | class MemoryAllocation { | 70 | class MemoryAllocation { |
| @@ -131,7 +143,7 @@ public: | |||
| 131 | 143 | ||
| 132 | /// Returns whether this allocation is compatible with the arguments. | 144 | /// Returns whether this allocation is compatible with the arguments. |
| 133 | [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { | 145 | [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { |
| 134 | return (flags & property_flags) && (type_mask & shifted_memory_type) != 0; | 146 | return (flags & property_flags) == property_flags && (type_mask & shifted_memory_type) != 0; |
| 135 | } | 147 | } |
| 136 | 148 | ||
| 137 | private: | 149 | private: |
| @@ -217,14 +229,18 @@ MemoryAllocator::~MemoryAllocator() = default; | |||
| 217 | 229 | ||
| 218 | MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { | 230 | MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { |
| 219 | // Find the fastest memory flags we can afford with the current requirements | 231 | // Find the fastest memory flags we can afford with the current requirements |
| 220 | const VkMemoryPropertyFlags flags = MemoryPropertyFlags(requirements.memoryTypeBits, usage); | 232 | const u32 type_mask = requirements.memoryTypeBits; |
| 233 | const VkMemoryPropertyFlags usage_flags = MemoryUsagePropertyFlags(usage); | ||
| 234 | const VkMemoryPropertyFlags flags = MemoryPropertyFlags(type_mask, usage_flags); | ||
| 221 | if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { | 235 | if (std::optional<MemoryCommit> commit = TryCommit(requirements, flags)) { |
| 222 | return std::move(*commit); | 236 | return std::move(*commit); |
| 223 | } | 237 | } |
| 224 | // Commit has failed, allocate more memory. | 238 | // Commit has failed, allocate more memory. |
| 225 | // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. | 239 | const u64 chunk_size = AllocationChunkSize(requirements.size); |
| 226 | AllocMemory(flags, requirements.memoryTypeBits, AllocationChunkSize(requirements.size)); | 240 | if (!TryAllocMemory(flags, type_mask, chunk_size)) { |
| 227 | 241 | // TODO(Rodrigo): Handle out of memory situations in some way like flushing to guest memory. | |
| 242 | throw vk::Exception(VK_ERROR_OUT_OF_DEVICE_MEMORY); | ||
| 243 | } | ||
| 228 | // Commit again, this time it won't fail since there's a fresh allocation above. | 244 | // Commit again, this time it won't fail since there's a fresh allocation above. |
| 229 | // If it does, there's a bug. | 245 | // If it does, there's a bug. |
| 230 | return TryCommit(requirements, flags).value(); | 246 | return TryCommit(requirements, flags).value(); |
| @@ -242,26 +258,25 @@ MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) | |||
| 242 | return commit; | 258 | return commit; |
| 243 | } | 259 | } |
| 244 | 260 | ||
| 245 | void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { | 261 | bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { |
| 246 | const u32 type = FindType(flags, type_mask).value(); | 262 | const u32 type = FindType(flags, type_mask).value(); |
| 247 | const VkExportMemoryAllocateInfo export_allocate_info{ | 263 | vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ |
| 248 | .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, | ||
| 249 | .pNext = nullptr, | ||
| 250 | #ifdef _WIN32 | ||
| 251 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, | ||
| 252 | #elif __unix__ | ||
| 253 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, | ||
| 254 | #else | ||
| 255 | .handleTypes = 0, | ||
| 256 | #endif | ||
| 257 | }; | ||
| 258 | vk::DeviceMemory memory = device.GetLogical().AllocateMemory({ | ||
| 259 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | 264 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| 260 | .pNext = export_allocations ? &export_allocate_info : nullptr, | 265 | .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr, |
| 261 | .allocationSize = size, | 266 | .allocationSize = size, |
| 262 | .memoryTypeIndex = type, | 267 | .memoryTypeIndex = type, |
| 263 | }); | 268 | }); |
| 269 | if (!memory) { | ||
| 270 | if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { | ||
| 271 | // Try to allocate non device local memory | ||
| 272 | return TryAllocMemory(flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, type_mask, size); | ||
| 273 | } else { | ||
| 274 | // RIP | ||
| 275 | return false; | ||
| 276 | } | ||
| 277 | } | ||
| 264 | allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); | 278 | allocations.push_back(std::make_unique<MemoryAllocation>(std::move(memory), flags, size, type)); |
| 279 | return true; | ||
| 265 | } | 280 | } |
| 266 | 281 | ||
| 267 | std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, | 282 | std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, |
| @@ -274,24 +289,24 @@ std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirement | |||
| 274 | return commit; | 289 | return commit; |
| 275 | } | 290 | } |
| 276 | } | 291 | } |
| 292 | if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { | ||
| 293 | // Look for non device local commits on failure | ||
| 294 | return TryCommit(requirements, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); | ||
| 295 | } | ||
| 277 | return std::nullopt; | 296 | return std::nullopt; |
| 278 | } | 297 | } |
| 279 | 298 | ||
| 280 | VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const { | ||
| 281 | return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage)); | ||
| 282 | } | ||
| 283 | |||
| 284 | VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, | 299 | VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, |
| 285 | VkMemoryPropertyFlags flags) const { | 300 | VkMemoryPropertyFlags flags) const { |
| 286 | if (FindType(flags, type_mask)) { | 301 | if (FindType(flags, type_mask)) { |
| 287 | // Found a memory type with those requirements | 302 | // Found a memory type with those requirements |
| 288 | return flags; | 303 | return flags; |
| 289 | } | 304 | } |
| 290 | if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) { | 305 | if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) { |
| 291 | // Remove host cached bit in case it's not supported | 306 | // Remove host cached bit in case it's not supported |
| 292 | return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); | 307 | return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_HOST_CACHED_BIT); |
| 293 | } | 308 | } |
| 294 | if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { | 309 | if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) { |
| 295 | // Remove device local, if it's not supported by the requested resource | 310 | // Remove device local, if it's not supported by the requested resource |
| 296 | return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); | 311 | return MemoryPropertyFlags(type_mask, flags & ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); |
| 297 | } | 312 | } |
| @@ -302,7 +317,7 @@ VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, | |||
| 302 | std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { | 317 | std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { |
| 303 | for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { | 318 | for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { |
| 304 | const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; | 319 | const VkMemoryPropertyFlags type_flags = properties.memoryTypes[type_index].propertyFlags; |
| 305 | if ((type_mask & (1U << type_index)) && (type_flags & flags)) { | 320 | if ((type_mask & (1U << type_index)) != 0 && (type_flags & flags) == flags) { |
| 306 | // The type matches in type and in the wanted properties. | 321 | // The type matches in type and in the wanted properties. |
| 307 | return type_index; | 322 | return type_index; |
| 308 | } | 323 | } |
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index d1ce29450..db12d02f4 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h | |||
| @@ -101,16 +101,13 @@ public: | |||
| 101 | MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); | 101 | MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); |
| 102 | 102 | ||
| 103 | private: | 103 | private: |
| 104 | /// Allocates a chunk of memory. | 104 | /// Tries to allocate a chunk of memory. |
| 105 | void AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); | 105 | bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); |
| 106 | 106 | ||
| 107 | /// Tries to allocate a memory commit. | 107 | /// Tries to allocate a memory commit. |
| 108 | std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, | 108 | std::optional<MemoryCommit> TryCommit(const VkMemoryRequirements& requirements, |
| 109 | VkMemoryPropertyFlags flags); | 109 | VkMemoryPropertyFlags flags); |
| 110 | 110 | ||
| 111 | /// Returns the fastest compatible memory property flags from a wanted usage. | ||
| 112 | VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const; | ||
| 113 | |||
| 114 | /// Returns the fastest compatible memory property flags from the wanted flags. | 111 | /// Returns the fastest compatible memory property flags from the wanted flags. |
| 115 | VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; | 112 | VkMemoryPropertyFlags MemoryPropertyFlags(u32 type_mask, VkMemoryPropertyFlags flags) const; |
| 116 | 113 | ||