blob: 4f619a3ef3330cebb3524629014f6462f55ee7bd [file] [log] [blame]
Greg Daniel164a9f02016-02-22 09:56:40 -05001/*
2* Copyright 2015 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 "GrVkMemory.h"
9
10#include "GrVkGpu.h"
11#include "GrVkUtil.h"
12
Greg Daniela9d3dae2018-05-30 22:59:03 +000013#ifdef SK_DEBUG
14// for simple tracking of how much we're using in each heap
15// last counter is for non-subheap allocations
16VkDeviceSize gHeapUsage[VK_MAX_MEMORY_HEAPS+1] = { 0 };
17#endif
jvanverth68c3d302016-09-23 10:30:04 -070018
Greg Daniela9d3dae2018-05-30 22:59:03 +000019static bool get_valid_memory_type_index(const VkPhysicalDeviceMemoryProperties& physDevMemProps,
20 uint32_t typeBits,
21 VkMemoryPropertyFlags requestedMemFlags,
22 uint32_t* typeIndex,
23 uint32_t* heapIndex) {
24 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
25 if (typeBits & (1 << i)) {
26 uint32_t supportedFlags = physDevMemProps.memoryTypes[i].propertyFlags &
27 requestedMemFlags;
28 if (supportedFlags == requestedMemFlags) {
29 *typeIndex = i;
30 *heapIndex = physDevMemProps.memoryTypes[i].heapIndex;
31 return true;
32 }
33 }
Greg Daniel164a9f02016-02-22 09:56:40 -050034 }
Greg Daniela9d3dae2018-05-30 22:59:03 +000035 return false;
36}
37
38static GrVkGpu::Heap buffer_type_to_heap(GrVkBuffer::Type type) {
39 const GrVkGpu::Heap kBufferToHeap[]{
40 GrVkGpu::kVertexBuffer_Heap,
41 GrVkGpu::kIndexBuffer_Heap,
42 GrVkGpu::kUniformBuffer_Heap,
43 GrVkGpu::kTexelBuffer_Heap,
44 GrVkGpu::kCopyReadBuffer_Heap,
45 GrVkGpu::kCopyWriteBuffer_Heap,
46 };
47 GR_STATIC_ASSERT(0 == GrVkBuffer::kVertex_Type);
48 GR_STATIC_ASSERT(1 == GrVkBuffer::kIndex_Type);
49 GR_STATIC_ASSERT(2 == GrVkBuffer::kUniform_Type);
50 GR_STATIC_ASSERT(3 == GrVkBuffer::kTexel_Type);
51 GR_STATIC_ASSERT(4 == GrVkBuffer::kCopyRead_Type);
52 GR_STATIC_ASSERT(5 == GrVkBuffer::kCopyWrite_Type);
53
54 return kBufferToHeap[type];
Greg Daniel164a9f02016-02-22 09:56:40 -050055}
56
57bool GrVkMemory::AllocAndBindBufferMemory(const GrVkGpu* gpu,
58 VkBuffer buffer,
jvanverth6b6ffc42016-06-13 14:28:07 -070059 GrVkBuffer::Type type,
jvanvertha584de92016-06-30 09:10:52 -070060 bool dynamic,
jvanverth1e305ba2016-06-01 09:39:15 -070061 GrVkAlloc* alloc) {
Greg Daniela9d3dae2018-05-30 22:59:03 +000062 const GrVkInterface* iface = gpu->vkInterface();
63 VkDevice device = gpu->device();
Greg Daniel164a9f02016-02-22 09:56:40 -050064
Greg Daniela9d3dae2018-05-30 22:59:03 +000065 VkMemoryRequirements memReqs;
66 GR_VK_CALL(iface, GetBufferMemoryRequirements(device, buffer, &memReqs));
Greg Daniel164a9f02016-02-22 09:56:40 -050067
Greg Daniela9d3dae2018-05-30 22:59:03 +000068 uint32_t typeIndex = 0;
69 uint32_t heapIndex = 0;
70 const VkPhysicalDeviceMemoryProperties& phDevMemProps = gpu->physicalDeviceMemoryProperties();
71 const VkPhysicalDeviceProperties& phDevProps = gpu->physicalDeviceProperties();
72 if (dynamic) {
73 // try to get cached and ideally non-coherent memory first
74 if (!get_valid_memory_type_index(phDevMemProps,
75 memReqs.memoryTypeBits,
76 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
77 VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
78 &typeIndex,
79 &heapIndex)) {
80 // some sort of host-visible memory type should always be available for dynamic buffers
81 SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
82 memReqs.memoryTypeBits,
83 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
84 &typeIndex,
85 &heapIndex));
86 }
87
88 VkMemoryPropertyFlags mpf = phDevMemProps.memoryTypes[typeIndex].propertyFlags;
89 alloc->fFlags = mpf & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ? 0x0
90 : GrVkAlloc::kNoncoherent_Flag;
91 if (SkToBool(alloc->fFlags & GrVkAlloc::kNoncoherent_Flag)) {
92 SkASSERT(SkIsPow2(memReqs.alignment));
93 SkASSERT(SkIsPow2(phDevProps.limits.nonCoherentAtomSize));
94 memReqs.alignment = SkTMax(memReqs.alignment, phDevProps.limits.nonCoherentAtomSize);
95 }
96 } else {
97 // device-local memory should always be available for static buffers
98 SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
99 memReqs.memoryTypeBits,
100 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
101 &typeIndex,
102 &heapIndex));
103 alloc->fFlags = 0x0;
jvanverth6b6ffc42016-06-13 14:28:07 -0700104 }
Greg Daniela9d3dae2018-05-30 22:59:03 +0000105
106 GrVkHeap* heap = gpu->getHeap(buffer_type_to_heap(type));
107
108 if (!heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
109 // if static, try to allocate from non-host-visible non-device-local memory instead
110 if (dynamic ||
111 !get_valid_memory_type_index(phDevMemProps, memReqs.memoryTypeBits,
112 0, &typeIndex, &heapIndex) ||
113 !heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
114 SkDebugf("Failed to alloc buffer\n");
115 return false;
116 }
117 }
Greg Daniel164a9f02016-02-22 09:56:40 -0500118
jvanverth9d54afc2016-09-20 09:20:03 -0700119 // Bind buffer
Greg Daniela9d3dae2018-05-30 22:59:03 +0000120 VkResult err = GR_VK_CALL(iface, BindBufferMemory(device, buffer,
121 alloc->fMemory, alloc->fOffset));
Greg Daniel164a9f02016-02-22 09:56:40 -0500122 if (err) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000123 SkASSERT_RELEASE(heap->free(*alloc));
Greg Daniel164a9f02016-02-22 09:56:40 -0500124 return false;
125 }
jvanverth6b6ffc42016-06-13 14:28:07 -0700126
Greg Daniel164a9f02016-02-22 09:56:40 -0500127 return true;
128}
129
jvanverth6b6ffc42016-06-13 14:28:07 -0700130void GrVkMemory::FreeBufferMemory(const GrVkGpu* gpu, GrVkBuffer::Type type,
131 const GrVkAlloc& alloc) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000132
133 GrVkHeap* heap = gpu->getHeap(buffer_type_to_heap(type));
134 SkASSERT_RELEASE(heap->free(alloc));
jvanverth6b6ffc42016-06-13 14:28:07 -0700135}
136
Greg Daniela9d3dae2018-05-30 22:59:03 +0000137// for debugging
138static uint64_t gTotalImageMemory = 0;
139static uint64_t gTotalImageMemoryFullPage = 0;
140
jvanverth6b6ffc42016-06-13 14:28:07 -0700141const VkDeviceSize kMaxSmallImageSize = 16 * 1024;
Greg Daniela9d3dae2018-05-30 22:59:03 +0000142const VkDeviceSize kMinVulkanPageSize = 16 * 1024;
143
144static VkDeviceSize align_size(VkDeviceSize size, VkDeviceSize alignment) {
145 return (size + alignment - 1) & ~(alignment - 1);
146}
jvanverth1e305ba2016-06-01 09:39:15 -0700147
Greg Daniel164a9f02016-02-22 09:56:40 -0500148bool GrVkMemory::AllocAndBindImageMemory(const GrVkGpu* gpu,
149 VkImage image,
jvanverth6b6ffc42016-06-13 14:28:07 -0700150 bool linearTiling,
jvanverth1e305ba2016-06-01 09:39:15 -0700151 GrVkAlloc* alloc) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000152 const GrVkInterface* iface = gpu->vkInterface();
153 VkDevice device = gpu->device();
Greg Daniel164a9f02016-02-22 09:56:40 -0500154
155 VkMemoryRequirements memReqs;
Greg Daniela9d3dae2018-05-30 22:59:03 +0000156 GR_VK_CALL(iface, GetImageMemoryRequirements(device, image, &memReqs));
Greg Daniel164a9f02016-02-22 09:56:40 -0500157
Greg Daniela9d3dae2018-05-30 22:59:03 +0000158 uint32_t typeIndex = 0;
159 uint32_t heapIndex = 0;
160 GrVkHeap* heap;
161 const VkPhysicalDeviceMemoryProperties& phDevMemProps = gpu->physicalDeviceMemoryProperties();
162 const VkPhysicalDeviceProperties& phDevProps = gpu->physicalDeviceProperties();
163 if (linearTiling) {
164 VkMemoryPropertyFlags desiredMemProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
165 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
166 if (!get_valid_memory_type_index(phDevMemProps,
167 memReqs.memoryTypeBits,
168 desiredMemProps,
169 &typeIndex,
170 &heapIndex)) {
171 // some sort of host-visible memory type should always be available
172 SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
173 memReqs.memoryTypeBits,
174 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
175 &typeIndex,
176 &heapIndex));
177 }
178 heap = gpu->getHeap(GrVkGpu::kLinearImage_Heap);
179 VkMemoryPropertyFlags mpf = phDevMemProps.memoryTypes[typeIndex].propertyFlags;
180 alloc->fFlags = mpf & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ? 0x0
181 : GrVkAlloc::kNoncoherent_Flag;
182 if (SkToBool(alloc->fFlags & GrVkAlloc::kNoncoherent_Flag)) {
183 SkASSERT(SkIsPow2(memReqs.alignment));
184 SkASSERT(SkIsPow2(phDevProps.limits.nonCoherentAtomSize));
185 memReqs.alignment = SkTMax(memReqs.alignment, phDevProps.limits.nonCoherentAtomSize);
186 }
jvanverth6b6ffc42016-06-13 14:28:07 -0700187 } else {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000188 // this memory type should always be available
189 SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
190 memReqs.memoryTypeBits,
191 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
192 &typeIndex,
193 &heapIndex));
194 if (memReqs.size <= kMaxSmallImageSize) {
195 heap = gpu->getHeap(GrVkGpu::kSmallOptimalImage_Heap);
196 } else {
197 heap = gpu->getHeap(GrVkGpu::kOptimalImage_Heap);
198 }
199 alloc->fFlags = 0x0;
jvanverth6b6ffc42016-06-13 14:28:07 -0700200 }
201
Greg Daniela9d3dae2018-05-30 22:59:03 +0000202 if (!heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
203 // if optimal, try to allocate from non-host-visible non-device-local memory instead
204 if (linearTiling ||
205 !get_valid_memory_type_index(phDevMemProps, memReqs.memoryTypeBits,
206 0, &typeIndex, &heapIndex) ||
207 !heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
208 SkDebugf("Failed to alloc image\n");
209 return false;
210 }
Greg Daniel164a9f02016-02-22 09:56:40 -0500211 }
jvanverth6b6ffc42016-06-13 14:28:07 -0700212
Greg Daniela9d3dae2018-05-30 22:59:03 +0000213 // Bind image
214 VkResult err = GR_VK_CALL(iface, BindImageMemory(device, image,
215 alloc->fMemory, alloc->fOffset));
Greg Daniel331c2662018-05-30 14:51:53 -0400216 if (err) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000217 SkASSERT_RELEASE(heap->free(*alloc));
Greg Daniel331c2662018-05-30 14:51:53 -0400218 return false;
219 }
jvanverth6b6ffc42016-06-13 14:28:07 -0700220
Greg Daniela9d3dae2018-05-30 22:59:03 +0000221 gTotalImageMemory += alloc->fSize;
222
223 VkDeviceSize pageAlignedSize = align_size(alloc->fSize, kMinVulkanPageSize);
224 gTotalImageMemoryFullPage += pageAlignedSize;
225
Greg Daniel164a9f02016-02-22 09:56:40 -0500226 return true;
227}
228
jvanverth6b6ffc42016-06-13 14:28:07 -0700229void GrVkMemory::FreeImageMemory(const GrVkGpu* gpu, bool linearTiling,
230 const GrVkAlloc& alloc) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000231 GrVkHeap* heap;
232 if (linearTiling) {
233 heap = gpu->getHeap(GrVkGpu::kLinearImage_Heap);
234 } else if (alloc.fSize <= kMaxSmallImageSize) {
235 heap = gpu->getHeap(GrVkGpu::kSmallOptimalImage_Heap);
jvanverth6b6ffc42016-06-13 14:28:07 -0700236 } else {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000237 heap = gpu->getHeap(GrVkGpu::kOptimalImage_Heap);
238 }
239 if (!heap->free(alloc)) {
240 // must be an adopted allocation
jvanverth6b6ffc42016-06-13 14:28:07 -0700241 GR_VK_CALL(gpu->vkInterface(), FreeMemory(gpu->device(), alloc.fMemory, nullptr));
Greg Daniel331c2662018-05-30 14:51:53 -0400242 } else {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000243 gTotalImageMemory -= alloc.fSize;
244 VkDeviceSize pageAlignedSize = align_size(alloc.fSize, kMinVulkanPageSize);
245 gTotalImageMemoryFullPage -= pageAlignedSize;
Greg Daniel331c2662018-05-30 14:51:53 -0400246 }
247}
248
Greg Daniele35a99e2018-03-02 11:44:22 -0500249void GrVkMemory::FlushMappedAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc, VkDeviceSize offset,
250 VkDeviceSize size) {
jvanverth9d54afc2016-09-20 09:20:03 -0700251 if (alloc.fFlags & GrVkAlloc::kNoncoherent_Flag) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000252#ifdef SK_DEBUG
253 SkASSERT(offset >= alloc.fOffset);
254 VkDeviceSize alignment = gpu->physicalDeviceProperties().limits.nonCoherentAtomSize;
255 SkASSERT(0 == (offset & (alignment-1)));
256 if (size != VK_WHOLE_SIZE) {
257 SkASSERT(size > 0);
258 SkASSERT(0 == (size & (alignment-1)) ||
259 (offset + size) == (alloc.fOffset + alloc.fSize));
260 SkASSERT(offset + size <= alloc.fOffset + alloc.fSize);
Greg Daniele35a99e2018-03-02 11:44:22 -0500261 }
Greg Daniela9d3dae2018-05-30 22:59:03 +0000262#endif
263
264 VkMappedMemoryRange mappedMemoryRange;
265 memset(&mappedMemoryRange, 0, sizeof(VkMappedMemoryRange));
266 mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
267 mappedMemoryRange.memory = alloc.fMemory;
268 mappedMemoryRange.offset = offset;
269 mappedMemoryRange.size = size;
270 GR_VK_CALL(gpu->vkInterface(), FlushMappedMemoryRanges(gpu->device(),
271 1, &mappedMemoryRange));
jvanverth9d54afc2016-09-20 09:20:03 -0700272 }
273}
274
Greg Daniele35a99e2018-03-02 11:44:22 -0500275void GrVkMemory::InvalidateMappedAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc,
276 VkDeviceSize offset, VkDeviceSize size) {
jvanverth9d54afc2016-09-20 09:20:03 -0700277 if (alloc.fFlags & GrVkAlloc::kNoncoherent_Flag) {
Greg Daniela9d3dae2018-05-30 22:59:03 +0000278#ifdef SK_DEBUG
279 SkASSERT(offset >= alloc.fOffset);
280 VkDeviceSize alignment = gpu->physicalDeviceProperties().limits.nonCoherentAtomSize;
281 SkASSERT(0 == (offset & (alignment-1)));
282 if (size != VK_WHOLE_SIZE) {
283 SkASSERT(size > 0);
284 SkASSERT(0 == (size & (alignment-1)) ||
285 (offset + size) == (alloc.fOffset + alloc.fSize));
286 SkASSERT(offset + size <= alloc.fOffset + alloc.fSize);
jvanverth6b6ffc42016-06-13 14:28:07 -0700287 }
Greg Daniela9d3dae2018-05-30 22:59:03 +0000288#endif
289
290 VkMappedMemoryRange mappedMemoryRange;
291 memset(&mappedMemoryRange, 0, sizeof(VkMappedMemoryRange));
292 mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
293 mappedMemoryRange.memory = alloc.fMemory;
294 mappedMemoryRange.offset = offset;
295 mappedMemoryRange.size = size;
296 GR_VK_CALL(gpu->vkInterface(), InvalidateMappedMemoryRanges(gpu->device(),
297 1, &mappedMemoryRange));
jvanverth6b6ffc42016-06-13 14:28:07 -0700298 }
jvanverth6b6ffc42016-06-13 14:28:07 -0700299}
300
Greg Daniela9d3dae2018-05-30 22:59:03 +0000301bool GrVkFreeListAlloc::alloc(VkDeviceSize requestedSize,
302 VkDeviceSize* allocOffset, VkDeviceSize* allocSize) {
303 VkDeviceSize alignedSize = align_size(requestedSize, fAlignment);
304
305 // find the smallest block big enough for our allocation
306 FreeList::Iter iter = fFreeList.headIter();
307 FreeList::Iter bestFitIter;
308 VkDeviceSize bestFitSize = fSize + 1;
309 VkDeviceSize secondLargestSize = 0;
310 VkDeviceSize secondLargestOffset = 0;
311 while (iter.get()) {
312 Block* block = iter.get();
313 // need to adjust size to match desired alignment
314 SkASSERT(align_size(block->fOffset, fAlignment) - block->fOffset == 0);
315 if (block->fSize >= alignedSize && block->fSize < bestFitSize) {
316 bestFitIter = iter;
317 bestFitSize = block->fSize;
318 }
319 if (secondLargestSize < block->fSize && block->fOffset != fLargestBlockOffset) {
320 secondLargestSize = block->fSize;
321 secondLargestOffset = block->fOffset;
322 }
323 iter.next();
324 }
325 SkASSERT(secondLargestSize <= fLargestBlockSize);
326
327 Block* bestFit = bestFitIter.get();
328 if (bestFit) {
329 SkASSERT(align_size(bestFit->fOffset, fAlignment) == bestFit->fOffset);
330 *allocOffset = bestFit->fOffset;
331 *allocSize = alignedSize;
332 // adjust or remove current block
333 VkDeviceSize originalBestFitOffset = bestFit->fOffset;
334 if (bestFit->fSize > alignedSize) {
335 bestFit->fOffset += alignedSize;
336 bestFit->fSize -= alignedSize;
337 if (fLargestBlockOffset == originalBestFitOffset) {
338 if (bestFit->fSize >= secondLargestSize) {
339 fLargestBlockSize = bestFit->fSize;
340 fLargestBlockOffset = bestFit->fOffset;
341 } else {
342 fLargestBlockSize = secondLargestSize;
343 fLargestBlockOffset = secondLargestOffset;
344 }
345 }
346#ifdef SK_DEBUG
347 VkDeviceSize largestSize = 0;
348 iter = fFreeList.headIter();
349 while (iter.get()) {
350 Block* block = iter.get();
351 if (largestSize < block->fSize) {
352 largestSize = block->fSize;
353 }
354 iter.next();
355 }
356 SkASSERT(largestSize == fLargestBlockSize);
357#endif
358 } else {
359 SkASSERT(bestFit->fSize == alignedSize);
360 if (fLargestBlockOffset == originalBestFitOffset) {
361 fLargestBlockSize = secondLargestSize;
362 fLargestBlockOffset = secondLargestOffset;
363 }
364 fFreeList.remove(bestFit);
365#ifdef SK_DEBUG
366 VkDeviceSize largestSize = 0;
367 iter = fFreeList.headIter();
368 while (iter.get()) {
369 Block* block = iter.get();
370 if (largestSize < block->fSize) {
371 largestSize = block->fSize;
372 }
373 iter.next();
374 }
375 SkASSERT(largestSize == fLargestBlockSize);
376#endif
377 }
378 fFreeSize -= alignedSize;
379 SkASSERT(*allocSize > 0);
380
381 return true;
382 }
383
384 SkDebugf("Can't allocate %d bytes, %d bytes available, largest free block %d\n", alignedSize, fFreeSize, fLargestBlockSize);
385
386 return false;
387}
388
389void GrVkFreeListAlloc::free(VkDeviceSize allocOffset, VkDeviceSize allocSize) {
390 // find the block right after this allocation
391 FreeList::Iter iter = fFreeList.headIter();
392 FreeList::Iter prev;
393 while (iter.get() && iter.get()->fOffset < allocOffset) {
394 prev = iter;
395 iter.next();
396 }
397 // we have four cases:
398 // we exactly follow the previous one
399 Block* block;
400 if (prev.get() && prev.get()->fOffset + prev.get()->fSize == allocOffset) {
401 block = prev.get();
402 block->fSize += allocSize;
403 if (block->fOffset == fLargestBlockOffset) {
404 fLargestBlockSize = block->fSize;
405 }
406 // and additionally we may exactly precede the next one
407 if (iter.get() && iter.get()->fOffset == allocOffset + allocSize) {
408 block->fSize += iter.get()->fSize;
409 if (iter.get()->fOffset == fLargestBlockOffset) {
410 fLargestBlockOffset = block->fOffset;
411 fLargestBlockSize = block->fSize;
412 }
413 fFreeList.remove(iter.get());
414 }
415 // or we only exactly proceed the next one
416 } else if (iter.get() && iter.get()->fOffset == allocOffset + allocSize) {
417 block = iter.get();
418 block->fSize += allocSize;
419 if (block->fOffset == fLargestBlockOffset) {
420 fLargestBlockOffset = allocOffset;
421 fLargestBlockSize = block->fSize;
422 }
423 block->fOffset = allocOffset;
424 // or we fall somewhere in between, with gaps
425 } else {
426 block = fFreeList.addBefore(iter);
427 block->fOffset = allocOffset;
428 block->fSize = allocSize;
429 }
430 fFreeSize += allocSize;
431 if (block->fSize > fLargestBlockSize) {
432 fLargestBlockSize = block->fSize;
433 fLargestBlockOffset = block->fOffset;
434 }
435
436#ifdef SK_DEBUG
437 VkDeviceSize largestSize = 0;
438 iter = fFreeList.headIter();
439 while (iter.get()) {
440 Block* block = iter.get();
441 if (largestSize < block->fSize) {
442 largestSize = block->fSize;
443 }
444 iter.next();
445 }
446 SkASSERT(fLargestBlockSize == largestSize);
447#endif
448}
449
450GrVkSubHeap::GrVkSubHeap(const GrVkGpu* gpu, uint32_t memoryTypeIndex, uint32_t heapIndex,
451 VkDeviceSize size, VkDeviceSize alignment)
452 : INHERITED(size, alignment)
453 , fGpu(gpu)
454#ifdef SK_DEBUG
455 , fHeapIndex(heapIndex)
456#endif
457 , fMemoryTypeIndex(memoryTypeIndex) {
458
459 VkMemoryAllocateInfo allocInfo = {
460 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
461 nullptr, // pNext
462 size, // allocationSize
463 memoryTypeIndex, // memoryTypeIndex
464 };
465
466 VkResult err = GR_VK_CALL(gpu->vkInterface(), AllocateMemory(gpu->device(),
467 &allocInfo,
468 nullptr,
469 &fAlloc));
470 if (VK_SUCCESS != err) {
471 this->reset();
472 }
473#ifdef SK_DEBUG
474 else {
475 gHeapUsage[heapIndex] += size;
476 }
477#endif
478}
479
480GrVkSubHeap::~GrVkSubHeap() {
481 const GrVkInterface* iface = fGpu->vkInterface();
482 GR_VK_CALL(iface, FreeMemory(fGpu->device(), fAlloc, nullptr));
483#ifdef SK_DEBUG
484 gHeapUsage[fHeapIndex] -= fSize;
485#endif
486}
487
488bool GrVkSubHeap::alloc(VkDeviceSize size, GrVkAlloc* alloc) {
489 alloc->fMemory = fAlloc;
490 return INHERITED::alloc(size, &alloc->fOffset, &alloc->fSize);
491}
492
493void GrVkSubHeap::free(const GrVkAlloc& alloc) {
494 SkASSERT(alloc.fMemory == fAlloc);
495
496 INHERITED::free(alloc.fOffset, alloc.fSize);
497}
498
499bool GrVkHeap::subAlloc(VkDeviceSize size, VkDeviceSize alignment,
500 uint32_t memoryTypeIndex, uint32_t heapIndex, GrVkAlloc* alloc) {
501 VkDeviceSize alignedSize = align_size(size, alignment);
502
503 // if requested is larger than our subheap allocation, just alloc directly
504 if (alignedSize > fSubHeapSize) {
505 VkMemoryAllocateInfo allocInfo = {
506 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
507 nullptr, // pNext
508 alignedSize, // allocationSize
509 memoryTypeIndex, // memoryTypeIndex
510 };
511
512 VkResult err = GR_VK_CALL(fGpu->vkInterface(), AllocateMemory(fGpu->device(),
513 &allocInfo,
514 nullptr,
515 &alloc->fMemory));
516 if (VK_SUCCESS != err) {
517 return false;
518 }
519 alloc->fOffset = 0;
520 alloc->fSize = alignedSize;
521 alloc->fUsesSystemHeap = true;
522#ifdef SK_DEBUG
523 gHeapUsage[VK_MAX_MEMORY_HEAPS] += alignedSize;
524#endif
525
526 return true;
527 }
528
529 // first try to find a subheap that fits our allocation request
530 int bestFitIndex = -1;
531 VkDeviceSize bestFitSize = 0x7FFFFFFF;
532 for (auto i = 0; i < fSubHeaps.count(); ++i) {
533 if (fSubHeaps[i]->memoryTypeIndex() == memoryTypeIndex &&
534 fSubHeaps[i]->alignment() == alignment) {
535 VkDeviceSize heapSize = fSubHeaps[i]->largestBlockSize();
536 if (heapSize >= alignedSize && heapSize < bestFitSize) {
537 bestFitIndex = i;
538 bestFitSize = heapSize;
539 }
540 }
541 }
542
543 if (bestFitIndex >= 0) {
544 SkASSERT(fSubHeaps[bestFitIndex]->alignment() == alignment);
545 if (fSubHeaps[bestFitIndex]->alloc(size, alloc)) {
546 fUsedSize += alloc->fSize;
547 return true;
548 }
549 return false;
550 }
551
552 // need to allocate a new subheap
553 std::unique_ptr<GrVkSubHeap>& subHeap = fSubHeaps.push_back();
554 subHeap.reset(new GrVkSubHeap(fGpu, memoryTypeIndex, heapIndex, fSubHeapSize, alignment));
555 // try to recover from failed allocation by only allocating what we need
556 if (subHeap->size() == 0) {
557 VkDeviceSize alignedSize = align_size(size, alignment);
558 subHeap.reset(new GrVkSubHeap(fGpu, memoryTypeIndex, heapIndex, alignedSize, alignment));
559 if (subHeap->size() == 0) {
560 return false;
561 }
562 }
563 fAllocSize += fSubHeapSize;
564 if (subHeap->alloc(size, alloc)) {
565 fUsedSize += alloc->fSize;
566 return true;
567 }
568
569 return false;
570}
571
572bool GrVkHeap::singleAlloc(VkDeviceSize size, VkDeviceSize alignment,
573 uint32_t memoryTypeIndex, uint32_t heapIndex, GrVkAlloc* alloc) {
574 VkDeviceSize alignedSize = align_size(size, alignment);
575
576 // first try to find an unallocated subheap that fits our allocation request
577 int bestFitIndex = -1;
578 VkDeviceSize bestFitSize = 0x7FFFFFFF;
579 for (auto i = 0; i < fSubHeaps.count(); ++i) {
580 if (fSubHeaps[i]->memoryTypeIndex() == memoryTypeIndex &&
581 fSubHeaps[i]->alignment() == alignment &&
582 fSubHeaps[i]->unallocated()) {
583 VkDeviceSize heapSize = fSubHeaps[i]->size();
584 if (heapSize >= alignedSize && heapSize < bestFitSize) {
585 bestFitIndex = i;
586 bestFitSize = heapSize;
587 }
588 }
589 }
590
591 if (bestFitIndex >= 0) {
592 SkASSERT(fSubHeaps[bestFitIndex]->alignment() == alignment);
593 if (fSubHeaps[bestFitIndex]->alloc(size, alloc)) {
594 fUsedSize += alloc->fSize;
595 return true;
596 }
597 return false;
598 }
599
600 // need to allocate a new subheap
601 std::unique_ptr<GrVkSubHeap>& subHeap = fSubHeaps.push_back();
602 subHeap.reset(new GrVkSubHeap(fGpu, memoryTypeIndex, heapIndex, alignedSize, alignment));
603 fAllocSize += alignedSize;
604 if (subHeap->alloc(size, alloc)) {
605 fUsedSize += alloc->fSize;
606 return true;
607 }
608
609 return false;
610}
611
612bool GrVkHeap::free(const GrVkAlloc& alloc) {
613 // a size of 0 means we're using the system heap
614 if (alloc.fUsesSystemHeap) {
615 const GrVkInterface* iface = fGpu->vkInterface();
616 GR_VK_CALL(iface, FreeMemory(fGpu->device(), alloc.fMemory, nullptr));
617 return true;
618 }
619
620 for (auto i = 0; i < fSubHeaps.count(); ++i) {
621 if (fSubHeaps[i]->memory() == alloc.fMemory) {
622 fSubHeaps[i]->free(alloc);
623 fUsedSize -= alloc.fSize;
624 return true;
625 }
626 }
627
628 return false;
629}
630
631