1 /*
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 
13 #ifdef SK_DEBUG
14 // for simple tracking of how much we're using in each heap
15 // last counter is for non-subheap allocations
16 VkDeviceSize gHeapUsage[VK_MAX_MEMORY_HEAPS+1] = { 0 };
17 #endif
18 
get_valid_memory_type_index(const VkPhysicalDeviceMemoryProperties & physDevMemProps,uint32_t typeBits,VkMemoryPropertyFlags requestedMemFlags,uint32_t * typeIndex,uint32_t * heapIndex)19 static 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         }
34     }
35     return false;
36 }
37 
buffer_type_to_heap(GrVkBuffer::Type type)38 static 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::kCopyReadBuffer_Heap,
44         GrVkGpu::kCopyWriteBuffer_Heap,
45     };
46     GR_STATIC_ASSERT(0 == GrVkBuffer::kVertex_Type);
47     GR_STATIC_ASSERT(1 == GrVkBuffer::kIndex_Type);
48     GR_STATIC_ASSERT(2 == GrVkBuffer::kUniform_Type);
49     GR_STATIC_ASSERT(3 == GrVkBuffer::kCopyRead_Type);
50     GR_STATIC_ASSERT(4 == GrVkBuffer::kCopyWrite_Type);
51 
52     return kBufferToHeap[type];
53 }
54 
AllocAndBindBufferMemory(const GrVkGpu * gpu,VkBuffer buffer,GrVkBuffer::Type type,bool dynamic,GrVkAlloc * alloc)55 bool GrVkMemory::AllocAndBindBufferMemory(const GrVkGpu* gpu,
56                                           VkBuffer buffer,
57                                           GrVkBuffer::Type type,
58                                           bool dynamic,
59                                           GrVkAlloc* alloc) {
60     const GrVkInterface* iface = gpu->vkInterface();
61     VkDevice device = gpu->device();
62 
63     VkMemoryRequirements memReqs;
64     GR_VK_CALL(iface, GetBufferMemoryRequirements(device, buffer, &memReqs));
65 
66     uint32_t typeIndex = 0;
67     uint32_t heapIndex = 0;
68     const VkPhysicalDeviceMemoryProperties& phDevMemProps = gpu->physicalDeviceMemoryProperties();
69     if (dynamic) {
70         // try to get cached and ideally non-coherent memory first
71         if (!get_valid_memory_type_index(phDevMemProps,
72                                          memReqs.memoryTypeBits,
73                                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
74                                          VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
75                                          &typeIndex,
76                                          &heapIndex)) {
77             // some sort of host-visible memory type should always be available for dynamic buffers
78             SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
79                                                          memReqs.memoryTypeBits,
80                                                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
81                                                          &typeIndex,
82                                                          &heapIndex));
83         }
84 
85         VkMemoryPropertyFlags mpf = phDevMemProps.memoryTypes[typeIndex].propertyFlags;
86         alloc->fFlags = mpf & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ? 0x0
87                                                                    : GrVkAlloc::kNoncoherent_Flag;
88     } else {
89         // device-local memory should always be available for static buffers
90         SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
91                                                      memReqs.memoryTypeBits,
92                                                      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
93                                                      &typeIndex,
94                                                      &heapIndex));
95         alloc->fFlags = 0x0;
96     }
97 
98     GrVkHeap* heap = gpu->getHeap(buffer_type_to_heap(type));
99 
100     if (!heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
101         // if static, try to allocate from non-host-visible non-device-local memory instead
102         if (dynamic ||
103             !get_valid_memory_type_index(phDevMemProps, memReqs.memoryTypeBits,
104                                          0, &typeIndex, &heapIndex) ||
105             !heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
106             SkDebugf("Failed to alloc buffer\n");
107             return false;
108         }
109     }
110 
111     // Bind buffer
112     VkResult err = GR_VK_CALL(iface, BindBufferMemory(device, buffer,
113                                                       alloc->fMemory, alloc->fOffset));
114     if (err) {
115         SkASSERT_RELEASE(heap->free(*alloc));
116         return false;
117     }
118 
119     return true;
120 }
121 
FreeBufferMemory(const GrVkGpu * gpu,GrVkBuffer::Type type,const GrVkAlloc & alloc)122 void GrVkMemory::FreeBufferMemory(const GrVkGpu* gpu, GrVkBuffer::Type type,
123                                   const GrVkAlloc& alloc) {
124 
125     GrVkHeap* heap = gpu->getHeap(buffer_type_to_heap(type));
126     SkASSERT_RELEASE(heap->free(alloc));
127 }
128 
129 // for debugging
130 static uint64_t gTotalImageMemory = 0;
131 static uint64_t gTotalImageMemoryFullPage = 0;
132 
133 const VkDeviceSize kMaxSmallImageSize = 16 * 1024;
134 const VkDeviceSize kMinVulkanPageSize = 16 * 1024;
135 
align_size(VkDeviceSize size,VkDeviceSize alignment)136 static VkDeviceSize align_size(VkDeviceSize size, VkDeviceSize alignment) {
137     return (size + alignment - 1) & ~(alignment - 1);
138 }
139 
AllocAndBindImageMemory(const GrVkGpu * gpu,VkImage image,bool linearTiling,GrVkAlloc * alloc)140 bool GrVkMemory::AllocAndBindImageMemory(const GrVkGpu* gpu,
141                                          VkImage image,
142                                          bool linearTiling,
143                                          GrVkAlloc* alloc) {
144     const GrVkInterface* iface = gpu->vkInterface();
145     VkDevice device = gpu->device();
146 
147     VkMemoryRequirements memReqs;
148     GR_VK_CALL(iface, GetImageMemoryRequirements(device, image, &memReqs));
149 
150     uint32_t typeIndex = 0;
151     uint32_t heapIndex = 0;
152     GrVkHeap* heap;
153     const VkPhysicalDeviceMemoryProperties& phDevMemProps = gpu->physicalDeviceMemoryProperties();
154     if (linearTiling) {
155         VkMemoryPropertyFlags desiredMemProps = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
156                                                 VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
157         if (!get_valid_memory_type_index(phDevMemProps,
158                                          memReqs.memoryTypeBits,
159                                          desiredMemProps,
160                                          &typeIndex,
161                                          &heapIndex)) {
162             // some sort of host-visible memory type should always be available
163             SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
164                                                          memReqs.memoryTypeBits,
165                                                          VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
166                                                          &typeIndex,
167                                                          &heapIndex));
168         }
169         heap = gpu->getHeap(GrVkGpu::kLinearImage_Heap);
170         VkMemoryPropertyFlags mpf = phDevMemProps.memoryTypes[typeIndex].propertyFlags;
171         alloc->fFlags = mpf & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ? 0x0
172                                                                    : GrVkAlloc::kNoncoherent_Flag;
173     } else {
174         // this memory type should always be available
175         SkASSERT_RELEASE(get_valid_memory_type_index(phDevMemProps,
176                                                      memReqs.memoryTypeBits,
177                                                      VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
178                                                      &typeIndex,
179                                                      &heapIndex));
180         if (memReqs.size <= kMaxSmallImageSize) {
181             heap = gpu->getHeap(GrVkGpu::kSmallOptimalImage_Heap);
182         } else {
183             heap = gpu->getHeap(GrVkGpu::kOptimalImage_Heap);
184         }
185         alloc->fFlags = 0x0;
186     }
187 
188     if (!heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
189         // if optimal, try to allocate from non-host-visible non-device-local memory instead
190         if (linearTiling ||
191             !get_valid_memory_type_index(phDevMemProps, memReqs.memoryTypeBits,
192                                          0, &typeIndex, &heapIndex) ||
193             !heap->alloc(memReqs.size, memReqs.alignment, typeIndex, heapIndex, alloc)) {
194             SkDebugf("Failed to alloc image\n");
195             return false;
196         }
197     }
198 
199     // Bind image
200     VkResult err = GR_VK_CALL(iface, BindImageMemory(device, image,
201                               alloc->fMemory, alloc->fOffset));
202     if (err) {
203         SkASSERT_RELEASE(heap->free(*alloc));
204         return false;
205     }
206 
207     gTotalImageMemory += alloc->fSize;
208 
209     VkDeviceSize pageAlignedSize = align_size(alloc->fSize, kMinVulkanPageSize);
210     gTotalImageMemoryFullPage += pageAlignedSize;
211 
212     return true;
213 }
214 
FreeImageMemory(const GrVkGpu * gpu,bool linearTiling,const GrVkAlloc & alloc)215 void GrVkMemory::FreeImageMemory(const GrVkGpu* gpu, bool linearTiling,
216                                  const GrVkAlloc& alloc) {
217     GrVkHeap* heap;
218     if (linearTiling) {
219         heap = gpu->getHeap(GrVkGpu::kLinearImage_Heap);
220     } else if (alloc.fSize <= kMaxSmallImageSize) {
221         heap = gpu->getHeap(GrVkGpu::kSmallOptimalImage_Heap);
222     } else {
223         heap = gpu->getHeap(GrVkGpu::kOptimalImage_Heap);
224     }
225     if (!heap->free(alloc)) {
226         // must be an adopted allocation
227         GR_VK_CALL(gpu->vkInterface(), FreeMemory(gpu->device(), alloc.fMemory, nullptr));
228     } else {
229         gTotalImageMemory -= alloc.fSize;
230         VkDeviceSize pageAlignedSize = align_size(alloc.fSize, kMinVulkanPageSize);
231         gTotalImageMemoryFullPage -= pageAlignedSize;
232     }
233 }
234 
LayoutToPipelineStageFlags(const VkImageLayout layout)235 VkPipelineStageFlags GrVkMemory::LayoutToPipelineStageFlags(const VkImageLayout layout) {
236     if (VK_IMAGE_LAYOUT_GENERAL == layout) {
237         return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
238     } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
239                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
240         return VK_PIPELINE_STAGE_TRANSFER_BIT;
241     } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout ||
242                VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
243                VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout ||
244                VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
245         return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
246     } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
247         return VK_PIPELINE_STAGE_HOST_BIT;
248     }
249 
250     SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout);
251     return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
252 }
253 
LayoutToSrcAccessMask(const VkImageLayout layout)254 VkAccessFlags GrVkMemory::LayoutToSrcAccessMask(const VkImageLayout layout) {
255     // Currently we assume we will never being doing any explict shader writes (this doesn't include
256     // color attachment or depth/stencil writes). So we will ignore the
257     // VK_MEMORY_OUTPUT_SHADER_WRITE_BIT.
258 
259     // We can only directly access the host memory if we are in preinitialized or general layout,
260     // and the image is linear.
261     // TODO: Add check for linear here so we are not always adding host to general, and we should
262     //       only be in preinitialized if we are linear
263     VkAccessFlags flags = 0;;
264     if (VK_IMAGE_LAYOUT_GENERAL == layout) {
265         flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
266                 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
267                 VK_ACCESS_TRANSFER_WRITE_BIT |
268                 VK_ACCESS_TRANSFER_READ_BIT |
269                 VK_ACCESS_SHADER_READ_BIT |
270                 VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_HOST_READ_BIT;
271     } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
272         flags = VK_ACCESS_HOST_WRITE_BIT;
273     } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
274         flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
275     } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) {
276         flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
277     } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
278         flags = VK_ACCESS_TRANSFER_WRITE_BIT;
279     } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) {
280         flags = VK_ACCESS_TRANSFER_READ_BIT;
281     } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
282         flags = VK_ACCESS_SHADER_READ_BIT;
283     }
284     return flags;
285 }
286 
FlushMappedAlloc(const GrVkGpu * gpu,const GrVkAlloc & alloc)287 void GrVkMemory::FlushMappedAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc) {
288     if (alloc.fFlags & GrVkAlloc::kNoncoherent_Flag) {
289         VkMappedMemoryRange mappedMemoryRange;
290         memset(&mappedMemoryRange, 0, sizeof(VkMappedMemoryRange));
291         mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
292         mappedMemoryRange.memory = alloc.fMemory;
293         mappedMemoryRange.offset = alloc.fOffset;
294         mappedMemoryRange.size = alloc.fSize;
295         GR_VK_CALL(gpu->vkInterface(), FlushMappedMemoryRanges(gpu->device(),
296                                                                1, &mappedMemoryRange));
297     }
298 }
299 
InvalidateMappedAlloc(const GrVkGpu * gpu,const GrVkAlloc & alloc)300 void GrVkMemory::InvalidateMappedAlloc(const GrVkGpu* gpu, const GrVkAlloc& alloc) {
301     if (alloc.fFlags & GrVkAlloc::kNoncoherent_Flag) {
302         VkMappedMemoryRange mappedMemoryRange;
303         memset(&mappedMemoryRange, 0, sizeof(VkMappedMemoryRange));
304         mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
305         mappedMemoryRange.memory = alloc.fMemory;
306         mappedMemoryRange.offset = alloc.fOffset;
307         mappedMemoryRange.size = alloc.fSize;
308         GR_VK_CALL(gpu->vkInterface(), InvalidateMappedMemoryRanges(gpu->device(),
309                                                                1, &mappedMemoryRange));
310     }
311 }
312 
alloc(VkDeviceSize requestedSize,VkDeviceSize * allocOffset,VkDeviceSize * allocSize)313 bool GrVkFreeListAlloc::alloc(VkDeviceSize requestedSize,
314                               VkDeviceSize* allocOffset, VkDeviceSize* allocSize) {
315     VkDeviceSize alignedSize = align_size(requestedSize, fAlignment);
316 
317     // find the smallest block big enough for our allocation
318     FreeList::Iter iter = fFreeList.headIter();
319     FreeList::Iter bestFitIter;
320     VkDeviceSize   bestFitSize = fSize + 1;
321     VkDeviceSize   secondLargestSize = 0;
322     VkDeviceSize   secondLargestOffset = 0;
323     while (iter.get()) {
324         Block* block = iter.get();
325         // need to adjust size to match desired alignment
326         SkASSERT(align_size(block->fOffset, fAlignment) - block->fOffset == 0);
327         if (block->fSize >= alignedSize && block->fSize < bestFitSize) {
328             bestFitIter = iter;
329             bestFitSize = block->fSize;
330         }
331         if (secondLargestSize < block->fSize && block->fOffset != fLargestBlockOffset) {
332             secondLargestSize = block->fSize;
333             secondLargestOffset = block->fOffset;
334         }
335         iter.next();
336     }
337     SkASSERT(secondLargestSize <= fLargestBlockSize);
338 
339     Block* bestFit = bestFitIter.get();
340     if (bestFit) {
341         SkASSERT(align_size(bestFit->fOffset, fAlignment) == bestFit->fOffset);
342         *allocOffset = bestFit->fOffset;
343         *allocSize = alignedSize;
344         // adjust or remove current block
345         VkDeviceSize originalBestFitOffset = bestFit->fOffset;
346         if (bestFit->fSize > alignedSize) {
347             bestFit->fOffset += alignedSize;
348             bestFit->fSize -= alignedSize;
349             if (fLargestBlockOffset == originalBestFitOffset) {
350                 if (bestFit->fSize >= secondLargestSize) {
351                     fLargestBlockSize = bestFit->fSize;
352                     fLargestBlockOffset = bestFit->fOffset;
353                 } else {
354                     fLargestBlockSize = secondLargestSize;
355                     fLargestBlockOffset = secondLargestOffset;
356                 }
357             }
358 #ifdef SK_DEBUG
359             VkDeviceSize largestSize = 0;
360             iter = fFreeList.headIter();
361             while (iter.get()) {
362                 Block* block = iter.get();
363                 if (largestSize < block->fSize) {
364                     largestSize = block->fSize;
365                 }
366                 iter.next();
367             }
368             SkASSERT(largestSize == fLargestBlockSize);
369 #endif
370         } else {
371             SkASSERT(bestFit->fSize == alignedSize);
372             if (fLargestBlockOffset == originalBestFitOffset) {
373                 fLargestBlockSize = secondLargestSize;
374                 fLargestBlockOffset = secondLargestOffset;
375             }
376             fFreeList.remove(bestFit);
377 #ifdef SK_DEBUG
378             VkDeviceSize largestSize = 0;
379             iter = fFreeList.headIter();
380             while (iter.get()) {
381                 Block* block = iter.get();
382                 if (largestSize < block->fSize) {
383                     largestSize = block->fSize;
384                 }
385                 iter.next();
386             }
387             SkASSERT(largestSize == fLargestBlockSize);
388 #endif
389         }
390         fFreeSize -= alignedSize;
391         SkASSERT(*allocSize > 0);
392 
393         return true;
394     }
395 
396     SkDebugf("Can't allocate %d bytes, %d bytes available, largest free block %d\n", alignedSize, fFreeSize, fLargestBlockSize);
397 
398     return false;
399 }
400 
free(VkDeviceSize allocOffset,VkDeviceSize allocSize)401 void GrVkFreeListAlloc::free(VkDeviceSize allocOffset, VkDeviceSize allocSize) {
402     // find the block right after this allocation
403     FreeList::Iter iter = fFreeList.headIter();
404     FreeList::Iter prev;
405     while (iter.get() && iter.get()->fOffset < allocOffset) {
406         prev = iter;
407         iter.next();
408     }
409     // we have four cases:
410     // we exactly follow the previous one
411     Block* block;
412     if (prev.get() && prev.get()->fOffset + prev.get()->fSize == allocOffset) {
413         block = prev.get();
414         block->fSize += allocSize;
415         if (block->fOffset == fLargestBlockOffset) {
416             fLargestBlockSize = block->fSize;
417         }
418         // and additionally we may exactly precede the next one
419         if (iter.get() && iter.get()->fOffset == allocOffset + allocSize) {
420             block->fSize += iter.get()->fSize;
421             if (iter.get()->fOffset == fLargestBlockOffset) {
422                 fLargestBlockOffset = block->fOffset;
423                 fLargestBlockSize = block->fSize;
424             }
425             fFreeList.remove(iter.get());
426         }
427     // or we only exactly proceed the next one
428     } else if (iter.get() && iter.get()->fOffset == allocOffset + allocSize) {
429         block = iter.get();
430         block->fSize += allocSize;
431         if (block->fOffset == fLargestBlockOffset) {
432             fLargestBlockOffset = allocOffset;
433             fLargestBlockSize = block->fSize;
434         }
435         block->fOffset = allocOffset;
436     // or we fall somewhere in between, with gaps
437     } else {
438         block = fFreeList.addBefore(iter);
439         block->fOffset = allocOffset;
440         block->fSize = allocSize;
441     }
442     fFreeSize += allocSize;
443     if (block->fSize > fLargestBlockSize) {
444         fLargestBlockSize = block->fSize;
445         fLargestBlockOffset = block->fOffset;
446     }
447 
448 #ifdef SK_DEBUG
449     VkDeviceSize   largestSize = 0;
450     iter = fFreeList.headIter();
451     while (iter.get()) {
452         Block* block = iter.get();
453         if (largestSize < block->fSize) {
454             largestSize = block->fSize;
455         }
456         iter.next();
457     }
458     SkASSERT(fLargestBlockSize == largestSize);
459 #endif
460 }
461 
GrVkSubHeap(const GrVkGpu * gpu,uint32_t memoryTypeIndex,uint32_t heapIndex,VkDeviceSize size,VkDeviceSize alignment)462 GrVkSubHeap::GrVkSubHeap(const GrVkGpu* gpu, uint32_t memoryTypeIndex, uint32_t heapIndex,
463                          VkDeviceSize size, VkDeviceSize alignment)
464     : INHERITED(size, alignment)
465     , fGpu(gpu)
466 #ifdef SK_DEBUG
467     , fHeapIndex(heapIndex)
468 #endif
469     , fMemoryTypeIndex(memoryTypeIndex) {
470 
471     VkMemoryAllocateInfo allocInfo = {
472         VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,      // sType
473         NULL,                                        // pNext
474         size,                                        // allocationSize
475         memoryTypeIndex,                             // memoryTypeIndex
476     };
477 
478     VkResult err = GR_VK_CALL(gpu->vkInterface(), AllocateMemory(gpu->device(),
479                                                                  &allocInfo,
480                                                                  nullptr,
481                                                                  &fAlloc));
482     if (VK_SUCCESS != err) {
483         this->reset();
484     }
485 #ifdef SK_DEBUG
486     else {
487         gHeapUsage[heapIndex] += size;
488     }
489 #endif
490 }
491 
~GrVkSubHeap()492 GrVkSubHeap::~GrVkSubHeap() {
493     const GrVkInterface* iface = fGpu->vkInterface();
494     GR_VK_CALL(iface, FreeMemory(fGpu->device(), fAlloc, nullptr));
495 #ifdef SK_DEBUG
496     gHeapUsage[fHeapIndex] -= fSize;
497 #endif
498 }
499 
alloc(VkDeviceSize size,GrVkAlloc * alloc)500 bool GrVkSubHeap::alloc(VkDeviceSize size, GrVkAlloc* alloc) {
501     alloc->fMemory = fAlloc;
502     return INHERITED::alloc(size, &alloc->fOffset, &alloc->fSize);
503 }
504 
free(const GrVkAlloc & alloc)505 void GrVkSubHeap::free(const GrVkAlloc& alloc) {
506     SkASSERT(alloc.fMemory == fAlloc);
507 
508     INHERITED::free(alloc.fOffset, alloc.fSize);
509 }
510 
subAlloc(VkDeviceSize size,VkDeviceSize alignment,uint32_t memoryTypeIndex,uint32_t heapIndex,GrVkAlloc * alloc)511 bool GrVkHeap::subAlloc(VkDeviceSize size, VkDeviceSize alignment,
512                         uint32_t memoryTypeIndex, uint32_t heapIndex, GrVkAlloc* alloc) {
513     VkDeviceSize alignedSize = align_size(size, alignment);
514 
515     // if requested is larger than our subheap allocation, just alloc directly
516     if (alignedSize > fSubHeapSize) {
517         VkMemoryAllocateInfo allocInfo = {
518             VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,      // sType
519             NULL,                                        // pNext
520             size,                                        // allocationSize
521             memoryTypeIndex,                             // memoryTypeIndex
522         };
523 
524         VkResult err = GR_VK_CALL(fGpu->vkInterface(), AllocateMemory(fGpu->device(),
525                                                                       &allocInfo,
526                                                                       nullptr,
527                                                                       &alloc->fMemory));
528         if (VK_SUCCESS != err) {
529             return false;
530         }
531         alloc->fOffset = 0;
532         alloc->fSize = 0;    // hint that this is not a subheap allocation
533 #ifdef SK_DEBUG
534         gHeapUsage[VK_MAX_MEMORY_HEAPS] += alignedSize;
535 #endif
536 
537         return true;
538     }
539 
540     // first try to find a subheap that fits our allocation request
541     int bestFitIndex = -1;
542     VkDeviceSize bestFitSize = 0x7FFFFFFF;
543     for (auto i = 0; i < fSubHeaps.count(); ++i) {
544         if (fSubHeaps[i]->memoryTypeIndex() == memoryTypeIndex &&
545             fSubHeaps[i]->alignment() == alignment) {
546             VkDeviceSize heapSize = fSubHeaps[i]->largestBlockSize();
547             if (heapSize >= alignedSize && heapSize < bestFitSize) {
548                 bestFitIndex = i;
549                 bestFitSize = heapSize;
550             }
551         }
552     }
553 
554     if (bestFitIndex >= 0) {
555         SkASSERT(fSubHeaps[bestFitIndex]->alignment() == alignment);
556         if (fSubHeaps[bestFitIndex]->alloc(size, alloc)) {
557             fUsedSize += alloc->fSize;
558             return true;
559         }
560         return false;
561     }
562 
563     // need to allocate a new subheap
564     std::unique_ptr<GrVkSubHeap>& subHeap = fSubHeaps.push_back();
565     subHeap.reset(new GrVkSubHeap(fGpu, memoryTypeIndex, heapIndex, fSubHeapSize, alignment));
566     // try to recover from failed allocation by only allocating what we need
567     if (subHeap->size() == 0) {
568         VkDeviceSize alignedSize = align_size(size, alignment);
569         subHeap.reset(new GrVkSubHeap(fGpu, memoryTypeIndex, heapIndex, alignedSize, alignment));
570         if (subHeap->size() == 0) {
571             return false;
572         }
573     }
574     fAllocSize += fSubHeapSize;
575     if (subHeap->alloc(size, alloc)) {
576         fUsedSize += alloc->fSize;
577         return true;
578     }
579 
580     return false;
581 }
582 
singleAlloc(VkDeviceSize size,VkDeviceSize alignment,uint32_t memoryTypeIndex,uint32_t heapIndex,GrVkAlloc * alloc)583 bool GrVkHeap::singleAlloc(VkDeviceSize size, VkDeviceSize alignment,
584                            uint32_t memoryTypeIndex, uint32_t heapIndex, GrVkAlloc* alloc) {
585     VkDeviceSize alignedSize = align_size(size, alignment);
586 
587     // first try to find an unallocated subheap that fits our allocation request
588     int bestFitIndex = -1;
589     VkDeviceSize bestFitSize = 0x7FFFFFFF;
590     for (auto i = 0; i < fSubHeaps.count(); ++i) {
591         if (fSubHeaps[i]->memoryTypeIndex() == memoryTypeIndex &&
592             fSubHeaps[i]->alignment() == alignment &&
593             fSubHeaps[i]->unallocated()) {
594             VkDeviceSize heapSize = fSubHeaps[i]->size();
595             if (heapSize >= alignedSize && heapSize < bestFitSize) {
596                 bestFitIndex = i;
597                 bestFitSize = heapSize;
598             }
599         }
600     }
601 
602     if (bestFitIndex >= 0) {
603         SkASSERT(fSubHeaps[bestFitIndex]->alignment() == alignment);
604         if (fSubHeaps[bestFitIndex]->alloc(size, alloc)) {
605             fUsedSize += alloc->fSize;
606             return true;
607         }
608         return false;
609     }
610 
611     // need to allocate a new subheap
612     std::unique_ptr<GrVkSubHeap>& subHeap = fSubHeaps.push_back();
613     subHeap.reset(new GrVkSubHeap(fGpu, memoryTypeIndex, heapIndex, alignedSize, alignment));
614     fAllocSize += alignedSize;
615     if (subHeap->alloc(size, alloc)) {
616         fUsedSize += alloc->fSize;
617         return true;
618     }
619 
620     return false;
621 }
622 
free(const GrVkAlloc & alloc)623 bool GrVkHeap::free(const GrVkAlloc& alloc) {
624     // a size of 0 means we're using the system heap
625     if (0 == alloc.fSize) {
626         const GrVkInterface* iface = fGpu->vkInterface();
627         GR_VK_CALL(iface, FreeMemory(fGpu->device(), alloc.fMemory, nullptr));
628         return true;
629     }
630 
631     for (auto i = 0; i < fSubHeaps.count(); ++i) {
632         if (fSubHeaps[i]->memory() == alloc.fMemory) {
633             fSubHeaps[i]->free(alloc);
634             fUsedSize -= alloc.fSize;
635             return true;
636         }
637     }
638 
639     return false;
640 }
641 
642 
643