原文链接:https://vulkan.lunarg.com/doc/view/1.2.131.2/windows/chunked_spec/chap6.html#synchronization
更新记录:
2020/4/3:: 6同步与缓存控制,6.1运行和内存依赖 ,6.1.1图片布局转换
2020/4/4::6.1.2管线阶段
2020/4/5::6.1.3访问类型
2020/4/8::修复了表格内数据看不见的情况,增加了6.1.4帧缓存区域依赖
6.同步与缓存控制
同步访问数据是Vulkan应用的基本能力,在主机端(host)调用指令的顺序和那些在设备端(device)调用的指令之间没有隐含的同步保证(假如说host端和device端有一块两方共享的内存,访问该内存时需要同步操作),这需要开发人员显示的做出同步保证。内存缓存和其他优化也需要显示的管理,也就是说应用底层下那些庞大的数据流动完全需要开发人员主动的显示管理。
在Vulkan中有一些隐示同步保证位于Vulkan命令(Commands)之间,在Vulkan中有5种显示同步机制原语:
Fence(栏栅)
用于同步主机端和设备端。当设备端的某些任务完成时,可以通过Fence获得该完成状态。
Semaphores(旗语,信号量)
用于多个设备队列(queue)之间的资源访问同步
Events(事件)
事件提供了一个细粒度的同步,可通过主机端或者Vulkan命令缓存(command buffer)中的命令(command)激活该事件。其可在命令缓存内等待。主机端可以主动获取Events的状态。
Pipeline Barriers(管线障)
管线障同Events一样,同样提供命令缓存(command buffer)内部同步控制,和Events的不同的是Pipeline Barriers只能同步一个特定点,而不能像Events那样既可以被唤醒也可以等待。
Render Passes(渲染通路)
Render Passes对于渲染任务提供了一个非常有用的同步框架。其可以有效地实现某些其他同步原语相同的功能。
6.1运行和内存依赖
在本机,外设甚至是显示器,在他们内部执行一项操作(operation),操作内部都是由很多小指令组成。同步指令通过指定同步范围(synchronization scopes),在两个操作集之间提供了显示的执行依赖(execution dependencies)和内存依赖(memory dependencies)。
当同步范围内执行同步命令时可以保证执行间的依赖。任何其他不在同步范围内的操作不会进行执行之间的依赖。例如对于很多同步指令,同步范围可以精确到特定的渲染管线阶段(pipeline stags),这将会将其他管线阶段排除在同步范围之外。
对于不同的同步指令,同步范围不尽相同。
一个执行依赖,一般是指两个不同的操作集的运行顺序,第一个操作集必须在第二个操作集开始执行前执行结束。
仅仅使用执行依赖不足以保证从一个操作集产生的数据在另一个操作集中有效。
三种额外的操作类型用于控制内存访问。可用性操作(Availability Operations)使得特定内存写入所产生的数据可用,可在将来访问。如果任何已经可用的数据再次发生往该可用数据所在的内存中写入数据时,或者将这一部分清空,都将会导致该内存下的数据不可用。内存域操作(Memory Domain Operation)使得对于源内存域的写入在目标内存域可用,例如本机(host)和设备(device)公享同一块内存,在本机端对其写入,对于设备端是可用的,反之亦然。可见性操作(Visibility Operations)使得对于一块可用内存对于访问操作可见。
一个内存依赖是一个包含可用性操作和可见性操作的执行依赖。运行顺序如下:
- 第一个操作集完成后执行可用性操作。
- 可用性操作完成后执行可见性操作。
- 可见性操作完成后执行第二个操作集。
一旦写完数据对于特定的内存访问类型完成了可见性操作,就可以通过该类型的内存访问类型完成读写。Vulkan中大多数的同步指令都定义了内存依赖。
内存依赖的可用性和可见性是通过定义访问域(access scopes)来设置的。访问域一般是成对出现的。第一个访问域控制内存的可用性,第二个访问域控制内存的可见性。
注意:
执行依赖和内存依赖是用于解决“数据危害”的。即、确保读取和写入的顺序以明确的顺序进行。写后读(write-after-read)可以使用执行依赖解决,但是读后写(read-after-write)和写后写(write-after-write)需要合适的内存依赖在他们之间。如果一个应用不解决“数据危机”的话,对于数据操作的结果是未定义的。读后读(read-after-read)并不修改数据,并不需要同步操作。
写后读(write-after-read):未同步情况下,两个操作集,一个读,一个写,当对一块内存进行写操作时,这时同时进行读操作,读取的结果很大程度上可能是未定义的数据结果。
读后写(read-after-write):未同步情况下,两个操作集,一个读,一个写,当对一块内存进行读操作时,这时同时进行写操作,写入的结果很大程度上可能是未定义的数据结果。
写后写(write-after-write) :未同步情况下,两个操作集,都是写,当对一块内存进行写操作时,这时同时进行写操作,写入的结果很大程度上可能是未定义的数据结果。
6.1.1 图片布局(Image Layout)转换
图片子资源(Image Subresources)可以从一种布局转换成另一种布局作为内存依赖的一部分(例如通过显示的使用图片障(VkImageMemoryBarrier)或者隐示的使用渲染通路(Render Passes))。布局转换是发生在可用性操作之后,可见性操作之前。图片布局转换可能会在整个图片子资源所在的内存上进行读写操作,所以应用必须确保在格式转换发生前所有要写的内存已经可用。可用的内存自动的就对图片转换操作可用,图片转换写入的内存自动可用。
布局转换总是发生在图片的某项子资源上,转换时需要指定旧布局(当前布局)和新布局。如果旧布局不支持新布局,进行转换。旧布局必须匹配当前图片子资源的布局,有一个例外就是:旧布局可以总是置成VK_IMAGE_LAYOUT_UNDEFINED,尽管这样做的话将会使得图片的子资源的内容无效化。
由于图片布局转化可能需要对内存进行读写访问,如果布局转换影响力图片子资源的内存的话,如果当前转换的图片子资源的内存是平等内存(Peer Memory)的话,给该图片子资源分配内存的堆(heap)必须支持VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT和VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT 能力。
注:平等内存该段,本人未曾见过这种情况,不太清楚具体是什么意思,只是翻译出来,待好心人指点。通过查阅Vulkan文档目前所知平等内存好像是在GPU组中使用,假如说一台电脑上插上了三块显卡,通过某种技术可将三块显卡合成一块逻辑显卡,每块显卡上的内存是平等共享的。平等内存可能说的是这个东西。不知是否正确,待好心人指正。
注意:
如果旧布局置成VK_IMAGE_LAYOUT_UNDEFINED,这样做的话将会使得图片的子资源的内容无效化。使用该功能可以避免昂贵的布局转换操作。
应用必须确保所有对于旧布局的图片访问操作完成,之后对于图片布局的转换,需要在所有对新布局操作之前。图片布局转换内部隐示的读写内存,所以不定义内存依赖将会导致数据竞争(data race)。
图片布局转换和内存混叠(memory aliasing)交互进行。
说明:
图片子资源:图片是由一个个颜色块组成,每个颜色块都对应着颜色的数据。实际上该数据可以是任何数。如果记录着颜色,那么图片的其中一项子资源对应着颜色资源,如果记录着深度,那么图片的其中一项子资源对应着深度信息。但是深度信息也可以解释成颜色信息。可以理解成不同的子资源对于图片中所记录的数据解释不同。
内存混叠:根据Vulkan文档描述,内存混叠是指两个资源在底层存储的内存通过对齐存在交集。不知是否正确,待好心人指正。
对齐:请百度内存对齐。该功能正常是由编译器负责,除非开发底层要不然基本没接触过。
6.1.2 管线阶段
调用一个同步指令,其内部由很多小操作组成。这些小操作按照逻辑顺序一步步的执行,这些逻辑顺序Vulkan中称之为管线阶段(Pipeline Stage)。不同的同步指令中同步的管线阶段也不尽相同,并且和当前命令缓存(Command Buffer)中记录的指令(Command)相关。例如绘制指令、分配指令、复制指令、清空指令和同步指令,这些指令中指定的管线阶段都不尽相同。事实上同步指令并不会在指定的管线阶段上执行,但是同步指令会在VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT和VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT这两个阶段执行。
注意:
同步指令并不会在指定的管线阶段上执行,但是其他命令仍可以通过VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT和VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT这两个阶段与其(其他同步指令)同步。
执行通过管线必须遵守隐示顺序,特别是管道阶段的顺序。如果没有顺序保障,执行将是混乱的。
某些同步指令在运行之前需要指定要同步的管线阶段参数,限制该指令的同步范围。这将会带来非常良好的粒度(grained)控制。通过制定管线阶段来避免不必要的停顿和缓存刷新。
管线阶段如下所示:
typedef enum VkPipelineStageFlagBits {
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001,
VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002,
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004,
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008,
VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010,
VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020,
VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100,
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800,
VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000,

本文详细介绍了Vulkan API中的同步与缓存控制机制,包括同步原语、运行和内存依赖、图片布局转换、管线阶段、访问类型及帧缓存区域依赖等内容,帮助开发者深入理解Vulkan的底层细节。
最低0.47元/天 解锁文章
489

被折叠的 条评论
为什么被折叠?



