|
| 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 | + } |
| 215 | + if (!SkToBool(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) { |
| 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 | + |
| 262 | +uint64_t FlutterSkiaVulkanMemoryAllocator::totalUsedMemory() const { |
| 263 | + VmaTotalStatistics stats; |
| 264 | + vmaCalculateStatistics(allocator_, &stats); |
| 265 | + return stats.total.statistics.allocationBytes; |
| 266 | +} |
| 267 | + |
| 268 | +uint64_t FlutterSkiaVulkanMemoryAllocator::totalAllocatedMemory() const { |
| 269 | + VmaTotalStatistics stats; |
| 270 | + vmaCalculateStatistics(allocator_, &stats); |
| 271 | + return stats.total.statistics.blockBytes; |
| 272 | +} |
| 273 | + |
| 274 | +} // namespace flutter |
0 commit comments