blob: c6437fa0c265263586533f28a436345fa6e6c89b [file] [log] [blame]
Kaushik Iskaff05a312022-10-27 16:57:54 -04001// 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
11namespace flutter {
12
13sk_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
72FlutterSkiaVulkanMemoryAllocator::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
81FlutterSkiaVulkanMemoryAllocator::~FlutterSkiaVulkanMemoryAllocator() {
82 vmaDestroyAllocator(allocator_);
83 allocator_ = VK_NULL_HANDLE;
84}
85
86VkResult 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
118VkResult 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
193void FlutterSkiaVulkanMemoryAllocator::freeMemory(
194 const skgpu::VulkanBackendMemory& memoryHandle) {
195 const VmaAllocation allocation =
196 reinterpret_cast<const VmaAllocation>(memoryHandle);
197 vmaFreeMemory(allocator_, allocation);
198}
199
200void 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 Lubick24a6a912022-12-19 14:22:05 -0500215 if (!(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) {
Kaushik Iskaff05a312022-10-27 16:57:54 -0400216 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
229VkResult 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
237void FlutterSkiaVulkanMemoryAllocator::unmapMemory(
238 const skgpu::VulkanBackendMemory& memoryHandle) {
239 const VmaAllocation allocation =
240 reinterpret_cast<const VmaAllocation>(memoryHandle);
241 vmaUnmapMemory(allocator_, allocation);
242}
243
244VkResult 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
253VkResult 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 Simmonsb5d19f82022-12-13 11:52:32 -0800262std::pair<uint64_t, uint64_t>
263FlutterSkiaVulkanMemoryAllocator::totalAllocatedAndUsedMemory() const {
Kaushik Iskaff05a312022-10-27 16:57:54 -0400264 VmaTotalStatistics stats;
265 vmaCalculateStatistics(allocator_, &stats);
Jason Simmonsb5d19f82022-12-13 11:52:32 -0800266 return {stats.total.statistics.blockBytes,
267 stats.total.statistics.allocationBytes};
Kaushik Iskaff05a312022-10-27 16:57:54 -0400268}
269
270} // namespace flutter