Kaushik Iska | ff05a31 | 2022-10-27 16:57:54 -0400 | [diff] [blame] | 1 | // Copyright 2013 The Flutter Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "flutter/flutter_vma/flutter_skia_vma.h" |
| 6 | |
| 7 | #include "flutter/fml/memory/ref_ptr.h" |
| 8 | #include "flutter/vulkan/procs/vulkan_handle.h" |
| 9 | #include "flutter/vulkan/procs/vulkan_proc_table.h" |
| 10 | |
| 11 | namespace flutter { |
| 12 | |
| 13 | sk_sp<skgpu::VulkanMemoryAllocator> FlutterSkiaVulkanMemoryAllocator::Make( |
| 14 | uint32_t vulkan_api_version, |
| 15 | VkInstance instance, |
| 16 | VkPhysicalDevice physicalDevice, |
| 17 | VkDevice device, |
| 18 | const fml::RefPtr<vulkan::VulkanProcTable>& vk, |
| 19 | bool mustUseCoherentHostVisibleMemory) { |
| 20 | #define PROVIDE_PROC(tbl, proc, provider) tbl.vk##proc = provider->proc; |
| 21 | |
| 22 | VmaVulkanFunctions proc_table = {}; |
| 23 | proc_table.vkGetInstanceProcAddr = vk->NativeGetInstanceProcAddr(); |
| 24 | PROVIDE_PROC(proc_table, GetDeviceProcAddr, vk); |
| 25 | PROVIDE_PROC(proc_table, GetPhysicalDeviceProperties, vk); |
| 26 | PROVIDE_PROC(proc_table, GetPhysicalDeviceMemoryProperties, vk); |
| 27 | PROVIDE_PROC(proc_table, AllocateMemory, vk); |
| 28 | PROVIDE_PROC(proc_table, FreeMemory, vk); |
| 29 | PROVIDE_PROC(proc_table, MapMemory, vk); |
| 30 | PROVIDE_PROC(proc_table, UnmapMemory, vk); |
| 31 | PROVIDE_PROC(proc_table, FlushMappedMemoryRanges, vk); |
| 32 | PROVIDE_PROC(proc_table, InvalidateMappedMemoryRanges, vk); |
| 33 | PROVIDE_PROC(proc_table, BindBufferMemory, vk); |
| 34 | PROVIDE_PROC(proc_table, BindImageMemory, vk); |
| 35 | PROVIDE_PROC(proc_table, GetBufferMemoryRequirements, vk); |
| 36 | PROVIDE_PROC(proc_table, GetImageMemoryRequirements, vk); |
| 37 | PROVIDE_PROC(proc_table, CreateBuffer, vk); |
| 38 | PROVIDE_PROC(proc_table, DestroyBuffer, vk); |
| 39 | PROVIDE_PROC(proc_table, CreateImage, vk); |
| 40 | PROVIDE_PROC(proc_table, DestroyImage, vk); |
| 41 | PROVIDE_PROC(proc_table, CmdCopyBuffer, vk); |
| 42 | |
| 43 | #define PROVIDE_PROC_COALESCE(tbl, proc, provider) \ |
| 44 | tbl.vk##proc##KHR = provider->proc ? provider->proc : provider->proc##KHR; |
| 45 | // See the following link for why we have to pick either KHR version or |
| 46 | // promoted non-KHR version: |
| 47 | // https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/issues/203 |
| 48 | PROVIDE_PROC_COALESCE(proc_table, GetBufferMemoryRequirements2, vk); |
| 49 | PROVIDE_PROC_COALESCE(proc_table, GetImageMemoryRequirements2, vk); |
| 50 | PROVIDE_PROC_COALESCE(proc_table, BindBufferMemory2, vk); |
| 51 | PROVIDE_PROC_COALESCE(proc_table, BindImageMemory2, vk); |
| 52 | PROVIDE_PROC_COALESCE(proc_table, GetPhysicalDeviceMemoryProperties2, vk); |
| 53 | #undef PROVIDE_PROC_COALESCE |
| 54 | |
| 55 | #undef PROVIDE_PROC |
| 56 | |
| 57 | VmaAllocatorCreateInfo allocator_info = {}; |
| 58 | allocator_info.vulkanApiVersion = vulkan_api_version; |
| 59 | allocator_info.physicalDevice = physicalDevice; |
| 60 | allocator_info.device = device; |
| 61 | allocator_info.instance = instance; |
| 62 | allocator_info.pVulkanFunctions = &proc_table; |
| 63 | |
| 64 | VmaAllocator allocator; |
| 65 | vmaCreateAllocator(&allocator_info, &allocator); |
| 66 | |
| 67 | return sk_sp<FlutterSkiaVulkanMemoryAllocator>( |
| 68 | new FlutterSkiaVulkanMemoryAllocator(vk, allocator, |
| 69 | mustUseCoherentHostVisibleMemory)); |
| 70 | } |
| 71 | |
| 72 | FlutterSkiaVulkanMemoryAllocator::FlutterSkiaVulkanMemoryAllocator( |
| 73 | fml::RefPtr<vulkan::VulkanProcTable> vk_proc_table, |
| 74 | VmaAllocator allocator, |
| 75 | bool mustUseCoherentHostVisibleMemory) |
| 76 | : vk_proc_table_(std::move(vk_proc_table)), |
| 77 | allocator_(allocator), |
| 78 | must_use_coherent_host_visible_memory_(mustUseCoherentHostVisibleMemory) { |
| 79 | } |
| 80 | |
| 81 | FlutterSkiaVulkanMemoryAllocator::~FlutterSkiaVulkanMemoryAllocator() { |
| 82 | vmaDestroyAllocator(allocator_); |
| 83 | allocator_ = VK_NULL_HANDLE; |
| 84 | } |
| 85 | |
| 86 | VkResult FlutterSkiaVulkanMemoryAllocator::allocateImageMemory( |
| 87 | VkImage image, |
| 88 | uint32_t allocationPropertyFlags, |
| 89 | skgpu::VulkanBackendMemory* backendMemory) { |
| 90 | VmaAllocationCreateInfo info; |
| 91 | info.flags = 0; |
| 92 | info.usage = VMA_MEMORY_USAGE_UNKNOWN; |
| 93 | info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| 94 | info.preferredFlags = 0; |
| 95 | info.memoryTypeBits = 0; |
| 96 | info.pool = VK_NULL_HANDLE; |
| 97 | info.pUserData = nullptr; |
| 98 | |
| 99 | if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) { |
| 100 | info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
| 101 | } |
| 102 | if (kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) { |
| 103 | info.requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; |
| 104 | } |
| 105 | if (kProtected_AllocationPropertyFlag & allocationPropertyFlags) { |
| 106 | info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT; |
| 107 | } |
| 108 | |
| 109 | VmaAllocation allocation; |
| 110 | VkResult result = |
| 111 | vmaAllocateMemoryForImage(allocator_, image, &info, &allocation, nullptr); |
| 112 | if (VK_SUCCESS == result) { |
| 113 | *backendMemory = reinterpret_cast<skgpu::VulkanBackendMemory>(allocation); |
| 114 | } |
| 115 | return result; |
| 116 | } |
| 117 | |
| 118 | VkResult FlutterSkiaVulkanMemoryAllocator::allocateBufferMemory( |
| 119 | VkBuffer buffer, |
| 120 | BufferUsage usage, |
| 121 | uint32_t allocationPropertyFlags, |
| 122 | skgpu::VulkanBackendMemory* backendMemory) { |
| 123 | VmaAllocationCreateInfo info; |
| 124 | info.flags = 0; |
| 125 | info.usage = VMA_MEMORY_USAGE_UNKNOWN; |
| 126 | info.memoryTypeBits = 0; |
| 127 | info.pool = VK_NULL_HANDLE; |
| 128 | info.pUserData = nullptr; |
| 129 | |
| 130 | switch (usage) { |
| 131 | case BufferUsage::kGpuOnly: |
| 132 | info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| 133 | info.preferredFlags = 0; |
| 134 | break; |
| 135 | case BufferUsage::kCpuWritesGpuReads: |
| 136 | // When doing cpu writes and gpu reads the general rule of thumb is to use |
| 137 | // coherent memory. Though this depends on the fact that we are not doing |
| 138 | // any cpu reads and the cpu writes are sequential. For sparse writes we'd |
| 139 | // want cpu cached memory, however we don't do these types of writes in |
| 140 | // Skia. |
| 141 | // |
| 142 | // TODO (kaushikiska): In the future there may be times where specific |
| 143 | // types of memory could benefit from a coherent and cached memory. |
| 144 | // Typically these allow for the gpu to read cpu writes from the cache |
| 145 | // without needing to flush the writes throughout the cache. The reverse |
| 146 | // is not true and GPU writes tend to invalidate the cache regardless. |
| 147 | // Also these gpu cache read access are typically lower bandwidth than |
| 148 | // non-cached memory. For now Skia doesn't really have a need or want of |
| 149 | // this type of memory. But if we ever do we could pass in an |
| 150 | // AllocationPropertyFlag that requests the cached property. |
| 151 | info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| 152 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| 153 | info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| 154 | break; |
| 155 | case BufferUsage::kTransfersFromCpuToGpu: |
| 156 | info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| 157 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| 158 | info.preferredFlags = 0; |
| 159 | break; |
| 160 | case BufferUsage::kTransfersFromGpuToCpu: |
| 161 | info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
| 162 | info.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
| 163 | break; |
| 164 | } |
| 165 | |
| 166 | if (must_use_coherent_host_visible_memory_ && |
| 167 | (info.requiredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) { |
| 168 | info.requiredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| 169 | } |
| 170 | if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) { |
| 171 | info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
| 172 | } |
| 173 | if ((kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) && |
| 174 | BufferUsage::kGpuOnly == usage) { |
| 175 | info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; |
| 176 | } |
| 177 | |
| 178 | if (kPersistentlyMapped_AllocationPropertyFlag & allocationPropertyFlags) { |
| 179 | SkASSERT(BufferUsage::kGpuOnly != usage); |
| 180 | info.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; |
| 181 | } |
| 182 | |
| 183 | VmaAllocation allocation; |
| 184 | VkResult result = vmaAllocateMemoryForBuffer(allocator_, buffer, &info, |
| 185 | &allocation, nullptr); |
| 186 | if (VK_SUCCESS == result) { |
| 187 | *backendMemory = reinterpret_cast<skgpu::VulkanBackendMemory>(allocation); |
| 188 | } |
| 189 | |
| 190 | return result; |
| 191 | } |
| 192 | |
| 193 | void FlutterSkiaVulkanMemoryAllocator::freeMemory( |
| 194 | const skgpu::VulkanBackendMemory& memoryHandle) { |
| 195 | const VmaAllocation allocation = |
| 196 | reinterpret_cast<const VmaAllocation>(memoryHandle); |
| 197 | vmaFreeMemory(allocator_, allocation); |
| 198 | } |
| 199 | |
| 200 | void FlutterSkiaVulkanMemoryAllocator::getAllocInfo( |
| 201 | const skgpu::VulkanBackendMemory& memoryHandle, |
| 202 | skgpu::VulkanAlloc* alloc) const { |
| 203 | const VmaAllocation allocation = |
| 204 | reinterpret_cast<const VmaAllocation>(memoryHandle); |
| 205 | VmaAllocationInfo vmaInfo; |
| 206 | vmaGetAllocationInfo(allocator_, allocation, &vmaInfo); |
| 207 | |
| 208 | VkMemoryPropertyFlags memFlags; |
| 209 | vmaGetMemoryTypeProperties(allocator_, vmaInfo.memoryType, &memFlags); |
| 210 | |
| 211 | uint32_t flags = 0; |
| 212 | if (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT & memFlags) { |
| 213 | flags |= skgpu::VulkanAlloc::kMappable_Flag; |
| 214 | } |
Kevin Lubick | 24a6a91 | 2022-12-19 14:22:05 -0500 | [diff] [blame] | 215 | if (!(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) { |
Kaushik Iska | ff05a31 | 2022-10-27 16:57:54 -0400 | [diff] [blame] | 216 | flags |= skgpu::VulkanAlloc::kNoncoherent_Flag; |
| 217 | } |
| 218 | if (VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT & memFlags) { |
| 219 | flags |= skgpu::VulkanAlloc::kLazilyAllocated_Flag; |
| 220 | } |
| 221 | |
| 222 | alloc->fMemory = vmaInfo.deviceMemory; |
| 223 | alloc->fOffset = vmaInfo.offset; |
| 224 | alloc->fSize = vmaInfo.size; |
| 225 | alloc->fFlags = flags; |
| 226 | alloc->fBackendMemory = memoryHandle; |
| 227 | } |
| 228 | |
| 229 | VkResult FlutterSkiaVulkanMemoryAllocator::mapMemory( |
| 230 | const skgpu::VulkanBackendMemory& memoryHandle, |
| 231 | void** data) { |
| 232 | const VmaAllocation allocation = |
| 233 | reinterpret_cast<const VmaAllocation>(memoryHandle); |
| 234 | return vmaMapMemory(allocator_, allocation, data); |
| 235 | } |
| 236 | |
| 237 | void FlutterSkiaVulkanMemoryAllocator::unmapMemory( |
| 238 | const skgpu::VulkanBackendMemory& memoryHandle) { |
| 239 | const VmaAllocation allocation = |
| 240 | reinterpret_cast<const VmaAllocation>(memoryHandle); |
| 241 | vmaUnmapMemory(allocator_, allocation); |
| 242 | } |
| 243 | |
| 244 | VkResult FlutterSkiaVulkanMemoryAllocator::flushMemory( |
| 245 | const skgpu::VulkanBackendMemory& memoryHandle, |
| 246 | VkDeviceSize offset, |
| 247 | VkDeviceSize size) { |
| 248 | const VmaAllocation allocation = |
| 249 | reinterpret_cast<const VmaAllocation>(memoryHandle); |
| 250 | return vmaFlushAllocation(allocator_, allocation, offset, size); |
| 251 | } |
| 252 | |
| 253 | VkResult FlutterSkiaVulkanMemoryAllocator::invalidateMemory( |
| 254 | const skgpu::VulkanBackendMemory& memoryHandle, |
| 255 | VkDeviceSize offset, |
| 256 | VkDeviceSize size) { |
| 257 | const VmaAllocation allocation = |
| 258 | reinterpret_cast<const VmaAllocation>(memoryHandle); |
| 259 | return vmaInvalidateAllocation(allocator_, allocation, offset, size); |
| 260 | } |
| 261 | |
Jason Simmons | b5d19f8 | 2022-12-13 11:52:32 -0800 | [diff] [blame] | 262 | std::pair<uint64_t, uint64_t> |
| 263 | FlutterSkiaVulkanMemoryAllocator::totalAllocatedAndUsedMemory() const { |
Kaushik Iska | ff05a31 | 2022-10-27 16:57:54 -0400 | [diff] [blame] | 264 | VmaTotalStatistics stats; |
| 265 | vmaCalculateStatistics(allocator_, &stats); |
Jason Simmons | b5d19f8 | 2022-12-13 11:52:32 -0800 | [diff] [blame] | 266 | return {stats.total.statistics.blockBytes, |
| 267 | stats.total.statistics.allocationBytes}; |
Kaushik Iska | ff05a31 | 2022-10-27 16:57:54 -0400 | [diff] [blame] | 268 | } |
| 269 | |
| 270 | } // namespace flutter |