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