Aqoole_Hateenaの技術日記

vulkan+raytraceで色々描いてます

Bufferの作成とメモリ割り当てについて

vkCreateBuffer()

vkCreateBuffer - Create a new buffer object

// Provided by VK_VERSION_1_0
VkResult vkCreateBuffer(
    VkDevice                                    device,
    const VkBufferCreateInfo*                   pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkBuffer*                                   pBuffer);

vkCreateBuffer(3)
Bufferの作成にはvkCreateBuffer()を使用する。
作成に当たり、VkBufferCreateInfoを埋める必要がある。

VkBufferCreateInfo

VkBufferCreateInfo - Structure specifying the parameters of a newly created buffer object

// Provided by VK_VERSION_1_0
typedef struct VkBufferCreateInfo {
    VkStructureType        sType;
    const void*            pNext;
    VkBufferCreateFlags    flags;
    VkDeviceSize           size;
    VkBufferUsageFlags     usage;
    VkSharingMode          sharingMode;
    uint32_t               queueFamilyIndexCount;
    const uint32_t*        pQueueFamilyIndices;
} VkBufferCreateInfo;

  • sType is the type of this structure.
  • pNext is NULL or a pointer to a structure extending this structure.
  • flags is a bitmask of VkBufferCreateFlagBits specifying additional parameters of the buffer.
  • size is the size in bytes of the buffer to be created.
  • usage is a bitmask of VkBufferUsageFlagBits specifying allowed usages of the buffer.
  • sharingMode is a VkSharingMode value specifying the sharing mode of the buffer when it will be accessed by multiple queue families.
  • queueFamilyIndexCount is the number of entries in the pQueueFamilyIndices array.
  • pQueueFamilyIndices is a pointer to an array of queue families that will access this buffer. It is ignored if sharingMode is not VK_SHARING_MODE_CONCURRENT.

VkBufferCreateInfo(3)

VkSharingMode

VkSharingMode - Buffer and image sharing modes.
Buffer and image objects are created with a sharing mode controlling how they can be accessed from queues.

Sharing ModeとはQueueからのaccess modeのこと。

// Provided by VK_VERSION_1_0
typedef enum VkSharingMode {
    VK_SHARING_MODE_EXCLUSIVE = 0,
    VK_SHARING_MODE_CONCURRENT = 1,
} VkSharingMode;

メンバはEXCLUSIVEとCONCURRENTの2つのみ。

  • VK_SHARING_MODE_EXCLUSIVE specifies that access to any range or image subresource of the object will be exclusive to a single queue family at a time.
  • VK_SHARING_MODE_CONCURRENT specifies that concurrent access to any range or image subresource of the object from multiple queue families is supported.

VkSharingMode(3)

exclusiveは「排他的な、他を入れない」という意味で、concurrentは「同時」という意味。
exclusive とは 意味・読み方・表現 | Weblio英和辞書
concurrent とは 意味・読み方・表現 | Weblio英和辞書
VK_SHARING_MODE_EXCLUSIVE を指定すると、一度にひとつのqueue familyからしかアクセスできず、VK_SHARING_MODE_CONCURRENT を指定すると一度に複数のqueue familyからアクセスできる。

vkAllocateMemory()

vkAllocateMemory - Allocate device memory

作成したBufferをデバイスメモリに割り当てるには、vkAllocateMemory()を使用する。

// Provided by VK_VERSION_1_0
VkResult vkAllocateMemory(
    VkDevice                                    device,
    const VkMemoryAllocateInfo*                 pAllocateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDeviceMemory*                             pMemory);

  • device is the logical device that owns the memory.
  • pAllocateInfo is a pointer to a VkMemoryAllocateInfo structure describing parameters of the allocation. A successfully returned allocation must use the requested parameters — no substitution is permitted by the implementation.
  • pAllocator controls host memory allocation as described in the Memory Allocation chapter.
  • pMemory is a pointer to a VkDeviceMemory handle in which information about the allocated memory is returned.

vkAllocateMemory(3)

VkMemoryAllocateInfoによる具体的なパラメータの指定が必要。

VkMemoryAllocateInfo

VkMemoryAllocateInfo - Structure containing parameters of a memory allocation

