Vulkan Specification(Vulkan规范):第十章 10.2 设备内存

本文深入解析Vulkan API中设备内存的管理方式,包括物理设备内存属性的查询、内存类型的分配与释放,以及如何通过不同内存属性优化资源使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设备内存是设备可见的内存,例如可以被设备原生访问的不透明图片的内容,或者常驻设备内存的统一缓冲区对象。

物理设备的内存属性描述了内存堆和可用的内存类型。

可调用如下命令来查询内存属性:

void vkGetPhysicalDeviceMemoryProperties(
    VkPhysicalDevice                            physicalDevice,
    VkPhysicalDeviceMemoryProperties*           pMemoryProperties);
  • physicalDevice 是被查询内存所在的设备的handle。

  • pMemoryProperties 指向VkPhysicalDeviceMemoryProperties 类型的数据结构,属性信息由此返回。

Valid Usage (Implicit)

  • physicalDevice must be a valid VkPhysicalDevice handle

  • pMemoryProperties must be a pointer to a VkPhysicalDeviceMemoryProperties structure

VkPhysicalDeviceMemoryProperties数据类型定义如下:

typedef struct VkPhysicalDeviceMemoryProperties {
    uint32_t        memoryTypeCount;
    VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];
    uint32_t        memoryHeapCount;
    VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;
  • memoryTypeCount 是memoryTypes 数组中有效元素的个数。

  • memoryTypes 是一个 VkMemoryType 类型的数组,描述了 可以用来访问memoryHeaps从堆上分配出来的内存的 内存类型

  • memoryHeapCount 是 memoryHeaps 数组中有效元素的个数。

  • memoryHeaps 是一个 VkMemoryHeap 数组,描述了 memory heaps ,可以从它分配内存。

VkPhysicalDeviceMemoryProperties 类型数据结构描述了多个 memory heaps 和一系列 memory types ,用这些type参数可以访问到从堆分配出来的内存。 每一个堆描述了特定大小的内存资源,每一个内存类型描述了内存的属性(例如,主机端缓存 vs 未缓存),属性需应用到内存堆上。 用特定内存类型分配内存将从 内存类型的堆索引表示的堆 中消耗资源。 多个内存类型可能共享一个内存对,堆和内存类型提供了一种机制,来通知物理内存资源的准确大小,且允许内存被用作各种用途。

内存堆的个数由 memoryHeapCount 给出,比VK_MAX_MEMORY_HEAPS 要小。 每一个堆通过memoryHeaps 数组中的一个元素(VkMemoryHeap类型)来描述。 各个内存堆中可用的可用类型的个数由memoryTypeCount 给出,不大于VK_MAX_MEMORY_TYPES。 每一个内存类型通过memoryTypes数组的一个元素( VkMemoryType 类型)来描述。

至少一个堆 必须:在VkMemoryHeap::flags 里包含VK_MEMORY_HEAP_DEVICE_LOCAL_BIT 标志位。 若有多个堆拥有类似的性能特点,它们可能都包含VK_MEMORY_HEAP_DEVICE_LOCAL_BIT

在统一内存架构(UMA)系统中,经常只有单块儿内存堆,它被主机端和设备端都认为是"`local`" 的,这种情况下Vulkan实现必须要把内存堆声明为 device-local。

通过vkGetPhysicalDeviceMemoryProperties 返回的每一个内存类型必须带有如下的标志位之一:

  • 0

  • VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT

  • VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT

  • VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT

  • VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT

  • VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT

  • VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT

  • VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT

  • VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT

一定存在至少一种内存类型,VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT 和 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 标志位都在 propertyFlags中。 一定存在至少一种内存类型,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 标志位在 propertyFlags中。

内存类型根据预定义的顺序被排序了,预定义的顺序有助于更容易选择合适的内存类型。 假设被给定两种内存类型 X与Y,若满足如下,预定义的顺序是 X ≤ Y :

  • X的内存属性标志位必须严格是 Y 的内存属性标志位的子集。或者,

  • X的内存属性标志位和Y 的内存属性标志位完全相同,且X 使用的内存堆大于等于Y的性能。(取决于Vulkan实现特定的方式)。

内存类型被排序,若按照前序, (X ≤ Y) ∧ ¬ (Y ≤ X), X的memoryTypeIndex 小于等于 Y。 注意,上述的 允许的内存属性标志位结合方式的列表满足此前序,但是,其他顺序也是可以的。 此排序的目的是让应用程序使用一种简单的方式来遍历合适的内存类型,形式类似于如下:

