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