| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/vulkan/skia_vk_memory_allocator_impl.h" |
| |
| #include <vk_mem_alloc.h> |
| #include <vulkan/vulkan_core.h> |
| |
| #include <array> |
| |
| #include "base/feature_list.h" |
| #include "base/trace_event/trace_event.h" |
| #include "gpu/vulkan/vulkan_device_queue.h" |
| #include "gpu/vulkan/vulkan_function_pointers.h" |
| |
| namespace gpu { |
| |
| SkiaVulkanMemoryAllocator::SkiaVulkanMemoryAllocator(VmaAllocator allocator) |
| : allocator_(allocator) {} |
| |
| VkResult SkiaVulkanMemoryAllocator::allocateImageMemory( |
| VkImage image, |
| uint32_t flags, |
| skgpu::VulkanBackendMemory* backend_memory) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::allocateMemoryForImage"); |
| VmaAllocationCreateInfo info = { |
| .flags = 0, |
| .usage = VMA_MEMORY_USAGE_UNKNOWN, |
| .requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, |
| .preferredFlags = 0, |
| .memoryTypeBits = 0, |
| .pool = VK_NULL_HANDLE, |
| .pUserData = nullptr, |
| }; |
| |
| if (kDedicatedAllocation_AllocationPropertyFlag & flags) { |
| info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
| } |
| |
| if (kLazyAllocation_AllocationPropertyFlag & flags) { |
| // If the caller asked for lazy allocation then they already set up the |
| // VkImage for it so we must require the lazy property. |
| info.requiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; |
| // Make the transient allocation a dedicated allocation for tracking |
| // memory separately. |
| info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
| } |
| |
| if (kProtected_AllocationPropertyFlag & flags) { |
| info.requiredFlags |= VK_MEMORY_PROPERTY_PROTECTED_BIT; |
| } |
| |
| VmaAllocation allocation; |
| VkResult result = vma::AllocateMemoryForImage(allocator_, image, &info, |
| &allocation, nullptr); |
| if (VK_SUCCESS == result) { |
| if (info.requiredFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { |
| VmaAllocationInfo vma_info; |
| vma::GetAllocationInfo(allocator_, allocation, &vma_info); |
| lazy_allocated_size_ += vma_info.size; |
| } |
| *backend_memory = reinterpret_cast<skgpu::VulkanBackendMemory>(allocation); |
| } |
| |
| return result; |
| } |
| |
| VkResult SkiaVulkanMemoryAllocator::allocateBufferMemory( |
| VkBuffer buffer, |
| BufferUsage usage, |
| uint32_t flags, |
| skgpu::VulkanBackendMemory* backend_memory) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::allocateMemoryForBuffer"); |
| VmaAllocationCreateInfo info = { |
| .flags = 0, |
| .usage = VMA_MEMORY_USAGE_UNKNOWN, |
| .memoryTypeBits = 0, |
| .pool = VK_NULL_HANDLE, |
| .pUserData = nullptr, |
| }; |
| |
| switch (usage) { |
| case BufferUsage::kGpuOnly: |
| info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| info.preferredFlags = 0; |
| break; |
| case BufferUsage::kCpuWritesGpuReads: |
| info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; |
| break; |
| case BufferUsage::kTransfersFromCpuToGpu: |
| info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | |
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| info.preferredFlags = 0; |
| break; |
| case BufferUsage::kTransfersFromGpuToCpu: |
| info.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
| info.preferredFlags = VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
| break; |
| } |
| |
| if (kDedicatedAllocation_AllocationPropertyFlag & flags) { |
| info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
| } |
| |
| if ((kLazyAllocation_AllocationPropertyFlag & flags) && |
| BufferUsage::kGpuOnly == usage) { |
| info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; |
| // Make the transient allocation a dedicated allocation for tracking |
| // memory separately. |
| info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; |
| } |
| |
| if (kPersistentlyMapped_AllocationPropertyFlag & flags) { |
| SkASSERT(BufferUsage::kGpuOnly != usage); |
| info.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; |
| } |
| |
| VmaAllocation allocation; |
| VkResult result = vma::AllocateMemoryForBuffer(allocator_, buffer, &info, |
| &allocation, nullptr); |
| if (VK_SUCCESS == result) { |
| if (info.preferredFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { |
| VmaAllocationInfo vma_info; |
| vma::GetAllocationInfo(allocator_, allocation, &vma_info); |
| lazy_allocated_size_ += vma_info.size; |
| } |
| *backend_memory = reinterpret_cast<skgpu::VulkanBackendMemory>(allocation); |
| } |
| |
| return result; |
| } |
| |
| void SkiaVulkanMemoryAllocator::freeMemory( |
| const skgpu::VulkanBackendMemory& memory) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::freeMemory"); |
| VmaAllocation allocation = reinterpret_cast<const VmaAllocation>(memory); |
| |
| // Update `lazy_allocated_size_` tracking. |
| VmaAllocationInfo vma_info; |
| vma::GetAllocationInfo(allocator_, allocation, &vma_info); |
| VkMemoryPropertyFlags mem_flags; |
| vma::GetMemoryTypeProperties(allocator_, vma_info.memoryType, &mem_flags); |
| if (mem_flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) { |
| lazy_allocated_size_ -= vma_info.size; |
| } |
| |
| vma::FreeMemory(allocator_, allocation); |
| } |
| |
| void SkiaVulkanMemoryAllocator::getAllocInfo( |
| const skgpu::VulkanBackendMemory& memory, |
| skgpu::VulkanAlloc* alloc) const { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::getAllocInfo"); |
| const VmaAllocation allocation = |
| reinterpret_cast<const VmaAllocation>(memory); |
| VmaAllocationInfo vma_info; |
| vma::GetAllocationInfo(allocator_, allocation, &vma_info); |
| |
| VkMemoryPropertyFlags mem_flags; |
| vma::GetMemoryTypeProperties(allocator_, vma_info.memoryType, &mem_flags); |
| |
| uint32_t flags = 0; |
| if (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT & mem_flags) { |
| flags |= skgpu::VulkanAlloc::kMappable_Flag; |
| } |
| if (!(VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & mem_flags)) { |
| flags |= skgpu::VulkanAlloc::kNoncoherent_Flag; |
| } |
| if (VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT & mem_flags) { |
| flags |= skgpu::VulkanAlloc::kLazilyAllocated_Flag; |
| } |
| |
| alloc->fMemory = vma_info.deviceMemory; |
| alloc->fOffset = vma_info.offset; |
| alloc->fSize = vma_info.size; |
| alloc->fFlags = flags; |
| alloc->fBackendMemory = memory; |
| } |
| |
| VkResult SkiaVulkanMemoryAllocator::mapMemory( |
| const skgpu::VulkanBackendMemory& memory, |
| void** data) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::mapMemory"); |
| const VmaAllocation allocation = |
| reinterpret_cast<const VmaAllocation>(memory); |
| return vma::MapMemory(allocator_, allocation, data); |
| } |
| |
| void SkiaVulkanMemoryAllocator::unmapMemory( |
| const skgpu::VulkanBackendMemory& memory) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::unmapMemory"); |
| const VmaAllocation allocation = |
| reinterpret_cast<const VmaAllocation>(memory); |
| vma::UnmapMemory(allocator_, allocation); |
| } |
| |
| VkResult SkiaVulkanMemoryAllocator::flushMemory( |
| const skgpu::VulkanBackendMemory& memory, |
| VkDeviceSize offset, |
| VkDeviceSize size) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::flushMappedMemory"); |
| const VmaAllocation allocation = |
| reinterpret_cast<const VmaAllocation>(memory); |
| return vma::FlushAllocation(allocator_, allocation, offset, size); |
| } |
| |
| VkResult SkiaVulkanMemoryAllocator::invalidateMemory( |
| const skgpu::VulkanBackendMemory& memory, |
| VkDeviceSize offset, |
| VkDeviceSize size) { |
| TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("gpu.vulkan.vma"), |
| "SkiaVulkanMemoryAllocator::invalidateMappedMemory"); |
| const VmaAllocation allocation = |
| reinterpret_cast<const VmaAllocation>(memory); |
| return vma::InvalidateAllocation(allocator_, allocation, offset, size); |
| } |
| |
| std::pair<uint64_t, uint64_t> |
| SkiaVulkanMemoryAllocator::totalAllocatedAndUsedMemory() const { |
| uint64_t total_allocated_memory = 0, total_used_memory = 0; |
| std::array<VmaBudget, VK_MAX_MEMORY_HEAPS> budget; |
| vma::GetBudget(allocator_, budget.data()); |
| const VkPhysicalDeviceMemoryProperties* physical_device_memory_properties; |
| vmaGetMemoryProperties(allocator_, &physical_device_memory_properties); |
| for (uint32_t i = 0; i < physical_device_memory_properties->memoryHeapCount; |
| ++i) { |
| total_allocated_memory += budget[i].statistics.blockBytes; |
| total_used_memory += budget[i].statistics.allocationBytes; |
| } |
| DCHECK_LE(total_used_memory, total_allocated_memory); |
| return {total_allocated_memory, total_used_memory}; |
| } |
| |
| } // namespace gpu |