// Provided by VK_VERSION_1_0
typedef struct VkMemoryAllocateInfo {
    VkStructureType    sType;
    const void*        pNext;
    VkDeviceSize       allocationSize;
    uint32_t           memoryTypeIndex;
} VkMemoryAllocateInfo;

  • sType is the type of this structure.
  • pNext is NULL or a pointer to a structure extending this structure.
  • allocationSize is the size of the allocation in bytes.
  • memoryTypeIndex is an index identifying a memory type from the memoryTypes array of the VkPhysicalDeviceMemoryProperties structure.

VkMemoryAllocateInfo(3)

使用したいmemory typeを、VkPhysicalDeviceMemoryPropertiesのmemoryTypes[]のindexで指定する必要がある。

VkPhysicalDeviceMemoryProperties

VkPhysicalDeviceMemoryProperties - Structure specifying physical device memory properties

// Provided by VK_VERSION_1_0
typedef struct VkPhysicalDeviceMemoryProperties {
    uint32_t        memoryTypeCount;
    VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];
    uint32_t        memoryHeapCount;
    VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;

VkPhysicalDeviceMemoryProperties(3)
vkGetBufferMemoryRequirements()でphysical deviceでサポートされているVkPhysicalDeviceMemoryPropertiesが取得できるので、そこから使用するmemory typesのindexを指定してVkMemoryAllocateInfoに記述する。

vkAllocateMemory()の具体例

サンプルコードでよく見かける具体例。

VkMemoryRequirements memReq;
vkGetBufferMemoryRequirements(device, buffer, &memReq);
VkMemoryAllocateInfo allocInfo{
    .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
    .pNext = nullptr,
    .allocationSize = memReq.size,
    .memoryTypeIndex = 0,  // Memory type assigned in the next step
};

vkGetBufferMemoryRequirements()でbuffer objectのmemory requiementを取得する。
vkGetBufferMemoryRequirements(3)
このrequirementでallocInfo.allocationSizeの値を埋める。

VkMemoryRequirements

VkMemoryRequirements - Structure specifying memory requirements

// Provided by VK_VERSION_1_0
typedef struct VkMemoryRequirements {
    VkDeviceSize    size;
    VkDeviceSize    alignment;
    uint32_t        memoryTypeBits;
} VkMemoryRequirements;

  • size is the size, in bytes, of the memory allocation required for the resource.
  • alignment is the alignment, in bytes, of the offset within the allocation required for the resource.
  • memoryTypeBits is a bitmask and contains one bit set for every supported memory type for the resource. Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the physical device is supported for the resource.

VkMemoryRequirements(3)

memoryTypeBitsはphysical deviceでサポートされているビットセットを表す。bitのi番目はVkPhysicalDeviceMemoryProperties.memoryTypesのi番目に対応する。
この事実をもとに、割り当てるメモリで使いたいmemory propertyをもつmemoryTypesは何番目かを調べる。

uint32_t SearchIndex(typeBits, requirements_mask){
  for (uint32_t i = 0; i < memReq.memoryTypeCount; i++) {
    if ((typeBits & 1) == 1) {
      // Type is available, does it match user properties?
      if ((memoryProperties.memoryTypes[i].propertyFlags & requirements_mask) ==
          requirements_mask) {
        return i;
      }
    }
    typeBits >>= 1;
  }
  throw std::runtime_error("failed to find suitable memory types for buffer");
}

typeBitsにはVkMemoryRequirements.memoryTypeBitsを入れ、requirements_maskには指定したいVkFlags(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BITなど)を入れる。
i番目のindexがi番目のtypeBitsのbitに対応しているので、bitが一致するかどうかを確かめる。一致していた場合、使用したいrequirements_maskがmemoryProperties.memoryTypesのi番目であるかどうかを確かめる。一致していた場合、このiが欲しいiなので、returnしてiを受け取る。一致しない場合、typeBitsを一つ右にシフトして、次のiに対応させてからsearchのループをやり直す。
以上の手順により、requirements_maskに対応するmemoryProperties.memoryTypesのindexを取得できる。
これでVkMemoryAllocateInfoが完成する。