xref: /aosp_15_r20/external/skia/src/gpu/vk/vulkanmemoryallocator/VulkanAMDMemoryAllocator.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/vk/vulkanmemoryallocator/VulkanAMDMemoryAllocator.h"
9 
10 #include "include/gpu/vk/VulkanBackendContext.h"
11 #include "include/gpu/vk/VulkanExtensions.h"
12 #include "include/private/base/SkAssert.h"
13 #include "include/private/base/SkTo.h"
14 #include "src/core/SkTraceEvent.h"
15 #include "src/gpu/GpuTypesPriv.h"
16 #include "src/gpu/vk/VulkanInterface.h"
17 #include "src/gpu/vk/VulkanUtilsPriv.h"
18 #include "src/gpu/vk/vulkanmemoryallocator/VulkanMemoryAllocatorPriv.h"
19 
20 #include <algorithm>
21 #include <cstring>
22 
23 namespace skgpu {
24 
Make(VkInstance instance,VkPhysicalDevice physicalDevice,VkDevice device,uint32_t physicalDeviceVersion,const VulkanExtensions * extensions,const VulkanInterface * interface,ThreadSafe threadSafe,std::optional<VkDeviceSize> blockSize)25 sk_sp<VulkanMemoryAllocator> VulkanAMDMemoryAllocator::Make(VkInstance instance,
26                                                             VkPhysicalDevice physicalDevice,
27                                                             VkDevice device,
28                                                             uint32_t physicalDeviceVersion,
29                                                             const VulkanExtensions* extensions,
30                                                             const VulkanInterface* interface,
31                                                             ThreadSafe threadSafe,
32                                                             std::optional<VkDeviceSize> blockSize) {
33 #define SKGPU_COPY_FUNCTION(NAME) functions.vk##NAME = interface->fFunctions.f##NAME
34 #define SKGPU_COPY_FUNCTION_KHR(NAME) functions.vk##NAME##KHR = interface->fFunctions.f##NAME
35 
36     VmaVulkanFunctions functions;
37     // We should be setting all the required functions (at least through vulkan 1.1), but this is
38     // just extra belt and suspenders to make sure there isn't unitialized values here.
39     std::memset(&functions, 0, sizeof(VmaVulkanFunctions));
40 
41     // We don't use dynamic function getting in the allocator so we set the getProc functions to
42     // null.
43     functions.vkGetInstanceProcAddr = nullptr;
44     functions.vkGetDeviceProcAddr = nullptr;
45     SKGPU_COPY_FUNCTION(GetPhysicalDeviceProperties);
46     SKGPU_COPY_FUNCTION(GetPhysicalDeviceMemoryProperties);
47     SKGPU_COPY_FUNCTION(AllocateMemory);
48     SKGPU_COPY_FUNCTION(FreeMemory);
49     SKGPU_COPY_FUNCTION(MapMemory);
50     SKGPU_COPY_FUNCTION(UnmapMemory);
51     SKGPU_COPY_FUNCTION(FlushMappedMemoryRanges);
52     SKGPU_COPY_FUNCTION(InvalidateMappedMemoryRanges);
53     SKGPU_COPY_FUNCTION(BindBufferMemory);
54     SKGPU_COPY_FUNCTION(BindImageMemory);
55     SKGPU_COPY_FUNCTION(GetBufferMemoryRequirements);
56     SKGPU_COPY_FUNCTION(GetImageMemoryRequirements);
57     SKGPU_COPY_FUNCTION(CreateBuffer);
58     SKGPU_COPY_FUNCTION(DestroyBuffer);
59     SKGPU_COPY_FUNCTION(CreateImage);
60     SKGPU_COPY_FUNCTION(DestroyImage);
61     SKGPU_COPY_FUNCTION(CmdCopyBuffer);
62     SKGPU_COPY_FUNCTION_KHR(GetBufferMemoryRequirements2);
63     SKGPU_COPY_FUNCTION_KHR(GetImageMemoryRequirements2);
64     SKGPU_COPY_FUNCTION_KHR(BindBufferMemory2);
65     SKGPU_COPY_FUNCTION_KHR(BindImageMemory2);
66     SKGPU_COPY_FUNCTION_KHR(GetPhysicalDeviceMemoryProperties2);
67 
68     VmaAllocatorCreateInfo info;
69     info.flags = 0;
70     if (threadSafe == ThreadSafe::kNo) {
71         info.flags |= VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT;
72     }
73     if (physicalDeviceVersion >= VK_MAKE_VERSION(1, 1, 0) ||
74         (extensions->hasExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, 1) &&
75          extensions->hasExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, 1))) {
76         info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
77     }
78 
79     info.physicalDevice = physicalDevice;
80     info.device = device;
81     // 4MB was picked for the size here by looking at memory usage of Android apps and runs of DM.
82     // It seems to be a good compromise of not wasting unused allocated space and not making too
83     // many small allocations. The AMD allocator will start making blocks at 1/8 the max size and
84     // builds up block size as needed before capping at the max set here.
85     info.preferredLargeHeapBlockSize = blockSize.value_or(4 * 1024 * 1024);
86     info.pAllocationCallbacks = nullptr;
87     info.pDeviceMemoryCallbacks = nullptr;
88     info.pHeapSizeLimit = nullptr;
89     info.pVulkanFunctions = &functions;
90     info.instance = instance;
91     // TODO: Update our interface and headers to support vulkan 1.3 and add in the new required
92     // functions for 1.3 that the allocator needs. Until then we just clamp the version to 1.1.
93     info.vulkanApiVersion = std::min(physicalDeviceVersion, VK_MAKE_VERSION(1, 1, 0));
94     info.pTypeExternalMemoryHandleTypes = nullptr;
95 
96     VmaAllocator allocator;
97     vmaCreateAllocator(&info, &allocator);
98 
99     return sk_sp<VulkanAMDMemoryAllocator>(new VulkanAMDMemoryAllocator(allocator));
100 }
101 
VulkanAMDMemoryAllocator(VmaAllocator allocator)102 VulkanAMDMemoryAllocator::VulkanAMDMemoryAllocator(VmaAllocator allocator)
103         : fAllocator(allocator) {}
104 
~VulkanAMDMemoryAllocator()105 VulkanAMDMemoryAllocator::~VulkanAMDMemoryAllocator() {
106     vmaDestroyAllocator(fAllocator);
107     fAllocator = VK_NULL_HANDLE;
108 }
109 
allocateImageMemory(VkImage image,uint32_t allocationPropertyFlags,skgpu::VulkanBackendMemory * backendMemory)110 VkResult VulkanAMDMemoryAllocator::allocateImageMemory(VkImage image,
111                                                        uint32_t allocationPropertyFlags,
112                                                        skgpu::VulkanBackendMemory* backendMemory) {
113     TRACE_EVENT0_ALWAYS("skia.gpu", TRACE_FUNC);
114     VmaAllocationCreateInfo info;
115     info.flags = 0;
116     info.usage = VMA_MEMORY_USAGE_UNKNOWN;
117     info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
118     info.preferredFlags = 0;
119     info.memoryTypeBits = 0;
120     info.pool = VK_NULL_HANDLE;
121     info.pUserData = nullptr;
122 
123     if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
124         info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
125     }
126     if (kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
127         info.requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
128     }
129     if (kProtected_AllocationPropertyFlag & allocationPropertyFlags) {
130         info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
131     }
132 
133     VmaAllocation allocation;
134     VkResult result = vmaAllocateMemoryForImage(fAllocator, image, &info, &allocation, nullptr);
135     if (VK_SUCCESS == result) {
136         *backendMemory = (VulkanBackendMemory)allocation;
137     }
138     return result;
139 }
140 
allocateBufferMemory(VkBuffer buffer,BufferUsage usage,uint32_t allocationPropertyFlags,skgpu::VulkanBackendMemory * backendMemory)141 VkResult VulkanAMDMemoryAllocator::allocateBufferMemory(VkBuffer buffer,
142                                                         BufferUsage usage,
143                                                         uint32_t allocationPropertyFlags,
144                                                         skgpu::VulkanBackendMemory* backendMemory) {
145     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
146     VmaAllocationCreateInfo info;
147     info.flags = 0;
148     info.usage = VMA_MEMORY_USAGE_UNKNOWN;
149     info.memoryTypeBits = 0;
150     info.pool = VK_NULL_HANDLE;
151     info.pUserData = nullptr;
152 
153     switch (usage) {
154         case BufferUsage::kGpuOnly:
155             info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
156             info.preferredFlags = 0;
157             break;
158         case BufferUsage::kCpuWritesGpuReads:
159             // When doing cpu writes and gpu reads the general rule of thumb is to use coherent
160             // memory. Though this depends on the fact that we are not doing any cpu reads and the
161             // cpu writes are sequential. For sparse writes we'd want cpu cached memory, however we
162             // don't do these types of writes in Skia.
163             //
164             // TODO: In the future there may be times where specific types of memory could benefit
165             // from a coherent and cached memory. Typically these allow for the gpu to read cpu
166             // writes from the cache without needing to flush the writes throughout the cache. The
167             // reverse is not true and GPU writes tend to invalidate the cache regardless. Also
168             // these gpu cache read access are typically lower bandwidth than non-cached memory.
169             // For now Skia doesn't really have a need or want of this type of memory. But if we
170             // ever do we could pass in an AllocationPropertyFlag that requests the cached property.
171             info.requiredFlags =
172                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
173             info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
174             break;
175         case BufferUsage::kTransfersFromCpuToGpu:
176             info.requiredFlags =
177                     VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
178             info.preferredFlags = 0;
179             break;
180         case BufferUsage::kTransfersFromGpuToCpu:
181             info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
182             info.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
183             break;
184     }
185 
186     if (kDedicatedAllocation_AllocationPropertyFlag & allocationPropertyFlags) {
187         info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
188     }
189     if ((kLazyAllocation_AllocationPropertyFlag & allocationPropertyFlags) &&
190         BufferUsage::kGpuOnly == usage) {
191         info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
192     }
193 
194     if (kPersistentlyMapped_AllocationPropertyFlag & allocationPropertyFlags) {
195         SkASSERT(BufferUsage::kGpuOnly != usage);
196         info.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
197     }
198 
199     if (kProtected_AllocationPropertyFlag & allocationPropertyFlags) {
200         info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
201     }
202 
203     VmaAllocation allocation;
204     VkResult result = vmaAllocateMemoryForBuffer(fAllocator, buffer, &info, &allocation, nullptr);
205     if (VK_SUCCESS == result) {
206         *backendMemory = (VulkanBackendMemory)allocation;
207     }
208 
209     return result;
210 }
211 
freeMemory(const VulkanBackendMemory & memoryHandle)212 void VulkanAMDMemoryAllocator::freeMemory(const VulkanBackendMemory& memoryHandle) {
213     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
214     const VmaAllocation allocation = (VmaAllocation)memoryHandle;
215     vmaFreeMemory(fAllocator, allocation);
216 }
217 
getAllocInfo(const VulkanBackendMemory & memoryHandle,VulkanAlloc * alloc) const218 void VulkanAMDMemoryAllocator::getAllocInfo(const VulkanBackendMemory& memoryHandle,
219                                             VulkanAlloc* alloc) const {
220     const VmaAllocation allocation = (VmaAllocation)memoryHandle;
221     VmaAllocationInfo vmaInfo;
222     vmaGetAllocationInfo(fAllocator, allocation, &vmaInfo);
223 
224     VkMemoryPropertyFlags memFlags;
225     vmaGetMemoryTypeProperties(fAllocator, vmaInfo.memoryType, &memFlags);
226 
227     uint32_t flags = 0;
228     if (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT & memFlags) {
229         flags |= VulkanAlloc::kMappable_Flag;
230     }
231     if (!SkToBool(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & memFlags)) {
232         flags |= VulkanAlloc::kNoncoherent_Flag;
233     }
234     if (VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT & memFlags) {
235         flags |= VulkanAlloc::kLazilyAllocated_Flag;
236     }
237 
238     alloc->fMemory        = vmaInfo.deviceMemory;
239     alloc->fOffset        = vmaInfo.offset;
240     alloc->fSize          = vmaInfo.size;
241     alloc->fFlags         = flags;
242     alloc->fBackendMemory = memoryHandle;
243 }
244 
mapMemory(const VulkanBackendMemory & memoryHandle,void ** data)245 VkResult VulkanAMDMemoryAllocator::mapMemory(const VulkanBackendMemory& memoryHandle, void** data) {
246     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
247     const VmaAllocation allocation = (VmaAllocation)memoryHandle;
248     return vmaMapMemory(fAllocator, allocation, data);
249 }
250 
unmapMemory(const VulkanBackendMemory & memoryHandle)251 void VulkanAMDMemoryAllocator::unmapMemory(const VulkanBackendMemory& memoryHandle) {
252     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
253     const VmaAllocation allocation = (VmaAllocation)memoryHandle;
254     vmaUnmapMemory(fAllocator, allocation);
255 }
256 
flushMemory(const VulkanBackendMemory & memoryHandle,VkDeviceSize offset,VkDeviceSize size)257 VkResult VulkanAMDMemoryAllocator::flushMemory(const VulkanBackendMemory& memoryHandle,
258                                                VkDeviceSize offset,
259                                                VkDeviceSize size) {
260     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
261     const VmaAllocation allocation = (VmaAllocation)memoryHandle;
262     return vmaFlushAllocation(fAllocator, allocation, offset, size);
263 }
264 
invalidateMemory(const VulkanBackendMemory & memoryHandle,VkDeviceSize offset,VkDeviceSize size)265 VkResult VulkanAMDMemoryAllocator::invalidateMemory(const VulkanBackendMemory& memoryHandle,
266                                                     VkDeviceSize offset,
267                                                     VkDeviceSize size) {
268     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
269     const VmaAllocation allocation = (VmaAllocation)memoryHandle;
270     return vmaInvalidateAllocation(fAllocator, allocation, offset, size);
271 }
272 
totalAllocatedAndUsedMemory() const273 std::pair<uint64_t, uint64_t> VulkanAMDMemoryAllocator::totalAllocatedAndUsedMemory() const {
274     VmaTotalStatistics stats;
275     vmaCalculateStatistics(fAllocator, &stats);
276     return {stats.total.statistics.blockBytes, stats.total.statistics.allocationBytes};
277 }
278 
279 namespace VulkanMemoryAllocators {
Make(const skgpu::VulkanBackendContext & backendContext,ThreadSafe threadSafe,std::optional<VkDeviceSize> blockSize)280 sk_sp<VulkanMemoryAllocator> Make(const skgpu::VulkanBackendContext& backendContext,
281                                   ThreadSafe threadSafe,
282                                   std::optional<VkDeviceSize> blockSize) {
283     SkASSERT(backendContext.fInstance != VK_NULL_HANDLE);
284     SkASSERT(backendContext.fPhysicalDevice != VK_NULL_HANDLE);
285     SkASSERT(backendContext.fDevice != VK_NULL_HANDLE);
286     SkASSERT(backendContext.fQueue != VK_NULL_HANDLE);
287     SkASSERT(backendContext.fGetProc);
288 
289     skgpu::VulkanExtensions ext;
290     const skgpu::VulkanExtensions* extensions = &ext;
291     if (backendContext.fVkExtensions) {
292         extensions = backendContext.fVkExtensions;
293     }
294 
295     // It is a bit superfluous to create a VulkanInterface here just to create a memory allocator
296     // given that Ganesh and Graphite will create their own. However, there's not a clean way to
297     // have the interface created here persist for potential re-use without refactoring
298     // VulkanMemoryAllocator to hold onto its interface as opposed to "borrowing" it.
299     // Such a refactor could get messy without much actual benefit since interface creation is
300     // not too expensive and this cost is only paid once during initialization.
301     uint32_t physDevVersion = 0;
302     sk_sp<const skgpu::VulkanInterface> interface =
303             skgpu::MakeInterface(backendContext, extensions, &physDevVersion, nullptr);
304     if (!interface) {
305         return nullptr;
306     }
307 
308     return VulkanAMDMemoryAllocator::Make(backendContext.fInstance,
309                                           backendContext.fPhysicalDevice,
310                                           backendContext.fDevice,
311                                           physDevVersion,
312                                           extensions,
313                                           interface.get(),
314                                           threadSafe,
315                                           blockSize);
316 }
317 
318 }  // namespace VulkanMemoryAllocators
319 }  // namespace skgpu
320