summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-05-27 05:47:07 -0300
committerGravatar ReinUsesLisp2021-05-27 05:50:48 -0300
commit19454e71d808da5bdfe4c4831ff85333a4514323 (patch)
treec8d259521c4ded09839a61f786fa802fffa28cc4
parentMerge pull request #6366 from lat9nq/bundled-qt-linux (diff)
downloadyuzu-19454e71d808da5bdfe4c4831ff85333a4514323.tar.gz
yuzu-19454e71d808da5bdfe4c4831ff85333a4514323.tar.xz
yuzu-19454e71d808da5bdfe4c4831ff85333a4514323.zip
vulkan_memory_allocator: Allow textures to be allocated in host memory
Allow Vulkan's allocator to use host memory when there's no more device local memory. This delays OOM, but it will eventually still happen.
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp67
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.h7
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
57constexpr 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
58class MemoryAllocation { 70class 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
137private: 149private:
@@ -217,14 +229,18 @@ MemoryAllocator::~MemoryAllocator() = default;
217 229
218MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { 230MemoryCommit 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
245void MemoryAllocator::AllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { 261bool 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
267std::optional<MemoryCommit> MemoryAllocator::TryCommit(const VkMemoryRequirements& requirements, 282std::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
280VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, MemoryUsage usage) const {
281 return MemoryPropertyFlags(type_mask, MemoryUsagePropertyFlags(usage));
282}
283
284VkMemoryPropertyFlags MemoryAllocator::MemoryPropertyFlags(u32 type_mask, 299VkMemoryPropertyFlags 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,
302std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 type_mask) const { 317std::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
103private: 103private:
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