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