转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=1374

上文介绍了feature level和option features这两个最容易被误解的D3D11特性,本篇主要探讨一下另外两个重要特性,compute shader和multi-threaded。他们同样可以在D3D10级别硬件上使用,但存在很多细节需要注意。

Compute Shader

compute shader(也叫DirectCompute)是D3D11新增的主要功能之一。在D3D11的GPU上,compute shader是完整的5.0版本,而在D3D10.x的GPU上,compute shader有个简化的4.x版。两者的具体差别请见Compute Shaders on Downlevel Hardware

CS 4.x的一个很重要缺点是不支持RWTexture,所以shader无法写入texture,只能写入buffer。(这是NV造成的。AMD的硬件很 早就可以做到写入RWTexture,但因为CS 4.x要求同时兼容NV和AMD,所以就放弃了写入RWTexture的要求。相信用过CUDA 2.0之前的朋友也了解这件事情)结果就是无法把CS 4.x用于post process这样的操作。有个解决方案是compute shader的结果都写到buffer,要让图形流水线用的时候调用一个pixel shader来把buffer转到texture中。CS 4.x可以把texture和buffer作为输入、buffer作为输出,而D3D11下的PS 4.x可以把texture和buffer作为输入、texture作为输出,所以可以把两者结合来实现从texture到texture的流水线。

但是,不管是CS 4.x还是5.0,从CS转回图形流水线的时候,在NV的卡上都会出现一个明显的几毫秒延迟。所以每一帧的切换次数要尽量减少。在AMD卡完全没这个问题。

CS 4.x还有一个重要缺点,每个thread虽然可以读任意地址的shared memory,但只能写入自己对应的shared memory单元,所以没办法用CUDA里面常用的stride布局。(这是AMD的实现限制。还是因为有兼容的要求,所以就算NV能做到也不行了)常见 的做法是,声明一个结构体,里面有自己要用到的所有共享变量。用这样的结构体来声明shared memory,就可以写入多个值。

Multi-threaded

D3D11 加入了显式的multi-threaded支持。它把D3D10的接口分成两部分。负责建立资源的那些函数属于ID3D11Device,剩下的都分到了 ID3D11DeviceContext。ID3D11Device是线程安全的,如果驱动不支持并行建立资源,runtime就会通过加锁来保证线程安 全(除非用D3D11_CREATE_DEVICE_SINGLETHREADED来强制关闭这一点)。ID3D11DeviceContext是没有线 程安全的,得由上层代码自己处理。

D3D11的device context分为Immediate Context和Deferred Context两种。Immediate context的命令都会立刻得到执行,deferred context则是把命令存在列表里,等他被提交的时候才开始执行。因为在调用Draw的时候,CPU需要做一些额外的事情,所以如果Draw call被分散到多个core去执行,是会有性能提升的。

Deferred Context

高性能地使用这 个多线程是需要驱动支持的。现在的GPU一般都有多个command unit,但驱动往往不开放多线程的功能。目前不论是D3D11还是D3D10级别的,都不支持Multi-threaded,所有的deferred context都是软件实现的,所以性能并无提高、反而降低。希望某天驱动能完善到支持它的程度,这才能真正让开发人员用上高效的deferred context。

本篇到此为止,下一篇会讲tessellation和新的纹理压缩格式。