// Find a memory type in "memoryTypeBits" that includes all of "properties"
int32_t FindProperties(uint32_t memoryTypeBits, VkMemoryPropertyFlags properties)
{
    for (int32_t i = 0; i < memoryTypeCount; ++i)
    {
        if ((memoryTypeBits & (1 << i)) &&
            ((memoryTypes[i].propertyFlags & properties) == properties))
            return i;
    }
    return -1;
}

// Try to find an optimal memory type, or if it does not exist
// find any compatible memory type
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements(device, image, &memoryRequirements);
int32_t memoryType = FindProperties(memoryRequirements.memoryTypeBits, optimalProperties);
if (memoryType == -1)
    memoryType = FindProperties(memoryRequirements.memoryTypeBits, requiredProperties);

此循环将会找到被支持的内存类型,它符合 properties 中所有的标志位。 若不存在匹配,将寻找最接近匹配(即,内存类型其他的标志位最少),它有其他的标志位,但是并不能决定 properties 要求的行为方式。 应用程序可首先寻找最优的属性,如内存类型是device-local或者支持缓存一致性,尽量接近目标使用方式,若此种内存类型 无法找到,将退而求其次的寻找次优解,但仍保证诸如 "0" 或者 "host-visible and coherent"的属性。

VkMemoryHeap 类型数据结构定义如下:

typedef struct VkMemoryHeap {
    VkDeviceSize         size;
    VkMemoryHeapFlags    flags;
} VkMemoryHeap;
  • size 是堆中总内存的大小,以字节为单位。

  • flags 是描述堆的属性标志位的掩码。可用bit 如下:

    typedef enum VkMemoryHeapFlagBits {
        VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001,
    } VkMemoryHeapFlagBits;
    • flags 包含 VK_MEMORY_HEAP_DEVICE_LOCAL_BIT,它意味着堆内存对应着 device local 内存。 Device local 内存可能与 host local 内存的性能特征有所不同,且支持不同的内存属性标志位。

VkMemoryType 类型数据结构定义如下:

typedef struct VkMemoryType {
    VkMemoryPropertyFlags    propertyFlags;
    uint32_t                 heapIndex;
} VkMemoryType;
  • heapIndex describes which memory heap this memory type corresponds to, and must be less than memoryHeapCount from the VkPhysicalDeviceMemoryProperties structure.

  • propertyFlags is a bitmask of properties for this memory type. The bits specified in propertyFlags are:

    typedef enum VkMemoryPropertyFlagBits {
        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001,
        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002,
        VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004,
        VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008,
        VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010,
    } VkMemoryPropertyFlagBits;
    • if propertyFlags has the VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT bit set, memory allocated with this type is the most efficient for device access. This property will only be set for memory types belonging to heaps with the VK_MEMORY_HEAP_DEVICE_LOCAL_BIT set.

    • if propertyFlags has the VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit set, memory allocated with this type can be mapped for host access using vkMapMemory.

    • if propertyFlags has the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set, host cache management commands vkFlushMappedMemoryRanges and vkInvalidateMappedMemoryRanges are not needed to make host writes visible to the device or device writes visible to the host, respectively.

    • if propertyFlags has the VK_MEMORY_PROPERTY_HOST_CACHED_BIT bit set, memory allocated with this type is cached on the host. Host memory accesses to uncached memory are slower than to cached memory, however uncached memory is always host coherent.

    • if propertyFlags has the VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT bit set, the memory type only allows device access to the memory. Memory types must not have both VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT and VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT set. Additionally, the object’s backing memory may be provided by the implementation lazily as specified in Lazily Allocated Memory.

一个Vulkan设备通过内存对象操作设备内存上的数据,内存对象由API 的VkDeviceMemory handle表示。

内存对象通过VkDeviceMemory handle表示:

VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory)

可调用如下命令来分配内存对象:

VkResult vkAllocateMemory(
    VkDevice                                    device,
    const VkMemoryAllocateInfo*                 pAllocateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDeviceMemory*                             pMemory);
  • device 是拥有内存的逻辑设备。

  • pAllocateInfo 是一个指向VkMemoryAllocateInfo 类型对象的指针,描述了分配行为的参数。一次成功的内存分配必须要使用要求的参数, 不允许Vulkan实现替换任何参数。

  • pAllocator 控制了主机端内存分配,如 内存分配 一章所描述的。

  • pMemory 是一个指向VkDeviceMemory 类型handle的指针,通过它返回内存分配相关的信息。

