|
返回上级目录
第7章 同步与缓存控制(Synchronization and Cache Control)
<hr/>内存屏障被用于显式控制对buffer和image subresource ranges(图像子资源范围)的访问。内存屏障被用于在队列族之间转移所有权,改变图像布局,与定义availability和visibility操作。它们会显式定义访问类型,以及由包含它们的同步命令创建的内存依赖的访问范围中包含的buffer和 图像子资源范围。
<hr/>7.7.1. 全局内存屏障(Global Memory Barriers)
全局内存屏障会应用于其执行时的所有涉及到的内存对象的内存访问。
typedef struct VkMemoryBarrier2 {
VkStructureType sType;
const void* pNext;
VkPipelineStageFlags2 srcStageMask;
VkAccessFlags2 srcAccessMask;
VkPipelineStageFlags2 dstStageMask;
VkAccessFlags2 dstAccessMask;
} VkMemoryBarrier2;第一个同步和访问作用域,只包含由srcStageMask和srcAccessMask指定的操作与内存访问,第二个同步和访问作用域同理,由dstStageMask和dstAccessMask指定。
typedef struct VkMemoryBarrier {
VkStructureType sType;
const void* pNext;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
} VkMemoryBarrier;第一个和第二个访问作用域由srcAccessMask和dstAccessMask指定。
<hr/>7.7.2. 缓冲内存屏障(Buffer Memory Barriers)
buffer内存屏障只适用于所涉及的特定buffer范围的内存访问。buffer内存屏障同样可以用于为指定的buffer范围,定义一个队列族所有权转移。
typedef struct VkBufferMemoryBarrier2 {
VkStructureType sType;
const void* pNext;
VkPipelineStageFlags2 srcStageMask;
VkAccessFlags2 srcAccessMask;
VkPipelineStageFlags2 dstStageMask;
VkAccessFlags2 dstAccessMask;
uint32_t srcQueueFamilyIndex;
uint32_t dstQueueFamilyIndex;
VkBuffer buffer;
VkDeviceSize offset;
VkDeviceSize size;
} VkBufferMemoryBarrier2;• offset是buffer的后备内存中的一个字节偏移量,这与绑定到buffer的基础偏移量有关。
• size是buffer的后备内存受影响区域的大小(字节),或者在从offset到buffer末尾都受到影响时,使用VK_WHOLE_SIZE。
该结构体定义了一个被限制到了buffer中的一个范围内的内存依赖,并且可以为该范围定义一个队列族转移操作。
两个访问作用域都被限制到了由offset和size定义的buffer的范围内的内存访问。
如果创建buffer时,使用了VK_SHARING_MODE_EXCLUSIVE,而且srcQueueFamilyIndex ≠ dstQueueFamilyIndex,那么,该内存屏障会定义一个队列族转移操作。
当在一个family为srcQueueFamilyIndex的队列上执行时,该屏障会为指定的buffer范围定义一个family release操作,且第二个同步和访问作用域不会在该队列上执行同步操作(因为屏障后面的操作在另一个队列中)。
当在一个family为dstQueueFamilyIndex的队列上执行时,此屏障会为指定的buffer范围定义一个family acquire操作,并且第一个同步和访问作用域不会在该队列上执行同步操作(因为屏障前面的操作在另一个队列中)。
当srcQueueFamilyIndex ≠ dstQueueFamilyIndex时,也会定义一个队列族转移操作,并且两个queue family index都是为外部内存所有权转移保留的special queue family values(一些特殊队列族值),如同Queue Family Ownership Transfer中描述的一样。
当srcQueueFamilyIndex是其中一个值时,将定义一个queue family acquire操作。
当dstQueueFamilyIndex是其中一个值时,将定义一个queue family release操作。
VkBufferMemoryBarrier:
typedef struct VkBufferMemoryBarrier {
VkStructureType sType;
const void* pNext;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
uint32_t srcQueueFamilyIndex;
uint32_t dstQueueFamilyIndex;
VkBuffer buffer;
VkDeviceSize offset;
VkDeviceSize size;
} VkBufferMemoryBarrier;如果srcAccessMask包含VK_ACCESS_HOST_WRITE_BIT,会执行一个内存域操作,会使得在主机域中available(可用)的内存同时也对设备域可用。
如果dstAccessMask包含VK_ACCESS_HOST_WRITE_BIT或VK_ACCESS_HOST_READ_BIT,会执行一个内存域操作,使设备域可用的内存同时也对主机域可用。
<hr/>注意:
当使用了VK_MEMORY_PROPERTY_HOST_COHERENT_BIT时,主机域中的可用内存会自动对主机域可见,任何主机写会自动对主机域可见。
如果srcQueueFamilyIndex ≠ dstQueueFamilyIndex,并且srcQueueFamilyIndex等于当前的queue family,那么该内存屏障将为指定的buffer范围,定义一个queue family release操作,并且第二个访问作用域将不会包含任何访问(因为发生在另一个队列中),如同dstAccessMask是0一样。
如果dstQueueFamilyIndex ≠ srcQueueFamilyIndex,并且dstQueueFamilyIndex等于当前的queue family,那么该内存屏障会为指定的buffer范围,定义一queue family acquire操作,并且第一个访问作用域将不会包含任何访问(因为发生在另一个队列中),如同srcAccessMask为0一样。
<hr/>

