Vulkan 内存分为两类: 主机内存 和 设备内存 。
10.1. 主机内存
主机内存是Vulkan实现需要的、设备不可见的内存。 这些内存可以用来存储软件内部数据。
Vulkan给应用程序提供了代表Vulkan实现来操作主机内存分配的机会。 如果这个特征没有被使用,Vulkan实现将使用自己的内存分配函数。 因为大多数内存分配都不是在要求高性能代码区域的,这就不是一个提升性能的特征了。 相反,在一些嵌入式的平台上,这还是非常有用的,如为了调试(e.g. putting a guard page after all host allocations), 或者记录内存分配。
内存分配器是应用程序通过一个指向VkAllocationCallbacks
类型数据的指针来提供的。
typedef struct VkAllocationCallbacks {
void* pUserData;
PFN_vkAllocationFunction pfnAllocation;
PFN_vkReallocationFunction pfnReallocation;
PFN_vkFreeFunction pfnFree;
PFN_vkInternalAllocationNotification pfnInternalAllocation;
PFN_vkInternalFreeNotification pfnInternalFree;
} VkAllocationCallbacks;
-
pUserData
是由回调自行解释的值。当VkAllocationCallbacks
之中任何回调函数被调用,Vulkan实现将把这个值作为第一个参数传递给回调函数。 每一次被传递到命令中,这个值都可以改变,甚至在多个命令中同一个对象带有的内存分配器。 -
pfnAllocation
是一个指向应用程序定义的内存分配函数的指针,类型为PFN_vkAllocationFunction
。 -
pfnReallocation
是一个指向应用程序定义的内存重分配函数的指针,类型为PFN_vkReallocationFunction
。 -
pfnFree
是一个指向应用程序定义的内存释放函数,类型为PFN_vkFreeFunction
。 -
pfnInternalAllocation
是一个指向应用程序定义的函数的指针,当被Vulkan实现调用时,就进行内部内存分配,类型为PFN_vkInternalAllocationNotification
。 -
pfnInternalFree
是一个指向应用程序定义的函数的指针,当被Vulkan实现调用时,就释放内部内存,类型为PFN_vkInternalFreeNotification
。
正确使用
-
pfnAllocation
必须: 是一个有效的用户自定义的PFN_vkAllocationFunction
类型的函数指针。 -
pfnReallocation
必须: 是一个指向有效的用户自定义的PFN_vkReallocationFunction
类型的函数指针。 -
pfnFree
必须: 是一个指向有效的用户自定义的PFN_vkFreeFunction
类型指针。 -
如果
pfnInternalAllocation
或者pfnInternalFree
不为NULL
,那么两个都必须: 是有效的回调函数。
pfnAllocation
定义如下:
typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)(
void* pUserData,
size_t size,
size_t alignment,
VkSystemAllocationScope allocationScope);
-
pUserData
是由应用程序指定的内存分配器的VkAllocationCallbacks
::pUserData
指定的。 -
size
是要求分配的内存字节大小。 -
alignment
是内存分配器要求的内存对齐的大小,以字节为单位,必须是2的幂。 -
allocationScope
是一个VkSystemAllocationScope
类型的值,指定了内存分配的生命周期,如 这里所述。
如果pfnAllocation
无法分配要求的内存,它必须返回 NULL
。 如果分配成功,它必须返回包含至少 size
字节内存的指针,且指针的数值必须是 alignment
的倍数。
注意 如果应用程序不遵守以下规则,就不能假定Vulkan会做正确的操作。 例如, |
如果pfnAllocation
返回`NULL`,且如果Vulkan实现因为没有获取要求的内存而不能正确的继续处理当前命令时, Vulkan实现必须把这个当作是运行时错误,在合适的时候给发生这个状况的命令产生一个VK_ERROR_OUT_OF_HOST_MEMORY
错误, 如Return Codes中所描述。
如果Vulkan实现在没有获取到要求分配的内存时能够继续正确的处理当前命令,那么它不能 因这次内存分配失败而产生VK_ERROR_OUT_OF_HOST_MEMORY
。
pfnReallocation
类型如下:
typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)(
void* pUserData,
void* pOriginal,
size_t size,
size_t alignment,
VkSystemAllocationScope allocationScope);
-
pUserData
是由应用程序指定的内存分配器的VkAllocationCallbacks
::pUserData
指定的。 -
pOriginal
必须: 是NULL
或者是 同一个内存分配器的pfnReallocation
或者pfnAllocation
返回的指针。 -
size
是要求分配的内存字节大小。 -
alignment
是内存分配器要求的内存对齐的大小,以字节为单位,必须是2的幂。 -
allocationScope
是一个VkSystemAllocationScope
类型的值,指定了内存分配的生命周期,如 这里所述。
pfnReallocation
必须: 返回size
字节的内存,原内存的内容从zero 到 min(original size, new size) - 1 必须: 被保留在新分配的内存中。 如果 size
比原来的内存要大, 附加部分内存的内容是未定义的。 如果满足这些创建新内存的条件,那么原来分配的内存应被释放掉了。
若 pOriginal
是 NULL
, 那么 pfnReallocation
必须: 表现的和以相同参数(除了pOriginal
)调用PFN_vkAllocationFunction
表现完全相同。
若 size
为0, 那么 pfnReallocation
必须: 表现的和以相同参数pUserData
,pMemory
等同于pOriginal
来调用 PFN_vkFreeFunction
完全相同。
若 pOriginal
是 non-NULL
, Vulkan实现必须 必须: 保证 alignment
等于 分配pOriginal
时所用到的alignment
。
若这个函数调用失败,pOriginal
是 non-NULL
,应用程序不能: 释放掉原来的内存。
pfnReallocation
必须: 符合PFN_vkAllocationFunction
返回值的规则。
pfnFree
类型定义如下:
typedef void (VKAPI_PTR *PFN_vkFreeFunction)(
void* pUserData,
void* pMemory);
-
pUserData
是由应用程序指定的内存分配器的VkAllocationCallbacks
::pUserData
指定的。 -
pMemory
是需要被释放的内存。
pMemory
可能: 是 NULL
, 此时回调函数 必须: 谨慎的处理。 若 pMemory
是 non-NULL
, 它必须: 是一个指针,指向 pfnAllocation
或 pfnReallocation
之前分配的内存。 应用程序 应该: 释放此内存。
pfnInternalAllocation
类型定义如下:
typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)(
void* pUserData,
size_t size,
VkInternalAllocationType allocationType,
VkSystemAllocationScope allocationScope);
-
pUserData
是由应用程序指定的内存分配器的VkAllocationCallbacks
::pUserData
指定的。 -
size
是要求分配的内存字节大小。 -
allocationType
是要求的内存分配的类型。 -
allocationScope
是一个VkSystemAllocationScope
类型的值,指定了内存分配的生命周期,如 这里所述。
这是一个纯粹的查看信息的回调函数。
pfnInternalFree
定义如下:
typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)(
void* pUserData,
size_t size,
VkInternalAllocationType allocationType,
VkSystemAllocationScope allocationScope);
-
pUserData
是由应用程序指定的内存分配器的VkAllocationCallbacks
::pUserData
指定的。 -
size
是要求分配的内存字节大小。 -
allocationType
是要求的内存分配的类型。 -
allocationScope
是一个VkSystemAllocationScope
类型的值,指定了内存分配的生命周期,如 这里所述。
每一份分配的内存都有 分配存活期 ,定义了它自己的生命周期和所关联对象。 内存存活期是通过传递给VkAllocationCallbacks
中定义的回调函数的allocationScope
参数提供的。 VkSystemAllocationScope
定义的可选的值如下:
typedef enum VkSystemAllocationScope {
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1,
VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2,
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4,
} VkSystemAllocationScope;
-
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND
- The allocation is scoped to the duration of the Vulkan command. -
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT
- The allocation is scoped to the lifetime of the Vulkan object that is being created or used. -
VK_SYSTEM_ALLOCATION_SCOPE_CACHE
- The allocation is scoped to the lifetime of aVkPipelineCache
object. -
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE
- The allocation is scoped to the lifetime of the Vulkan device. -
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE
- The allocation is scoped to the lifetime of the Vulkan instance.
大多数Vulkan命令操作单个对象,或者创建、操控单一对象。当一份分配的内存使用 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT
或 VK_SYSTEM_ALLOCATION_SCOPE_CACHE
时,内存的存活范围就是被创建或操控的对象。
当Vulkan实现获取主机端内存时,它将回调应用程序提供的回调函数来使用特定的内存分配器和内存存活范围:
-
若分配的内存生存周期限定于一个命令之内,内存分配器将使用
VK_SYSTEM_ALLOCATION_SCOPE_COMMAND
。 若创建的、或者被操控的对象有对应的内存分配器,那么将使用该特定的内存分配器,否则若 父VkDevice
有内存分配器,便使用此内存分配器,否则便使用父VkInstance
拥有的内存分配器。 -
若 If an allocation is associated with an object of type
VkPipelineCache
, the allocator will use theVK_SYSTEM_ALLOCATION_SCOPE_CACHE
allocation scope. The most specific allocator available is used (pipeline cache, else device, else instance). Else, -
If an allocation is scoped to the lifetime of an object, that object is being created or manipulated by the command, and that object’s type is not
VkDevice
orVkInstance
, the allocator will use an allocation scope ofVK_SYSTEM_ALLOCATION_SCOPE_OBJECT
. The most specific allocator available is used (object, else device, else instance). Else, -
If an allocation is scoped to the lifetime of a device, the allocator will use an allocation scope of
VK_SYSTEM_ALLOCATION_SCOPE_DEVICE
. The most specific allocator available is used (device, else instance). Else, -
If the allocation is scoped to the lifetime of an instance and the instance has an allocator, its allocator will be used with an allocation scope of
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE
. -
Otherwise an implementation will allocate memory through an alternative mechanism that is unspecified.
从缓存池中分配出来的对象并没有指定它们对应的内存分配器。 当Vulkan实现为此对象获取主机端内存时,内存是由父缓存池的内存分配器获取的。
不应期待应用程序会负责主机端执行代码时处理内存分配活动,因为多平台上Vulkan实现的负责的安全性问题。 Vulkan实现将在内部分配内存,当这些内存分配或释放时,调用通知性的回调函数告诉应用程序。 当分配这些可执行的内存时,pfnInternalAllocation
将会被调用。 当释放这些可执行的内存时,pfnInternalFree
将被调用。 当分配或释放这些可执行的内存时,Vulkan实现将只调用通知性的回调函数。
传递给 pfnInternalAllocation
和pfnInternalFree
函数的allocationType
参数 可能: 是如下值:
typedef enum VkInternalAllocationType {
VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0,
} VkInternalAllocationType;
-
VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE
- 分配的内存是计划给CPU端使用的。
Vulkan实现在API命令执行期间,只能调用应用程序在当前线程提供的分配函数。 Vulkan实现不应该同时调用这些回调函数。如果需要保持同步,回调函数应该自己实现。提供信息这一类的回调函数也和内存分配函数一样遵从此限制。
若Vulkan实现倾向于在vkCreate
*命令返回和对应的 vkDestroy
*命令开始之间通过VkAllocationCallbacks
数据结构来进行函数调用,该Vulkan实现必须在vkCreate
*返回之前保存一份内存分配器的copy。 它们依赖的回掉函数和任何数据结构都必须在与之关联的对象生命周期内都保持有效。
若给vkCreate
*提供一个内存分配器,则必须要给对应的vkDestroy
*命令提供一个兼容匹配的内存分配器。 若用pfnAllocation
or pfnReallocation
分配而来的内存都可以被pfnReallocation
or pfnFree
释放掉,那么两个VkAllocationCallbacks
数据结构必须兼容。 An allocator must not be provided to a vkDestroy
* command if an allocator was not provided to the corresponding vkCreate
* command.
If a non-NULL
allocator is used, the pfnAllocation
, pfnReallocation
and pfnFree
members must be non-NULL
and point to valid implementations of the callbacks. An application can choose to not provide informational callbacks by setting both pfnInternalAllocation
and pfnInternalFree
to NULL
. pfnInternalAllocation
and pfnInternalFree
must either both be NULL
or both be non-NULL
.
If pfnAllocation
or pfnReallocation
fail, the implementation may fail object creation and/or generate an VK_ERROR_OUT_OF_HOST_MEMORY
error, as appropriate.
内存分配回调函数不能调用任何Vulkan命令。
The following sets of rules define when an implementation is permitted to call the allocator callbacks.
pfnAllocation
或者 pfnReallocation
可能在一下情形中被调用:
-
Allocations scoped to a
VkDevice
orVkInstance
may be allocated from any API command. -
Allocations scoped to a command may be allocated from any API command.
-
Allocations scoped to a
VkPipelineCache
may only be allocated from:-
vkCreatePipelineCache
-
vkMergePipelineCaches
fordstCache
-
vkCreateGraphicsPipelines
forpPipelineCache
-
vkCreateComputePipelines
forpPipelineCache
-
-
Allocations scoped to a
VkDescriptorPool
may only be allocated from:-
any command that takes the pool as a direct argument
-
vkAllocateDescriptorSets
for thedescriptorPool
member of itspAllocateInfo
parameter -
vkCreateDescriptorPool
-
-
Allocations scoped to a
VkCommandPool
may only be allocated from:-
any command that takes the pool as a direct argument
-
vkCreateCommandPool
-
vkAllocateCommandBuffers
for thecommandPool
member of itspAllocateInfo
parameter -
any
vkCmd
* command whosecommandBuffer
was allocated from thatVkCommandPool
-
-
Allocations scoped to any other object may only be allocated in that object’s
vkCreate
* command.
pfnFree
可能在一下情形中被调用:
-
Allocations scoped to a
VkDevice
orVkInstance
may be freed from any API command. -
Allocations scoped to a command must be freed by any API command which allocates such memory.
-
Allocations scoped to a
VkPipelineCache
may be freed fromvkDestroyPipelineCache
. -
Allocations scoped to a
VkDescriptorPool
may be freed from-
any command that takes the pool as a direct argument
-
-
Allocations scoped to a
VkCommandPool
may be freed from:-
any command that takes the pool as a direct argument
-
vkResetCommandBuffer
whosecommandBuffer
was allocated from thatVkCommandPool
-
-
Allocations scoped to any other object may be freed in that object’s
vkDestroy
* command. -
Any command that allocates host memory may also free host memory of the same scope.