diff options
Diffstat (limited to 'src/video_core')
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_memory_manager.cpp | 158 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_memory_manager.h | 72 |
2 files changed, 142 insertions, 88 deletions
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.cpp b/src/video_core/renderer_vulkan/vk_memory_manager.cpp index 0451babbf..9cc9979d0 100644 --- a/src/video_core/renderer_vulkan/vk_memory_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_memory_manager.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <optional> | 6 | #include <optional> |
| 7 | #include <tuple> | 7 | #include <tuple> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | |||
| 9 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 10 | #include "common/assert.h" | 11 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| @@ -16,34 +17,32 @@ | |||
| 16 | 17 | ||
| 17 | namespace Vulkan { | 18 | namespace Vulkan { |
| 18 | 19 | ||
| 19 | // TODO(Rodrigo): Fine tune this number | 20 | namespace { |
| 20 | constexpr u64 ALLOC_CHUNK_SIZE = 64 * 1024 * 1024; | 21 | |
| 22 | u64 GetAllocationChunkSize(u64 required_size) { | ||
| 23 | static constexpr u64 sizes[] = {16ULL << 20, 32ULL << 20, 64ULL << 20, 128ULL << 20}; | ||
| 24 | auto it = std::lower_bound(std::begin(sizes), std::end(sizes), required_size); | ||
| 25 | return it != std::end(sizes) ? *it : Common::AlignUp(required_size, 256ULL << 20); | ||
| 26 | } | ||
| 27 | |||
| 28 | } // Anonymous namespace | ||
| 21 | 29 | ||
| 22 | class VKMemoryAllocation final { | 30 | class VKMemoryAllocation final { |
| 23 | public: | 31 | public: |
| 24 | explicit VKMemoryAllocation(const VKDevice& device, vk::DeviceMemory memory, | 32 | explicit VKMemoryAllocation(const VKDevice& device, vk::DeviceMemory memory, |
| 25 | vk::MemoryPropertyFlags properties, u64 alloc_size, u32 type) | 33 | vk::MemoryPropertyFlags properties, u64 allocation_size, u32 type) |
| 26 | : device{device}, memory{memory}, properties{properties}, alloc_size{alloc_size}, | 34 | : device{device}, memory{memory}, properties{properties}, allocation_size{allocation_size}, |
| 27 | shifted_type{ShiftType(type)}, is_mappable{properties & | 35 | shifted_type{ShiftType(type)} {} |
| 28 | vk::MemoryPropertyFlagBits::eHostVisible} { | ||
| 29 | if (is_mappable) { | ||
| 30 | const auto dev = device.GetLogical(); | ||
| 31 | const auto& dld = device.GetDispatchLoader(); | ||
| 32 | base_address = static_cast<u8*>(dev.mapMemory(memory, 0, alloc_size, {}, dld)); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | 36 | ||
| 36 | ~VKMemoryAllocation() { | 37 | ~VKMemoryAllocation() { |
| 37 | const auto dev = device.GetLogical(); | 38 | const auto dev = device.GetLogical(); |
| 38 | const auto& dld = device.GetDispatchLoader(); | 39 | const auto& dld = device.GetDispatchLoader(); |
| 39 | if (is_mappable) | ||
| 40 | dev.unmapMemory(memory, dld); | ||
| 41 | dev.free(memory, nullptr, dld); | 40 | dev.free(memory, nullptr, dld); |
| 42 | } | 41 | } |
| 43 | 42 | ||
| 44 | VKMemoryCommit Commit(vk::DeviceSize commit_size, vk::DeviceSize alignment) { | 43 | VKMemoryCommit Commit(vk::DeviceSize commit_size, vk::DeviceSize alignment) { |
| 45 | auto found = TryFindFreeSection(free_iterator, alloc_size, static_cast<u64>(commit_size), | 44 | auto found = TryFindFreeSection(free_iterator, allocation_size, |
| 46 | static_cast<u64>(alignment)); | 45 | static_cast<u64>(commit_size), static_cast<u64>(alignment)); |
| 47 | if (!found) { | 46 | if (!found) { |
| 48 | found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size), | 47 | found = TryFindFreeSection(0, free_iterator, static_cast<u64>(commit_size), |
| 49 | static_cast<u64>(alignment)); | 48 | static_cast<u64>(alignment)); |
| @@ -52,8 +51,7 @@ public: | |||
| 52 | return nullptr; | 51 | return nullptr; |
| 53 | } | 52 | } |
| 54 | } | 53 | } |
| 55 | u8* address = is_mappable ? base_address + *found : nullptr; | 54 | auto commit = std::make_unique<VKMemoryCommitImpl>(device, this, memory, *found, |
| 56 | auto commit = std::make_unique<VKMemoryCommitImpl>(this, memory, address, *found, | ||
| 57 | *found + commit_size); | 55 | *found + commit_size); |
| 58 | commits.push_back(commit.get()); | 56 | commits.push_back(commit.get()); |
| 59 | 57 | ||
| @@ -65,12 +63,10 @@ public: | |||
| 65 | 63 | ||
| 66 | void Free(const VKMemoryCommitImpl* commit) { | 64 | void Free(const VKMemoryCommitImpl* commit) { |
| 67 | ASSERT(commit); | 65 | ASSERT(commit); |
| 68 | const auto it = | 66 | |
| 69 | std::find_if(commits.begin(), commits.end(), | 67 | const auto it = std::find(std::begin(commits), std::end(commits), commit); |
| 70 | [&](const auto& stored_commit) { return stored_commit == commit; }); | ||
| 71 | if (it == commits.end()) { | 68 | if (it == commits.end()) { |
| 72 | LOG_CRITICAL(Render_Vulkan, "Freeing unallocated commit!"); | 69 | UNREACHABLE_MSG("Freeing unallocated commit!"); |
| 73 | UNREACHABLE(); | ||
| 74 | return; | 70 | return; |
| 75 | } | 71 | } |
| 76 | commits.erase(it); | 72 | commits.erase(it); |
| @@ -88,11 +84,11 @@ private: | |||
| 88 | } | 84 | } |
| 89 | 85 | ||
| 90 | /// A memory allocator, it may return a free region between "start" and "end" with the solicited | 86 | /// A memory allocator, it may return a free region between "start" and "end" with the solicited |
| 91 | /// requeriments. | 87 | /// requirements. |
| 92 | std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const { | 88 | std::optional<u64> TryFindFreeSection(u64 start, u64 end, u64 size, u64 alignment) const { |
| 93 | u64 iterator = start; | 89 | u64 iterator = Common::AlignUp(start, alignment); |
| 94 | while (iterator + size < end) { | 90 | while (iterator + size <= end) { |
| 95 | const u64 try_left = Common::AlignUp(iterator, alignment); | 91 | const u64 try_left = iterator; |
| 96 | const u64 try_right = try_left + size; | 92 | const u64 try_right = try_left + size; |
| 97 | 93 | ||
| 98 | bool overlap = false; | 94 | bool overlap = false; |
| @@ -100,7 +96,7 @@ private: | |||
| 100 | const auto [commit_left, commit_right] = commit->interval; | 96 | const auto [commit_left, commit_right] = commit->interval; |
| 101 | if (try_left < commit_right && commit_left < try_right) { | 97 | if (try_left < commit_right && commit_left < try_right) { |
| 102 | // There's an overlap, continue the search where the overlapping commit ends. | 98 | // There's an overlap, continue the search where the overlapping commit ends. |
| 103 | iterator = commit_right; | 99 | iterator = Common::AlignUp(commit_right, alignment); |
| 104 | overlap = true; | 100 | overlap = true; |
| 105 | break; | 101 | break; |
| 106 | } | 102 | } |
| @@ -110,6 +106,7 @@ private: | |||
| 110 | return try_left; | 106 | return try_left; |
| 111 | } | 107 | } |
| 112 | } | 108 | } |
| 109 | |||
| 113 | // No free regions where found, return an empty optional. | 110 | // No free regions where found, return an empty optional. |
| 114 | return std::nullopt; | 111 | return std::nullopt; |
| 115 | } | 112 | } |
| @@ -117,12 +114,8 @@ private: | |||
| 117 | const VKDevice& device; ///< Vulkan device. | 114 | const VKDevice& device; ///< Vulkan device. |
| 118 | const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. | 115 | const vk::DeviceMemory memory; ///< Vulkan memory allocation handler. |
| 119 | const vk::MemoryPropertyFlags properties; ///< Vulkan properties. | 116 | const vk::MemoryPropertyFlags properties; ///< Vulkan properties. |
| 120 | const u64 alloc_size; ///< Size of this allocation. | 117 | const u64 allocation_size; ///< Size of this allocation. |
| 121 | const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted. | 118 | const u32 shifted_type; ///< Stored Vulkan type of this allocation, shifted. |
| 122 | const bool is_mappable; ///< Whether the allocation is mappable. | ||
| 123 | |||
| 124 | /// Base address of the mapped pointer. | ||
| 125 | u8* base_address{}; | ||
| 126 | 119 | ||
| 127 | /// Hints where the next free region is likely going to be. | 120 | /// Hints where the next free region is likely going to be. |
| 128 | u64 free_iterator{}; | 121 | u64 free_iterator{}; |
| @@ -132,13 +125,15 @@ private: | |||
| 132 | }; | 125 | }; |
| 133 | 126 | ||
| 134 | VKMemoryManager::VKMemoryManager(const VKDevice& device) | 127 | VKMemoryManager::VKMemoryManager(const VKDevice& device) |
| 135 | : device{device}, props{device.GetPhysical().getMemoryProperties(device.GetDispatchLoader())}, | 128 | : device{device}, properties{device.GetPhysical().getMemoryProperties( |
| 136 | is_memory_unified{GetMemoryUnified(props)} {} | 129 | device.GetDispatchLoader())}, |
| 130 | is_memory_unified{GetMemoryUnified(properties)} {} | ||
| 137 | 131 | ||
| 138 | VKMemoryManager::~VKMemoryManager() = default; | 132 | VKMemoryManager::~VKMemoryManager() = default; |
| 139 | 133 | ||
| 140 | VKMemoryCommit VKMemoryManager::Commit(const vk::MemoryRequirements& reqs, bool host_visible) { | 134 | VKMemoryCommit VKMemoryManager::Commit(const vk::MemoryRequirements& requirements, |
| 141 | ASSERT(reqs.size < ALLOC_CHUNK_SIZE); | 135 | bool host_visible) { |
| 136 | const u64 chunk_size = GetAllocationChunkSize(requirements.size); | ||
| 142 | 137 | ||
| 143 | // When a host visible commit is asked, search for host visible and coherent, otherwise search | 138 | // When a host visible commit is asked, search for host visible and coherent, otherwise search |
| 144 | // for a fast device local type. | 139 | // for a fast device local type. |
| @@ -147,32 +142,21 @@ VKMemoryCommit VKMemoryManager::Commit(const vk::MemoryRequirements& reqs, bool | |||
| 147 | ? vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | 142 | ? vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent |
| 148 | : vk::MemoryPropertyFlagBits::eDeviceLocal; | 143 | : vk::MemoryPropertyFlagBits::eDeviceLocal; |
| 149 | 144 | ||
| 150 | const auto TryCommit = [&]() -> VKMemoryCommit { | 145 | if (auto commit = TryAllocCommit(requirements, wanted_properties)) { |
| 151 | for (auto& alloc : allocs) { | ||
| 152 | if (!alloc->IsCompatible(wanted_properties, reqs.memoryTypeBits)) | ||
| 153 | continue; | ||
| 154 | |||
| 155 | if (auto commit = alloc->Commit(reqs.size, reqs.alignment); commit) { | ||
| 156 | return commit; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | return {}; | ||
| 160 | }; | ||
| 161 | |||
| 162 | if (auto commit = TryCommit(); commit) { | ||
| 163 | return commit; | 146 | return commit; |
| 164 | } | 147 | } |
| 165 | 148 | ||
| 166 | // Commit has failed, allocate more memory. | 149 | // Commit has failed, allocate more memory. |
| 167 | if (!AllocMemory(wanted_properties, reqs.memoryTypeBits, ALLOC_CHUNK_SIZE)) { | 150 | if (!AllocMemory(wanted_properties, requirements.memoryTypeBits, chunk_size)) { |
| 168 | // TODO(Rodrigo): Try to use host memory. | 151 | // TODO(Rodrigo): Handle these situations in some way like flushing to guest memory. |
| 169 | LOG_CRITICAL(Render_Vulkan, "Ran out of memory!"); | 152 | // Allocation has failed, panic. |
| 170 | UNREACHABLE(); | 153 | UNREACHABLE_MSG("Ran out of VRAM!"); |
| 154 | return {}; | ||
| 171 | } | 155 | } |
| 172 | 156 | ||
| 173 | // Commit again, this time it won't fail since there's a fresh allocation above. If it does, | 157 | // Commit again, this time it won't fail since there's a fresh allocation above. If it does, |
| 174 | // there's a bug. | 158 | // there's a bug. |
| 175 | auto commit = TryCommit(); | 159 | auto commit = TryAllocCommit(requirements, wanted_properties); |
| 176 | ASSERT(commit); | 160 | ASSERT(commit); |
| 177 | return commit; | 161 | return commit; |
| 178 | } | 162 | } |
| @@ -180,8 +164,7 @@ VKMemoryCommit VKMemoryManager::Commit(const vk::MemoryRequirements& reqs, bool | |||
| 180 | VKMemoryCommit VKMemoryManager::Commit(vk::Buffer buffer, bool host_visible) { | 164 | VKMemoryCommit VKMemoryManager::Commit(vk::Buffer buffer, bool host_visible) { |
| 181 | const auto dev = device.GetLogical(); | 165 | const auto dev = device.GetLogical(); |
| 182 | const auto& dld = device.GetDispatchLoader(); | 166 | const auto& dld = device.GetDispatchLoader(); |
| 183 | const auto requeriments = dev.getBufferMemoryRequirements(buffer, dld); | 167 | auto commit = Commit(dev.getBufferMemoryRequirements(buffer, dld), host_visible); |
| 184 | auto commit = Commit(requeriments, host_visible); | ||
| 185 | dev.bindBufferMemory(buffer, commit->GetMemory(), commit->GetOffset(), dld); | 168 | dev.bindBufferMemory(buffer, commit->GetMemory(), commit->GetOffset(), dld); |
| 186 | return commit; | 169 | return commit; |
| 187 | } | 170 | } |
| @@ -189,25 +172,23 @@ VKMemoryCommit VKMemoryManager::Commit(vk::Buffer buffer, bool host_visible) { | |||
| 189 | VKMemoryCommit VKMemoryManager::Commit(vk::Image image, bool host_visible) { | 172 | VKMemoryCommit VKMemoryManager::Commit(vk::Image image, bool host_visible) { |
| 190 | const auto dev = device.GetLogical(); | 173 | const auto dev = device.GetLogical(); |
| 191 | const auto& dld = device.GetDispatchLoader(); | 174 | const auto& dld = device.GetDispatchLoader(); |
| 192 | const auto requeriments = dev.getImageMemoryRequirements(image, dld); | 175 | auto commit = Commit(dev.getImageMemoryRequirements(image, dld), host_visible); |
| 193 | auto commit = Commit(requeriments, host_visible); | ||
| 194 | dev.bindImageMemory(image, commit->GetMemory(), commit->GetOffset(), dld); | 176 | dev.bindImageMemory(image, commit->GetMemory(), commit->GetOffset(), dld); |
| 195 | return commit; | 177 | return commit; |
| 196 | } | 178 | } |
| 197 | 179 | ||
| 198 | bool VKMemoryManager::AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, | 180 | bool VKMemoryManager::AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, |
| 199 | u64 size) { | 181 | u64 size) { |
| 200 | const u32 type = [&]() { | 182 | const u32 type = [&] { |
| 201 | for (u32 type_index = 0; type_index < props.memoryTypeCount; ++type_index) { | 183 | for (u32 type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { |
| 202 | const auto flags = props.memoryTypes[type_index].propertyFlags; | 184 | const auto flags = properties.memoryTypes[type_index].propertyFlags; |
| 203 | if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) { | 185 | if ((type_mask & (1U << type_index)) && (flags & wanted_properties)) { |
| 204 | // The type matches in type and in the wanted properties. | 186 | // The type matches in type and in the wanted properties. |
| 205 | return type_index; | 187 | return type_index; |
| 206 | } | 188 | } |
| 207 | } | 189 | } |
| 208 | LOG_CRITICAL(Render_Vulkan, "Couldn't find a compatible memory type!"); | 190 | UNREACHABLE_MSG("Couldn't find a compatible memory type!"); |
| 209 | UNREACHABLE(); | 191 | return 0U; |
| 210 | return 0u; | ||
| 211 | }(); | 192 | }(); |
| 212 | 193 | ||
| 213 | const auto dev = device.GetLogical(); | 194 | const auto dev = device.GetLogical(); |
| @@ -216,19 +197,33 @@ bool VKMemoryManager::AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 | |||
| 216 | // Try to allocate found type. | 197 | // Try to allocate found type. |
| 217 | const vk::MemoryAllocateInfo memory_ai(size, type); | 198 | const vk::MemoryAllocateInfo memory_ai(size, type); |
| 218 | vk::DeviceMemory memory; | 199 | vk::DeviceMemory memory; |
| 219 | if (const vk::Result res = dev.allocateMemory(&memory_ai, nullptr, &memory, dld); | 200 | if (const auto res = dev.allocateMemory(&memory_ai, nullptr, &memory, dld); |
| 220 | res != vk::Result::eSuccess) { | 201 | res != vk::Result::eSuccess) { |
| 221 | LOG_CRITICAL(Render_Vulkan, "Device allocation failed with code {}!", vk::to_string(res)); | 202 | LOG_CRITICAL(Render_Vulkan, "Device allocation failed with code {}!", vk::to_string(res)); |
| 222 | return false; | 203 | return false; |
| 223 | } | 204 | } |
| 224 | allocs.push_back( | 205 | allocations.push_back( |
| 225 | std::make_unique<VKMemoryAllocation>(device, memory, wanted_properties, size, type)); | 206 | std::make_unique<VKMemoryAllocation>(device, memory, wanted_properties, size, type)); |
| 226 | return true; | 207 | return true; |
| 227 | } | 208 | } |
| 228 | 209 | ||
| 229 | /*static*/ bool VKMemoryManager::GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& props) { | 210 | VKMemoryCommit VKMemoryManager::TryAllocCommit(const vk::MemoryRequirements& requirements, |
| 230 | for (u32 heap_index = 0; heap_index < props.memoryHeapCount; ++heap_index) { | 211 | vk::MemoryPropertyFlags wanted_properties) { |
| 231 | if (!(props.memoryHeaps[heap_index].flags & vk::MemoryHeapFlagBits::eDeviceLocal)) { | 212 | for (auto& allocation : allocations) { |
| 213 | if (!allocation->IsCompatible(wanted_properties, requirements.memoryTypeBits)) { | ||
| 214 | continue; | ||
| 215 | } | ||
| 216 | if (auto commit = allocation->Commit(requirements.size, requirements.alignment)) { | ||
| 217 | return commit; | ||
| 218 | } | ||
| 219 | } | ||
| 220 | return {}; | ||
| 221 | } | ||
| 222 | |||
| 223 | /*static*/ bool VKMemoryManager::GetMemoryUnified( | ||
| 224 | const vk::PhysicalDeviceMemoryProperties& properties) { | ||
| 225 | for (u32 heap_index = 0; heap_index < properties.memoryHeapCount; ++heap_index) { | ||
| 226 | if (!(properties.memoryHeaps[heap_index].flags & vk::MemoryHeapFlagBits::eDeviceLocal)) { | ||
| 232 | // Memory is considered unified when heaps are device local only. | 227 | // Memory is considered unified when heaps are device local only. |
| 233 | return false; | 228 | return false; |
| 234 | } | 229 | } |
| @@ -236,17 +231,28 @@ bool VKMemoryManager::AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 | |||
| 236 | return true; | 231 | return true; |
| 237 | } | 232 | } |
| 238 | 233 | ||
| 239 | VKMemoryCommitImpl::VKMemoryCommitImpl(VKMemoryAllocation* allocation, vk::DeviceMemory memory, | 234 | VKMemoryCommitImpl::VKMemoryCommitImpl(const VKDevice& device, VKMemoryAllocation* allocation, |
| 240 | u8* data, u64 begin, u64 end) | 235 | vk::DeviceMemory memory, u64 begin, u64 end) |
| 241 | : interval(std::make_pair(begin, end)), memory{memory}, allocation{allocation}, data{data} {} | 236 | : device{device}, interval{begin, end}, memory{memory}, allocation{allocation} {} |
| 242 | 237 | ||
| 243 | VKMemoryCommitImpl::~VKMemoryCommitImpl() { | 238 | VKMemoryCommitImpl::~VKMemoryCommitImpl() { |
| 244 | allocation->Free(this); | 239 | allocation->Free(this); |
| 245 | } | 240 | } |
| 246 | 241 | ||
| 247 | u8* VKMemoryCommitImpl::GetData() const { | 242 | MemoryMap VKMemoryCommitImpl::Map(u64 size, u64 offset_) const { |
| 248 | ASSERT_MSG(data != nullptr, "Trying to access an unmapped commit."); | 243 | const auto dev = device.GetLogical(); |
| 249 | return data; | 244 | const auto address = reinterpret_cast<u8*>( |
| 245 | dev.mapMemory(memory, interval.first + offset_, size, {}, device.GetDispatchLoader())); | ||
| 246 | return MemoryMap{this, address}; | ||
| 247 | } | ||
| 248 | |||
| 249 | void VKMemoryCommitImpl::Unmap() const { | ||
| 250 | const auto dev = device.GetLogical(); | ||
| 251 | dev.unmapMemory(memory, device.GetDispatchLoader()); | ||
| 252 | } | ||
| 253 | |||
| 254 | MemoryMap VKMemoryCommitImpl::Map() const { | ||
| 255 | return Map(interval.second - interval.first); | ||
| 250 | } | 256 | } |
| 251 | 257 | ||
| 252 | } // namespace Vulkan | 258 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_memory_manager.h b/src/video_core/renderer_vulkan/vk_memory_manager.h index 073597b35..cd00bb91b 100644 --- a/src/video_core/renderer_vulkan/vk_memory_manager.h +++ b/src/video_core/renderer_vulkan/vk_memory_manager.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | namespace Vulkan { | 13 | namespace Vulkan { |
| 14 | 14 | ||
| 15 | class MemoryMap; | ||
| 15 | class VKDevice; | 16 | class VKDevice; |
| 16 | class VKMemoryAllocation; | 17 | class VKMemoryAllocation; |
| 17 | class VKMemoryCommitImpl; | 18 | class VKMemoryCommitImpl; |
| @@ -21,13 +22,14 @@ using VKMemoryCommit = std::unique_ptr<VKMemoryCommitImpl>; | |||
| 21 | class VKMemoryManager final { | 22 | class VKMemoryManager final { |
| 22 | public: | 23 | public: |
| 23 | explicit VKMemoryManager(const VKDevice& device); | 24 | explicit VKMemoryManager(const VKDevice& device); |
| 25 | VKMemoryManager(const VKMemoryManager&) = delete; | ||
| 24 | ~VKMemoryManager(); | 26 | ~VKMemoryManager(); |
| 25 | 27 | ||
| 26 | /** | 28 | /** |
| 27 | * Commits a memory with the specified requeriments. | 29 | * Commits a memory with the specified requeriments. |
| 28 | * @param reqs Requeriments returned from a Vulkan call. | 30 | * @param requirements Requirements returned from a Vulkan call. |
| 29 | * @param host_visible Signals the allocator that it *must* use host visible and coherent | 31 | * @param host_visible Signals the allocator that it *must* use host visible and coherent |
| 30 | * memory. When passing false, it will try to allocate device local memory. | 32 | * memory. When passing false, it will try to allocate device local memory. |
| 31 | * @returns A memory commit. | 33 | * @returns A memory commit. |
| 32 | */ | 34 | */ |
| 33 | VKMemoryCommit Commit(const vk::MemoryRequirements& reqs, bool host_visible); | 35 | VKMemoryCommit Commit(const vk::MemoryRequirements& reqs, bool host_visible); |
| @@ -47,25 +49,35 @@ private: | |||
| 47 | /// Allocates a chunk of memory. | 49 | /// Allocates a chunk of memory. |
| 48 | bool AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, u64 size); | 50 | bool AllocMemory(vk::MemoryPropertyFlags wanted_properties, u32 type_mask, u64 size); |
| 49 | 51 | ||
| 52 | /// Tries to allocate a memory commit. | ||
| 53 | VKMemoryCommit TryAllocCommit(const vk::MemoryRequirements& requirements, | ||
| 54 | vk::MemoryPropertyFlags wanted_properties); | ||
| 55 | |||
| 50 | /// Returns true if the device uses an unified memory model. | 56 | /// Returns true if the device uses an unified memory model. |
| 51 | static bool GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& props); | 57 | static bool GetMemoryUnified(const vk::PhysicalDeviceMemoryProperties& properties); |
| 52 | 58 | ||
| 53 | const VKDevice& device; ///< Device handler. | 59 | const VKDevice& device; ///< Device handler. |
| 54 | const vk::PhysicalDeviceMemoryProperties props; ///< Physical device properties. | 60 | const vk::PhysicalDeviceMemoryProperties properties; ///< Physical device properties. |
| 55 | const bool is_memory_unified; ///< True if memory model is unified. | 61 | const bool is_memory_unified; ///< True if memory model is unified. |
| 56 | std::vector<std::unique_ptr<VKMemoryAllocation>> allocs; ///< Current allocations. | 62 | std::vector<std::unique_ptr<VKMemoryAllocation>> allocations; ///< Current allocations. |
| 57 | }; | 63 | }; |
| 58 | 64 | ||
| 59 | class VKMemoryCommitImpl final { | 65 | class VKMemoryCommitImpl final { |
| 60 | friend VKMemoryAllocation; | 66 | friend VKMemoryAllocation; |
| 67 | friend MemoryMap; | ||
| 61 | 68 | ||
| 62 | public: | 69 | public: |
| 63 | explicit VKMemoryCommitImpl(VKMemoryAllocation* allocation, vk::DeviceMemory memory, u8* data, | 70 | explicit VKMemoryCommitImpl(const VKDevice& device, VKMemoryAllocation* allocation, |
| 64 | u64 begin, u64 end); | 71 | vk::DeviceMemory memory, u64 begin, u64 end); |
| 65 | ~VKMemoryCommitImpl(); | 72 | ~VKMemoryCommitImpl(); |
| 66 | 73 | ||
| 67 | /// Returns the writeable memory map. The commit has to be mappable. | 74 | /// Maps a memory region and returns a pointer to it. |
| 68 | u8* GetData() const; | 75 | /// It's illegal to have more than one memory map at the same time. |
| 76 | MemoryMap Map(u64 size, u64 offset = 0) const; | ||
| 77 | |||
| 78 | /// Maps the whole commit and returns a pointer to it. | ||
| 79 | /// It's illegal to have more than one memory map at the same time. | ||
| 80 | MemoryMap Map() const; | ||
| 69 | 81 | ||
| 70 | /// Returns the Vulkan memory handler. | 82 | /// Returns the Vulkan memory handler. |
| 71 | vk::DeviceMemory GetMemory() const { | 83 | vk::DeviceMemory GetMemory() const { |
| @@ -78,10 +90,46 @@ public: | |||
| 78 | } | 90 | } |
| 79 | 91 | ||
| 80 | private: | 92 | private: |
| 93 | /// Unmaps memory. | ||
| 94 | void Unmap() const; | ||
| 95 | |||
| 96 | const VKDevice& device; ///< Vulkan device. | ||
| 81 | std::pair<u64, u64> interval{}; ///< Interval where the commit exists. | 97 | std::pair<u64, u64> interval{}; ///< Interval where the commit exists. |
| 82 | vk::DeviceMemory memory; ///< Vulkan device memory handler. | 98 | vk::DeviceMemory memory; ///< Vulkan device memory handler. |
| 83 | VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. | 99 | VKMemoryAllocation* allocation{}; ///< Pointer to the large memory allocation. |
| 84 | u8* data{}; ///< Pointer to the host mapped memory, it has the commit offset included. | 100 | }; |
| 101 | |||
| 102 | /// Holds ownership of a memory map. | ||
| 103 | class MemoryMap final { | ||
| 104 | public: | ||
| 105 | explicit MemoryMap(const VKMemoryCommitImpl* commit, u8* address) | ||
| 106 | : commit{commit}, address{address} {} | ||
| 107 | |||
| 108 | ~MemoryMap() { | ||
| 109 | if (commit) { | ||
| 110 | commit->Unmap(); | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | /// Prematurely releases the memory map. | ||
| 115 | void Release() { | ||
| 116 | commit->Unmap(); | ||
| 117 | commit = nullptr; | ||
| 118 | } | ||
| 119 | |||
| 120 | /// Returns the address of the memory map. | ||
| 121 | u8* GetAddress() const { | ||
| 122 | return address; | ||
| 123 | } | ||
| 124 | |||
| 125 | /// Returns the address of the memory map; | ||
| 126 | operator u8*() const { | ||
| 127 | return address; | ||
| 128 | } | ||
| 129 | |||
| 130 | private: | ||
| 131 | const VKMemoryCommitImpl* commit{}; ///< Mapped memory commit. | ||
| 132 | u8* address{}; ///< Address to the mapped memory. | ||
| 85 | }; | 133 | }; |
| 86 | 134 | ||
| 87 | } // namespace Vulkan | 135 | } // namespace Vulkan |