910e62b5创建于 1月15日历史提交
// 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