<hr/>7.7.3. 图像内存屏障(Image Memory Barriers)
图像内存屏障只适用于涉及特定内存子资源范围的内存访问。即,一个由图像内存屏障形成的内存依赖,作用域被限制为对特定图像子资源范围的访问。图像内存屏障也可以被用来为一个指定的图像子资源范围定义图像布局转换,或队列族所有权转移。
VkImageMemoryBarrier2:
typedef struct VkImageMemoryBarrier2 {
VkStructureType sType;
const void* pNext;
VkPipelineStageFlags2 srcStageMask;
VkAccessFlags2 srcAccessMask;
VkPipelineStageFlags2 dstStageMask;
VkAccessFlags2 dstAccessMask;
VkImageLayout oldLayout;
VkImageLayout newLayout;
uint32_t srcQueueFamilyIndex;
uint32_t dstQueueFamilyIndex;
VkImage image;
VkImageSubresourceRange subresourceRange;
} VkImageMemoryBarrier2;其余成员的作用与buffer内存屏障大体相同。
如果oldLayout不等于newLayout,那么内存屏障将为指定的图像子资源范围定义一个图像布局转换。如果该内存屏障调用了一个队列族转移操作,则该布局转换将至在队列间被执行一次。
<hr/>注意:
当旧的和新的layout相等时,layout值会被忽略 ------ 数据都会被保存,无论指定了什么值,或者该图像当前在什么布局内。
<hr/>如果图像有一个多平面格式,并且图像是disjoint的(即,每个平面都单独绑定到各自的内存,而不是把图像整体绑定到同一个单独的内存),那么在subresourceRange 的成员aspectMask中包含VK_IMAGE_ASPECT_COLOR_BIT,等价于包含VK_IMAGE_ASPECT_PLANE_0_BIT,VK_IMAGE_ASPECT_PLANE_1_BIT,和VK_IMAGE_ASPECT_PLANE_2_BIT(只对3-平面格式)。
VkImageMemoryBarrier:
typedef struct VkImageMemoryBarrier {
VkStructureType sType;
const void* pNext;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
VkImageLayout oldLayout;
VkImageLayout newLayout;
uint32_t srcQueueFamilyIndex;
uint32_t dstQueueFamilyIndex;
VkImage image;
VkImageSubresourceRange subresourceRange;
} VkImageMemoryBarrier;如果没有启用synchronization2特性,或者oldLayout不等于newLayout,那么oldLayout和newLayout会为指定的图像子资源范围定义一个图像布局转换。
其余成员的作用和VkImageMemoryBarrier2大体类似。
<hr/>注意:
当old和new的layout相等时,如果启用了synchronization2特性,layout值将被忽略 – 无论指定何值,或该图像当前的layout是什么,数据都将被保留。
<hr/>7.7.4. (队列族所有权转移)Queue Family Ownership Transfer
为了在一个不同的队列族的队列上,以一种良好定义的方式访问使用VK_SHARING_MODE_EXCLUSIVE创建的资源,必须显式地把它们的所有权从一个队列族转移到另一个。
特殊的队列族索引VK_QUEUE_FAMILY_IGNORED用以指出一个队列族参数或成员被忽略。
使用外部内存,由外部API或实例共享的资源,必须同样在局部和外部队列(或等价的外部API)之间,显式地管理所有权转移,无论这些资源创建时使用了何种VkSharingMode。
特殊的队列族索引VK_QUEUE_FAMILY_EXTERNAL表示任意对于资源当前的Vulkan实例的外部的队列,只要该队列使用相同的device group或physical device,且资源的VkDevice的驱动版本相同,由VkPhysicalDeviceIDProperties::deviceUUID和VkPhysicalDeviceIDProperties::driverUUID指定。
如果内存依赖在不同族的两个队列间被使用的这样的资源之间被正确表达,但没有定义所有权转移,那么该资源的内容对第二个队列族的任意读访问,是未定义的。
<hr/>注意:
如果一个应用不需要一个资源的内容在转移到另一个队列族时保持有效,则应跳过所有权转移。
<hr/>一个队列族所有权转移有两部分组成:
1. 从源队列族释放独占所有权
2. 为目标队列族获取独占所有权
应用程序必须确保执行操作以正确的顺序发生,这通过在它们之间定义执行依赖,例如semaphore,来实现。一个释放操作被用来释放一个buffer的范围或一个图像子资源范围的独占所有权。释放操作由在一个从源队列族而来的队列上使用一个管线屏障命令,来执行一个buffer内存屏障(对buffer范围)或image内存屏障(对一个图像子资源范围)来定义。屏障的srcQueueFamiliyIndex参数必须被设置为源队列族索引,dstQueueFamilyIndex参数必须被设置为目标队列族索引。
dstAccessMask会被此种屏障忽略,因此没有visibility操作被执行 – mask的这个值不影响该屏障的有效性。释放操作发生在availability操作之后,在调用指令的第二个同步作用域内被指定的操作之前。
获取操作被用来获取一个buffer范围或图像子资源范围的独占所有权。一个获取操作中指定的buffer范围或图像子资源范围必须域之前的释放操作中的完全匹配。srcAccessMask会被此类屏障忽略,因此没有availability操作被执行。获取操作发生在调用命令的第一个同步作用域中的操作之后,与visibility操作之前。
<hr/>注意:
虽然为用来释放或者获取操作提供的内存屏障提供目标或源的access mask并非无效,但它们没有实际效果。
在一个释放操作后的访问,会得到未定义的结果,并且这些访问的visibility没有实际效果。
类似地,在获取操作之前的写访问,会为将来的访问产生未定义的结果,因此这些写的availability没有实际用处。
在一个更早的规格版本中,需要在两侧都匹配 – 但后续被放宽了。这些掩码硬背设为0。
<hr/>如果转移是通过一个图像内存屏障,并且期待一个图像布局转换,那么在释放操作的内存屏障中的oldLayout和newLayout的值,必须等于在获取操作的内存屏障中的oldLayout和newLayout的值。尽管图像布局转换被提交了两次,但它只会执行一次。一个以此方式指定的布局转换,会发生在释放操作之后,与获取操作之前。
如果srcQueueFamilyIndex和dstQueueFamilyIndex相等,则没有所有权转移,并且屏障操作如同两个index都被设置为了VK_QUEUE_FAMILY_IGNORED。
队列族所有权转移可能会在buffer范围或图像子资源范围绑定的所有内存上执行读和写,因此应用要保证所有的内存写,必须在执行一个队列族所有权转移之前可用。可用内存会自动对队列族释放和获取操作可见,并且这些操作执行的写也会自动可用。
一旦一个队列族获取了VK_SHARING_MODE_EXCLUSIVE的一个buffer范围或图像子资源范围的所有权,它的内容对于其它队列族将是未定义的,直到所有权被转移。绑定到被转移的buffer或内存子资源范围的混叠内存的其他资源的任意部分的内容,会在释放或获取操作后变为未定义。
<hr/>注意:
由于event不能被直接用于队列间同步,并且vkCmdSetEvent()不包含一个释放操作所需的队列族索引或内存屏障参数,因此一个队列族的所有权释放与获取操作只能使用vkCmdPipelineBarrier()来执行。
<hr/> |
|