总之,Shader时代的GPU流水线已经越来越没有固定的渲染流程。而且Shader本身也开始变得不确定性。在ShaderModel 1.0时代,因为硬件和API的限制,开发人员只能写很“笨”的Shader。但到了ShaderModel 2.0,3.0以及最近DX10引入的4.0版本,Shader最大指令数的提高,临时暂存器数量的增加,纹理格式的丰富,以及一系列的流控制指令[像是循环(loops)、分支(branching)、呼叫(call)和返回(return)及子程序(subroutine)等]的引入,赋予了Shader真正的编程能力,Shader的结构已经变得越来越非线性。传统意义上的流水线在这种复杂shader下效率是很低的,必须引入Multi-Threading对这些复杂的渲染过程进行控制和管理,才能在流水线陷入存储器读取的时候切换线程,进行其他操作,掩盖内存延迟。合理利用到流水线的资源,单纯的增加GPU的平行管线数量已经难以获得有效的性能提升。小熊在线www.beareyes.com.cn 下图所示的是一个Multi-Threading体系对分支性能帮助的实例,这个分支进行的是对阴影边缘进行过滤的软阴影操作。小熊在线www.beareyes.com.cn 图中绿色区域代表指令前半段操作,也就是开头的「if」指令,而蓝色区域表示后半段的「else」指令,if不需要阴影处理,那所用的textures则保持不变;if指令遇到需要阴影处理,else指令就会指示shader开始阴影操作,红色的4×4像素区域就表示进行分支抉择的部位。如果Multi-Threading越强劲,那么能同时维持的Thread就更多,图中的块也可以做到更小。这种抉择体系的应用,无形中为Shader节约了大量的资源。 R6XX所有的执行单元被分成若干个SIMD单元矩阵,每个SIMD矩阵都有两个仲裁器,根据若干条件交错地给SIMD矩阵递交下一次执行的线程。小熊在线www.beareyes.com.cn R6XX的线程仲裁管理机构一共能为每个SIMD阵列维持总共256个或者更多平行的Thread(线程)。线程调度处理器会动态监测整个Unified Shader流水线的工作状况。一旦它发现其下某个阵列由于等待数据或工作完成而处于闲置状态,就会马上递交一个新的线程供其执行。线程在这里可以被看做指令的容器,线程里面指令的性质不同,只要做好响应的状态保存读取就完全没有差别。每个线程的作用范围为一个Batch(也就是一个像素块,R6XX的Batch是8x8大小的64个相邻pixel小块)。小熊在线www.beareyes.com.cn R6XX的线程分配管理器工作流程如下: G8X的Multi-Threading体系也与之类似。不过不同的是,G8X的管理架构更庞大,更复杂。G8X的线程管理调度器的基本单位是multiprocessor。G8X的每个TPC有两个multiprocessor,每个multiprocessor具备24个warp,每个warp拥有32个独立的thread,而G84一共有2个TCP,所以G84的线程调度管理器一共可以同时维持多达3072个平行的thread。G8X的“warp”是跟R6XX的Batch类似的象素块,是线程作用的基本范围。因为一个warp的大小是8x4,所以G8X的线程切换单位是32个pixel。小熊在线www.beareyes.com.cn
|