多线程渲染已经听了很久了,然而ES时代,这个事情完全不知从何下手。然而Vulkan引入了queue、command buffer等概念,终于开启了多线程渲染的篇章。然而多线程渲染更重要的是同步问题。本节主要就是说同步问题

https://blog.csdn.net/u010281924/article/details/105379542/、https://zhuanlan.zhihu.com/p/80692115


在Vulkan中,对资源访问的同步工作主要由应用程序负责。命令的执行顺序也几乎没有隐式保证,都是需要显式指定。memory caches和其它优化也是显式管理的,由应用程序控制数据流。

虽然command之间存在一些隐式保证,但Vulkan供暧了五种显式同步机制:

  • Fences:fence可用于与主机通信,表明设备上某些任务的执行已完成。fence是同步gpu执行队列和渲染线程
  • Semaphores:Semaphores可用于控制跨多个queue的资源访问。sephmore可以同步队列。
  • Events:Event提供一个细粒度的synchronization primitive,可以被command buffer或者host signaled,也可以在command buffer上等待、或者host 查询。event是cmd-barrier的升级版,barrier的wait和signal在同一个地方,event的wait和signal可以在两个地方。
  • Pipeline Barriers:Pipeline Barriers提供一个command buffer内部的synchronization control,但只在一个点上,然而拆分成signal和wait操作。barrier是防止指令乱序(资源读写乱序)
  • Render Passes:Render passes基于本章的内容,为大多数渲染任务,提供一个有效的synchronization framework。许多需要应用程序使用其它同步方案的情况,都可以更加有效作为render pass的一部分。

an operation 是指要在host、device或者外部实体(比如表现引擎)上执行的一些工作。Synchronization command 引入显式的执行依赖关系,以及由命令的两个同步作用域定义的两组操作之间的内存依赖关系。

同步作用域定义了同步命令能够创建执行依赖项的其它操作。任何不在同步命令的同步作用域中的操作类型都不会包含在生成的依赖项中。例如,对于很多同步命令,同步作用域可以仅限于在特性管道阶段中执行的操作,这允许从依赖项中排除其它管道阶段。根据特定命令,还可以使用其它范围选项。

执行依赖关系,是一种保证,对于两组操作,第一组操作必须发生在第二组操作之前。如果一个操作发生在另一个操作之前,那么第一个操作必须在第二个操作启动之前完成。更准确的说:

  • A和B是两个独立的操作
  • S是一个同步命令
  • As和Bs是S的同步作用域
  • A'是A和As的交集
  • B'是B和Bs的交际
  • 将A、S、B submit for execution。按照上面的顺序,会在A'和B'之间,产生一个execution dependency E
  • Ececution dependency E使得A'在B'之前发生。

execution dependency chain是一个,在第一个依赖项A'和最后一个依赖项B'形成happens-before关系的一系列execution dependencies。对于每一个连续的execution dependencies,如果第一个依赖项中的Bs和第二个依赖项中的As的交际不是空集,则存在一个chain。一个execution dependency chain中的一个单独的extension denpendency可以通过在execution depenencies的描述中替换以下内容来描述:

  • S是一系列synchronization commands来生成一个execution dependency chain
  • As是S中第一个command 的第一个同步作用域
  • Bs是S中最后一个command 的第二个同步作用域

execution dependencies不足以保证第一组操作中写入的值可以在另一组操作中读取。

其它还有三种操作用于控制内存访问。

  • Availability操作使得由指定内存写访问生成的值,可供memory domain用于将来的访问。在对同一内存位置进行后续写入(weather it is made available or not)或者释放内存之前,任何可用值都保持可用。
  • Memory domain 操作导致源 memory domain 可写 avaliable 的,变得对目标memory domain avaiable(例如:host domain availiable的变得,对device domain也 available)。
  • Visibility操作使得memory domain avaiable的值,对特定内存访问可见。

Availiability、visibility、memory domain以及memory domain操作在之后的章节会详细说明。Availability、Visibility、Domain操作定义了执行这些操作的API。

内存依赖是一个execution dependency,包含了availability和visibiliy操作:1.第一个系列的操作 happens-before availability操作,2.availability操作 happens-before visibility操作,3.visibility操作 happens-before 第二个系列的操作。

一旦写入之对特定类型的内存访问可见,就可以通过该类型的内存访问来读取和写入它们。Vulkan中的大部分同步命令都定义了内存依赖关系。

Available和visible的特定内存访问由内存依赖项的访问范围定义。在内存依赖项的第一个访问作用域中,且发生在A'中的任何类型的访问都是Available的。在内存依赖项的第二个访问作用域中,且发生在B'中的任何类型的访问都是Visble的。任何不在同步命令访问范围内的操作类型都不会包含在结果依赖项中。

memory dependency强制两组操作的 availability和visibility的内存访问和执行顺序。添加到execution dependency chains:

  • a为A'的memory accesses
  • b为B'的memory accesses
  • as为S中第一个命令的第一个访问范围
  • bs为S中最后一个命令的第二个访问范围
  • a'为a和as的交集
  • b'为b和bs的交集
  • 将A、S、B submit for execution。按照上面的顺序,会在A'和B'之间,产生一个memory dependency m
  • Memory dependency m带来:在a'中写的内存变成available,可写的available内存,包括a'中的,会对b' visible

Execution和memory dependencies被用于解决数据危害,即确保读写操作以明确定义的顺序进行。write-after-read的危害可以通过execution dependency解决,但是read-after-write和write-after-write危害需要在它们之间包含适当的memory dependencies。如果应用程序不包含解决这些危险的依赖项,那么内存访问的结果和执行顺序是未定义的。

本节教程就到此结束,希望大家继续阅读我之后的教程。

谢谢大家,再见!


原创技术文章,撰写不易,转载请注明出处:电子设备中的画家|王烁 于 2021 年 5 月 10 日发表,原文链接(http://geekfaner.com/shineengine/blog17_Vulkanv1.2_3.html)