Answers:
主要的好处是,将CPU任务划分为多个线程将更加容易,而不必解决访问图形API的所有难题。通常,您要么必须使上下文成为当前上下文(这可能会对性能造成不良影响),要么要提供一个队列并在单个线程中调用图形api。我认为这种方式不会获得任何性能,因为GPU确实确实会按顺序处理它们,但这使开发人员的工作容易得多。
直到现在还没有完成的原因可能是因为Directx和Opengl是在真正没有多线程的时代创建的。同样,Khronos委员会在更改API方面非常保守。他们对Vulkan的看法也是,它会与OpenGL并存,因为两者都有不同的用途。直到最近,随着消费者可以使用越来越多的处理器,Paralism才变得如此重要。
编辑:我并不是说在多个CPU中工作不会获得任何性能,将您的调用分成多个线程以更快地创建纹理/着色器没有用。而是由于有更多的处理器忙,并使gpu忙于要执行的事情而获得了性能。
要为GPU设置框架,CPU需要做很多工作,并且其中很大一部分工作在图形驱动程序内部。在DX12 / Vulkan之前,该图形驱动程序的工作实际上是由API的设计强制为单线程的。
希望DX12 / Vulkan解除该限制,从而允许在一个帧内的多个CPU线程上并行执行驱动程序工作。这将使多核CPU的使用效率更高,从而使游戏引擎可以推送更复杂的场景而不会受到CPU的限制。这是希望-在实践中是否会实现,这是我们在接下来的几年中必须拭目以待的事情。
详细说明:游戏引擎渲染器的输出是DX / GL API调用流,它们描述了渲染帧的操作顺序。但是,API调用流与GPU硬件消耗的实际二进制命令缓冲区之间有很大的距离。可以这么说,驱动程序必须将API调用“编译”为GPU的机器语言。这不是一个微不足道的过程,它涉及许多API概念到低级硬件现实的转换,验证以确保GPU永远不会被设置为无效状态,处理内存分配和数据,跟踪状态变化以发出GPU。纠正低级命令,依此类推等等。图形驱动程序负责所有这些工作。
在DX11 / GL4和更早版本的API中,此工作通常由单个驱动程序线程完成。即使您从多个线程调用该API(例如,您可以使用DX11延迟命令列表执行此操作),它也只会将一些工作添加到队列中,以供驱动程序线程稍后查看。原因之一是我之前提到的状态跟踪。许多硬件级别的GPU配置细节都需要了解当前图形流水线状态,因此没有很好的方法将命令列表分解为可以并行处理的块-每个块都必须确切知道应该开始的状态即使尚未处理前一个块。
这是DX12 / Vulkan中发生的重大变化之一。一方面,它们几乎将所有图形管线状态合并到一个对象中;而对于另一个对象(至少在DX12中),当您开始创建命令列表时,必须提供初始管线状态。状态不会从一个命令列表继承到下一个命令列表。原则上,这允许驱动程序在开始编译之前不必了解任何先前的命令列表,进而使应用程序可以将其呈现分解为可并行化的块,从而生成完全编译的命令列表,然后可以将其编译为串联在一起并以最少的麻烦发送到GPU。
当然,新的API中还有许多其他更改,但是就多线程而言,这是最重要的部分。
现代GPU通常只有一个前端部分,可以处理来自CPU的完全线性的命令流。这是自然的硬件设计,还是只是在单个CPU内核为GPU生成命令的时代之外逐渐演变,这是有争议的,但这是目前的现实。因此,如果生成单个线性的有状态命令流,那么在CPU的单个线程上线性生成该流当然是有道理的!对?
好吧,现代GPU通常还具有非常灵活的统一后端,可以同时处理许多不同的事物。一般而言,GPU可在相当精细的粒度下在顶点和像素上运行。GPU在一个绘制中处理1024个顶点与在两个不同绘制中处理512 + 512个顶点之间并没有太大区别。
这暗示了一种减少工作量的自然方法:不要在一次绘制调用中在GPU上抛出大量顶点,而是将模型分成多个部分,对这些部分进行廉价的粗选,如果每个块都通过了,则分别提交每个块。剔除测试。如果您以正确的粒度进行操作,则应该获得不错的加速效果!
不幸的是,在当前的图形API现实中,绘图调用在CPU上非常昂贵。原因的简化说明:GPU上的状态更改可能不直接与图形API调用相对应,因此许多图形API调用只是在驱动程序内部设置了一些状态,而依赖于此新状态的draw调用会遍历所有标记为自上次绘制以来已更改的状态,将其写入GPU的命令流中,然后实际启动绘制。这是为获取GPU前端单元的精简指令流而完成的所有工作。
归结为,您有一个预算用于绘图调用,这完全由驱动程序的开销决定。(我想我听说现在这些天您可以为60 FPS标题获得每帧5,000左右的收益。)您可以通过在并行块中构建此命令流来大幅提高该比率。
也有其他原因(例如,用于改善VR延迟的异步时间扭曲),但这对于图形绑定游戏和其他需要大量调用的软件(例如3D建模程序包)来说是一个很大的原因。