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
16VkDeviceSize gHeapUsage[VK_MAX_MEMORY_HEAPS+1] = { 0 };
17#endif
18
19static 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
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::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
55bool 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
122void 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
130static uint64_t gTotalImageMemory = 0;
131static uint64_t gTotalImageMemoryFullPage = 0;
132
133const VkDeviceSize kMaxSmallImageSize = 16 * 1024;
134const VkDeviceSize kMinVulkanPageSize = 16 * 1024;
135
136static VkDeviceSize align_size(VkDeviceSize size, VkDeviceSize alignment) {
137    return (size + alignment - 1) & ~(alignment - 1);
138}
139
140bool 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
215void 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
235VkPipelineStageFlags 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
254VkAccessFlags 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
287void 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
300void 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
313bool 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
401void 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
462GrVkSubHeap::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
492GrVkSubHeap::~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
500bool GrVkSubHeap::alloc(VkDeviceSize size, GrVkAlloc* alloc) {
501    alloc->fMemory = fAlloc;
502    return INHERITED::alloc(size, &alloc->fOffset, &alloc->fSize);
503}
504
505void GrVkSubHeap::free(const GrVkAlloc& alloc) {
506    SkASSERT(alloc.fMemory == fAlloc);
507
508    INHERITED::free(alloc.fOffset, alloc.fSize);
509}
510
511bool 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
583bool 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
623bool 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