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