译者:林公子
出处:木木的二进制人生
转载请注明作者和出处,谢谢!
这是微软公布的Direct3D 12文档的其中一篇,此翻译留作学习记录备忘,水平有限,错漏难免,还望海涵。
原文链接是https://msdn.microsoft.com/en-us/library/windows/desktop/dn899194(v=vs.85).aspx
Direct3D 12到Direct3D 11的重大改变
Direct3D 12是对Direct3D 11编程模型的一个巨大颠覆。Direct3D 12让应用程序比以往更接近硬件底层。通过接近底层,Direct3D 12更加的快速和高效。不过你的应用程序使用Direct3D 12带来的速度和效率提升相应的代价是你要比使用Direct3D 11负责更多的工作。
Direct3D 12是底层编程的回归。通过引入这些新特性,它给予你对游戏和应用程序中图形元素的更多的控制: 用于代表管线整体状态的的对象,用于任务提交的command list和bundles,用于资源访问的descriptor heap/table。
Direct3D 12 vs Direct3D 11的取舍
使用Direct3D 12你的应用程序的速度和效率得到了提升,不过你要负责相对与Direct3D 11更多的工作。
l 在Direct3D 12中,CPU-GPU的同步明确由应用程序负责,不再像Direct3D 11中由运行时隐式执行。这也意味着Direct3D 12没有对pipeline hazard的自动检查,因此这也成为了应用程序的职责。
l 在Direct3D 12中,应用程序负责管线数据更新。也就是Direct3D 11中的"Map/Lock-Discard"模式在Direct3D 12中必须手动进行。在Direct3D 11中,当你使用D3D11_MAP_WRITE_DISCARD标识调用ID3D11DeviceContext::Map时,运行时返回一个新内存区块的指针代替旧的缓冲数据。这让GPU能够在应用程序往新缓冲填充数据的同时使用旧的数据。应用程序不需要额外的内存管理。旧的缓冲在GPU使用完后会自动销毁或重用。
l 在Direct3D 12中,所有的动态更新(包括constant buffer,dynamic vertex buffer,dynamic textures等等)明确由应用程序来控制。这些动态更新包括任何请求的GPU fence和buffer。应用程序有责任保证内存在使用完之前是有效的。
l Direct3D 12只将COM风格的引用计数用于interface的生命周期(通过Direct3D的弱引用模型关联到device的生命周期)。所有的resource和description内存生命周期由应用程序来负责保证适当的存活时间,而且它们都没有使用引用计数。Direct3D 11也使用引用计数来管理interface相关的对象。
管线状态对象
Direct3D11允许使用大量独立的对象的集合来操纵管线状态。例如,input assembler state,pixel shader state,rasterizer state和output merge state都能够独立的进行修改。这种设计提供了便利性和相对高层的图形管线表示。但是没有利用现代硬件的能力。主要是各种各样的state通常是互相关联的。例如,很多GPU将pixel shader和output merger state合并为一个硬件表示。然而因为Direct3D 11 API这些管线阶段状态被分别设置,显示驱动不能决议管线状态直到状态最后确定下来,而这要等到绘制的时候。这种规划延迟了硬件的状态设置,也就意味着额外的开销和更少的每帧DP。
Direct3D 12通过将大部分管线状态统一到不可变的管线状态对象(PSOs)来解决这个问题,PSOs在创建的时间就决定了。硬件和驱动能够立即将PSO转换为用来驱动GPU工作的硬件本地指令和状态。你仍然可以动态切换使用的PSO。这么做硬件只需要直接拷贝最少的预计算状态到硬件寄存器,而不是实时计算硬件状态。通过使用PSOs,DP的开销显著的减少,然后每帧可以有更多的DP。更多关于PSOs的信息见Managing graphics pipeline state in Direct3D 12. 。
命令列表和集合(bundle)
在Direct3D 11中,所有的任务提交都通过immediate context完成,immediate context代表了一条送往GPU的指令流。要实现多线程,游戏还有deferred context可以使用。Direct3D 11中的Deferred Context不能完美的映射到硬件,所以它们能做的事情有限。
Direct3D 12引入了给予命令列表的任务提交模型。命令列表包含了在GPU上执行一个具体工作所需的所有信息。每个命令列表包含的信息有使用哪个PSO,需要什么纹理和缓冲资源和所有DP的参数。因为每个命令列表是自包含的并且没有状态继承。驱动能够预先计算所有需要的GPU命令,并且是以一种自由线程化(free-threaded)的方式。接下来唯一需要进行的处理是通过命令队列将命令列表最终提交到GPU。
除了命令列表,Direct3D 12还引入了一个二级的任务预计算方式:bundle。不像命令列表,完全的自包含,一般性的被构造,提交一次然后丢弃,bundle提供了某种形式的状态继承来允许复用。例如,如果游戏想要用不同的纹理绘制两个角色模型。一种方法是用一个命令列表记录两组完整一样的DP。另一种方法是记录一个绘制单一角色模型的bundle,然后用不同的资源在命令列表上“回放”bundle两次。在后一种情况下,显示驱动只需要计算相应的指令一次,而创建命令列表本质上相当于两个低开销的函数调用。
更多关于命令列表和bundle的信息,见Work Submission in Direct3D 12。
描述符堆和表(Descriptor Heap and Table)
Direct3D 11中的资源绑定高度抽象和便利,却留下很多现代硬件能力没有被利用到。在Direct3D 11中,游戏创建资源的视图对象,然后绑定这些视图到管线中不同shader阶段的slot中。然后Shader从显式绑定的 slot读取数据,这些绑定slot在绘制时是固定的。这个模型意味着每当游戏使用不同的资源绘制,它必须重新绑定不同的视图到不同的slot,然后再次调用绘制函数。这种情况也表现出额外的开销能够通过完全利用硬件能力来消除。
Direct3D 12改变了绑定模型来匹配现代硬件并显著的提升了性能。和需要独立的资源视图和显式的绑定到slot相反,Direct3D 12提供了一个descriptor heap用来创建游戏中不同的资源。这个方案提供了一种机制让GPU预先直接写入硬件本地资源描述(descriptor)到内存。因为descriptor heap已经被恰当的特定于硬件的descriptor数据填充,改变descriptor table是消耗相当低的操作
除了由descriptor heap和table带来的性能提升。Direct3D 12还允许资源在shader里被动态的索引,这提供了空前的灵活性并开启了新渲染技术的大门。举例来说,现代延迟渲染引擎一般将一个某种形式的材质或物体标识符编码到中间的G-Buffer。在Direct3D 11中,这些引擎必须小心的避免使用太多的材质,因为在一个G-Buffer中包含太多会极大的影响最终渲染pass的速度。有了能动态索引的资源,一个有上千材质的场景能够最终和只有十个材质的场景一样快。
更多关于descriptor head和table的信息,见Resource Binding。