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(pCreateInfo: &allocator_info, pAllocator: &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: 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: allocator_, image, pCreateInfo: &info, pAllocation: &allocation, pAllocationInfo: 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: allocator_, buffer, pCreateInfo: &info, |
185 | pAllocation: &allocation, pAllocationInfo: 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: 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: allocator_, allocation, pAllocationInfo: &vmaInfo); |
207 | |
208 | VkMemoryPropertyFlags memFlags; |
209 | vmaGetMemoryTypeProperties(allocator: allocator_, memoryTypeIndex: vmaInfo.memoryType, pFlags: &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 (!(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: allocator_, allocation, ppData: 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: 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: 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: allocator_, allocation, offset, size); |
260 | } |
261 | |
262 | std::pair<uint64_t, uint64_t> |
263 | FlutterSkiaVulkanMemoryAllocator::totalAllocatedAndUsedMemory() const { |
264 | VmaTotalStatistics stats; |
265 | vmaCalculateStatistics(allocator: allocator_, pStats: &stats); |
266 | return {stats.total.statistics.blockBytes, |
267 | stats.total.statistics.allocationBytes}; |
268 | } |
269 | |
270 | } // namespace flutter |
271 | |