设备内存是设备可见的内存,例如可以被设备原生访问的不透明图片的内容,或者常驻设备内存的统一缓冲区对象。
物理设备的内存属性描述了内存堆和可用的内存类型。
可调用如下命令来查询内存属性:
void vkGetPhysicalDeviceMemoryProperties(
VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMemoryProperties* pMemoryProperties);
-
physicalDevice
是被查询内存所在的设备的handle。 -
pMemoryProperties
指向VkPhysicalDeviceMemoryProperties
类型的数据结构,属性信息由此返回。
Valid Usage (Implicit)
-
physicalDevice
must be a validVkPhysicalDevice
handle -
pMemoryProperties
must be a pointer to aVkPhysicalDeviceMemoryProperties
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 thanmemoryHeapCount
from theVkPhysicalDeviceMemoryProperties
structure. -
propertyFlags
is a bitmask of properties for this memory type. The bits specified inpropertyFlags
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 theVK_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 theVK_MEMORY_HEAP_DEVICE_LOCAL_BIT
set. -
if
propertyFlags
has theVK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
bit set, memory allocated with this type can be mapped for host access usingvkMapMemory
. -
if
propertyFlags
has theVK_MEMORY_PROPERTY_HOST_COHERENT_BIT
bit set, host cache management commandsvkFlushMappedMemoryRanges
andvkInvalidateMappedMemoryRanges
are not needed to make host writes visible to the device or device writes visible to the host, respectively. -
if
propertyFlags
has theVK_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 theVK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
bit set, the memory type only allows device access to the memory. Memory types must not have bothVK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT
andVK_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实现,如果出现了此类错误,应该返回 |
正确使用
-
The number of currently valid memory objects, allocated from
device
, must be less thanVkPhysicalDeviceLimits
::maxMemoryAllocationCount
Valid Usage (Implicit)
-
device
must be a validVkDevice
handle -
pAllocateInfo
must be a pointer to a validVkMemoryAllocateInfo
structure -
If
pAllocator
is notNULL
,pAllocator
must be a pointer to a validVkAllocationCallbacks
structure -
pMemory
must be a pointer to aVkDeviceMemory
handle
Return Codes
-
VK_SUCCESS
-
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 beVK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
-
pNext
must beNULL
可调用如下命令来释放内存对象:
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 validVkDevice
handle -
If
memory
is notVK_NULL_HANDLE
,memory
must be a validVkDeviceMemory
handle -
If
pAllocator
is notNULL
,pAllocator
must be a pointer to a validVkAllocationCallbacks
structure -
If
memory
is a valid handle, it must have been created, allocated, or retrieved fromdevice
Host Synchronization
-
Host access to
memory
must be externally synchronized