10.2.1. CPU访问设备内存对象
通过vkAllocateMemory
创建的内存对象不能被CPU端直接访问。
创建时带有VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
属性的内存对象被认为是 可映射的 。 内存对象必须是可映射的,才能被映射到CPU端。
可调用如下命令可以获取指向映射到可映射内存对象的指针:
VkResult vkMapMemory(
VkDevice device,
VkDeviceMemory memory,
VkDeviceSize offset,
VkDeviceSize size,
VkMemoryMapFlags flags,
void** ppData);
-
device
是拥有内存对象的逻辑设备。 -
memory
是将要被映射的VkDeviceMemory
对象。 -
offset
是一个从内存对象的起始的 从0开始的字节offset。 -
size
是需要映射的内存区间的大小,或者值为VK_WHOLE_SIZE
时表示 从offset
到分配的内存结尾的全部映射。 -
flags
被保留。 -
ppData
指向一个 被返回的 host-accessible指向 被映射的区间指针。 这个指针 减去offset
必须被按照VkPhysicalDeviceLimits
::minMemoryMapAlignment
对齐。
若调用 vkMapMemory
对已映射过的内存对象再次映射,则是应用程序的错误。
注意
|
vkMapMemory
并不在返回一个host端可访问的指针之前检查设备内存当前是否正在使用中。 应用程序必须保证之前提交的任何命令(会向该区间写入的命令)已经在host端读取或者写入完成,且任何之前被提交的命令(从该区间读取内容的命令) 在host端写入之前已经完成。 (参看 这里 获取更详细的信息). 若分配设备内存时未带有 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
标志位,必须提供这些保证,应用程序必须从 区间的起始到最邻近的多个 VkPhysicalDeviceLimits
::nonCoherentAtomSize
聚集在一起,保证从区间的尾部到最邻近的多个 VkPhysicalDeviceLimits
::nonCoherentAtomSize
聚集在一起。
当一块区域的设备内存被映射到主机端可访问,应用程序需要负责同步该区间的内存访问。
注意 应用程序开发者需要对本章Synchronization and Cache Control 描述的机制相当熟悉, 这对于维持按序访问内存来说极其重要。 |
正确使用
-
memory
必须未被映射过 -
offset
必须要被memory
的大小要小 -
若
size
不等于VK_WHOLE_SIZE
,size
必须大于0
-
若
size
不等于VK_WHOLE_SIZE
,size
必须小于等于memory
减去offset
的结果的大小 -
memory
必须在创建时带有 内存类型VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
Valid Usage (Implicit)
-
device
must be a validVkDevice
handle -
memory
must be a validVkDeviceMemory
handle -
flags
must be0
-
ppData
must be a pointer to a pointer -
memory
must have been created, allocated, or retrieved fromdevice
Host Synchronization
-
Host access to
memory
must be externally synchronized
Return Codes
-
VK_SUCCESS
-
VK_ERROR_OUT_OF_HOST_MEMORY
-
VK_ERROR_OUT_OF_DEVICE_MEMORY
-
VK_ERROR_MEMORY_MAP_FAILED
为了应用程序可以与非一致性内存分配一起工作,有两个命令可以使用: vkFlushMappedMemoryRanges
和 vkInvalidateMappedMemoryRanges
。
注意 若创建内存对象时带有 |
可调用如下命令来把CPU缓存刷新到非一致的内存区域中:
VkResult vkFlushMappedMemoryRanges(
VkDevice device,
uint32_t memoryRangeCount,
const VkMappedMemoryRange* pMemoryRanges);
-
device
是拥有内存对象的逻辑设备。 -
memoryRangeCount
是pMemoryRanges
数组的长度。 -
pMemoryRanges
是一个指向VkMappedMemoryRange
类型数组的指针,描述了需要刷新的内存区域。
vkFlushMappedMemoryRanges
必须用于保证host端写入到非一致性内存的内容对于 device可见。 只能在host端写入到非一致性内存之后,且在会读取或写入这些内存的命令缓冲区被提交到队列之前,才可以被调用。
注意 取消非一致性内存映射并不意味着刷新被映射的内存, 没有被刷新的 host端写入的内容 可能对device不可见。 |
Valid Usage (Implicit)
-
device
must be a validVkDevice
handle -
pMemoryRanges
must be a pointer to an array ofmemoryRangeCount
validVkMappedMemoryRange
structures -
memoryRangeCount
must be greater than0
Return Codes
-
VK_SUCCESS
-
VK_ERROR_OUT_OF_HOST_MEMORY
-
VK_ERROR_OUT_OF_DEVICE_MEMORY
可调用如下函数从host端cache使 非一致性内存无效:
VkResult vkInvalidateMappedMemoryRanges(
VkDevice device,
uint32_t memoryRangeCount,
const VkMappedMemoryRange* pMemoryRanges);
-
device
是拥有内存范围的逻辑设备。 -
memoryRangeCount
是pMemoryRanges
数组的长度。 -
pMemoryRanges
是一个指针,指向一个VkMappedMemoryRange
数据结构的数组,描述了需要无效化的内存区间。
vkInvalidateMappedMemoryRanges
must be used to guarantee that device writes to non-coherent memory are visible to the host. It must be called after command buffers that execute and flush (via memory barriers) the device writes have completed, and before the host will read or write any of those locations. If a range of non-coherent memory is written by the host and then invalidated without first being flushed, its contents are undefined.
注意 Mapping non-coherent memory does not implicitly invalidate the mapped memory, and device writes that have not been invalidated must be made visible before the host reads or overwrites them. |
Valid Usage (Implicit)
-
device
must be a validVkDevice
handle -
pMemoryRanges
must be a pointer to an array ofmemoryRangeCount
validVkMappedMemoryRange
structures -
memoryRangeCount
must be greater than0
Return Codes
-
VK_SUCCESS
-
VK_ERROR_OUT_OF_HOST_MEMORY
-
VK_ERROR_OUT_OF_DEVICE_MEMORY
VkMappedMemoryRange
类型数据结构定义如下:
typedef struct VkMappedMemoryRange {
VkStructureType sType;
const void* pNext;
VkDeviceMemory memory;
VkDeviceSize offset;
VkDeviceSize size;
} VkMappedMemoryRange;
-
sType
是数据结构的类型。 -
pNext
是NULL
或者一个指向拓展特定的数据结构的指针。 -
memory
是这个区间所在的内存对象。 -
offset
是一个从内存对象的起始位置开始的,从0开始的偏移量。 -
size
要么是区间的大小,要么是 影响到 从offset
到当前映射到分配的内存结束的区间的VK_WHOLE_SIZE
。
正确使用
-
memory
当前必须已经被映射了 -
若
size
和VK_WHOLE_SIZE
不相等,offset
和size
必须: 指定已经映射的内存memory
之中的一段区间。 -
若
size
和VK_WHOLE_SIZE
相等,offset
必须: 和已经映射的内存的区间完全相同。 -
offset
必须: 是VkPhysicalDeviceLimits
::nonCoherentAtomSize
的倍数 -
若
size
和VK_WHOLE_SIZE
不相等,size
必须: 是VkPhysicalDeviceLimits
::nonCoherentAtomSize
的倍数。
Valid Usage (Implicit)
-
sType
must beVK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE
-
pNext
must beNULL
-
memory
must be a validVkDeviceMemory
handle
editing-note TODO (Tobias) - There’s a circular section reference between this next section and the synchronization section. The information is all covered by both places, but it seems a bit weird to have them reference each other. Not sure how to resolve it. |
Host-visible memory types that advertise the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
property still require memory barriers between host and device in order to be coherent, but do not require additional cache management operations to achieve coherency. For host writes to be seen by subsequent command buffer operations, a pipeline barrier from a source of VK_ACCESS_HOST_WRITE_BIT
and VK_PIPELINE_STAGE_HOST_BIT
to a destination of the relevant device pipeline stages and access types must be performed. Note that such a barrier is performed implicitly upon each command buffer submission, so an explicit barrier is only rarely needed (e.g. if a command buffer waits upon an event signaled by the host, where the host wrote some data after submission). A pipeline barrier is required to make writes visible to subsequent reads on the host.
应用程序若不在需要从host端访问内存对象,可调用如下命令来取消映射:
void vkUnmapMemory(
VkDevice device,
VkDeviceMemory memory);
-
device
是拥有内存的逻辑设备。 -
memory
是将要被取消映射的内存对象。
正确使用
-
memory
在当前必须被映射过
Valid Usage (Implicit)
-
device
must be a validVkDevice
handle -
memory
must be a validVkDeviceMemory
handle -
memory
must have been created, allocated, or retrieved fromdevice
Host Synchronization
-
Host access to
memory
must be externally synchronized