vkAllocateMemory返回的分配的内存被Vulkan实现保证按照要求对齐。 例如,应用程序要求对图像进行128 byte对齐,对缓冲区进行64 byte对齐,通过这种机制被返回的设备内存将按照128 byte对齐。 这保证应用程序能正确的在同一个内存对象中对不同的子类型进行内存分配(按照不同的内存对齐要求)。

当内存分配好了,它的内容是未定义的。

在单个设备上同时进行内存分配的最大次数是有限制的,依赖于Vulkan实现。 这是由VkPhysicalDeviceLimits 的maxMemoryAllocationCount 成员决定的。 若超过了maxMemoryAllocationCount ,vkAllocateMemory 将返回VK_ERROR_TOO_MANY_OBJECTS

 

注意

一些平台也许堆但此内存分配有大小限制。例如,某些系统将不能分配大于等于4GB的内存。 这个限制依赖于Vulkan实现,如果出现了此类错误,应该返回VK_ERROR_OUT_OF_DEVICE_MEMORY 。

正确使用

  • The number of currently valid memory objects, allocated from devicemust be less than VkPhysicalDeviceLimits::maxMemoryAllocationCount

Valid Usage (Implicit)

  • device must be a valid VkDevice handle

  • pAllocateInfo must be a pointer to a valid VkMemoryAllocateInfo structure

  • If pAllocator is not NULLpAllocator must be a pointer to a valid VkAllocationCallbacks structure

  • pMemory must be a pointer to a VkDeviceMemory handle

Return Codes

Success

  • VK_SUCCESS

Failure

  • VK_ERROR_OUT_OF_HOST_MEMORY

  • VK_ERROR_OUT_OF_DEVICE_MEMORY

  • VK_ERROR_TOO_MANY_OBJECTS

VkMemoryAllocateInfo 类型数据结构定义如下:

typedef struct VkMemoryAllocateInfo {
    VkStructureType    sType;
    const void*        pNext;
    VkDeviceSize       allocationSize;
    uint32_t           memoryTypeIndex;
} VkMemoryAllocateInfo;
  • sType 是数据结构的类型。

  • pNext 是 NULL 或者一个指向拓展特定的数据结构的指针。

  • allocationSize 是分配的内存大小,以字节为单位。

  • memoryTypeIndex 是内存类型的索引,它选择了将分配的内存的属性和从哪个堆分配内存的属性。

正确使用

  • allocationSize 必须 小于等于 memoryTypeIndex 和调用命令的VkDevice 指定的VkMemoryHeap 的 可用内存总量,

  • allocationSize 必须大于 0

Valid Usage (Implicit)

  • sType must be VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO

  • pNext must be NULL

可调用如下命令来释放内存对象:

void vkFreeMemory(
    VkDevice                                    device,
    VkDeviceMemory                              memory,
    const VkAllocationCallbacks*                pAllocator);
  • device 是拥有内存对象的逻辑设备。

  • memory 是需要被释放的VkDeviceMemory 对象。

  • pAllocator 控制了CPU端内存分配,如 Memory Allocation 一章所述。

在释放内存对象之前,应用程序必须确保内存对象不再被设备使用(如被命令缓冲区排队执行)。 在内存对象被释放时,内存仍绑定在图像或者缓冲区上,但是(在CPU或者设备端)进一步使用它们将造成这些对象被破坏, 导致未知结果。 若仍存在绑定的图像或者缓冲区,内存将不会被Vulkan实现立即释放,但是,在绑定的图像或者缓冲区被销毁时, 内存会被立即释放。 一旦内存被释放,它将被送还给heap,

关于内存对象如何绑定到图像、缓冲区,在Resource Memory Association 一节中有详细描述。.

若内存对象在释放时仍处于映射状态,那么它被隐式的解除了映射。

正确使用

  • All submitted commands that refer to memory (via images or buffers) must have completed execution

Valid Usage (Implicit)

  • device must be a valid VkDevice handle

  • If memory is not VK_NULL_HANDLEmemory must be a valid VkDeviceMemory handle

  • If pAllocator is not NULLpAllocator must be a pointer to a valid VkAllocationCallbacks structure

  • If memory is a valid handle, it must have been created, allocated, or retrieved from device

Host Synchronization

  • Host access to memory must be externally synchronized

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值