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