这一章节主要讲述了GPU硬件的发展历程,GPU流水管线以及GPU各种着色器的作用及其适用性(由于本章涉及要很多概念上以及专业术语的翻译,有的地方可能没有理解到位而导致错误,希望大家能够帮忙指出,这也是一个互相进步的过程)。
这一章的篇幅略长,这里分为两篇介绍:上篇主要介绍GPU并行架构以及GPU流水管线的各阶段可操作程度;下篇主要介绍各着色器的特点及作用。
数据并行架构
CPU与GPU对于处理延迟的不同策略:
CPU:有多重处理器,大部分CPU芯片包含本地缓存,内存中存放了需要使用的数据。以及其他的技术:预分支,指令重排,寄存器重命名、缓存预取等。
GPU:有一个大的处理器组,称为着色器内核,通常有上千个。GPU是流处理器,依次有序的处理相似数据集。由于这种相似性(例如,一系列顶点或者像素),GPU可以大规模并行的方式处理数据。相邻数据之间保持独立,这样才可以完全并行处理,不需要等待另一个处理器的结果。
原文有一个GPU架构的,很生动的例子:
假设一个网格有2000个像素需要被渲染,以及有一个最弱的GPU,每次只能执行一个像素。
他开始执行第1个像素,对寄存器中的值进行一系列算法操作(寄存器是本地的,因此可以快速访问,因此不会发生延迟)。接下来他需要访问一张纹理资源,这是一个独立的资源,并且访问纹理可能涉及到一些其他的内容,这时间,处理器等待访问纹理的数据,什么也做不了,就产生了延迟。想象一下2000个像素处理完,延迟有多大。
此时为了使GPU变得更快,给每个片元一段本地寄存器储存空间。当第一个像素执行到访问纹理,就记录下当前像素的属性,以及当前执行的指令,然后开始处理第二个像素,依次处理到2000个像素。这时转回到第一个像素,他的纹理数据正好返回,则执行后续操作,然后是第二个像素,直到2000个像素全部执行完毕。这样看来,单个像素的执行时间变长了,但是整体处理的时间将大大减少。
接下来有几个概念:
SIMD: single instruction, multiple data,单一指令,多重数据。固定数量的着色器程序同步执行同一逻辑。
线程:2000个片元,每个片元的处理称为一个线程。此处线程不同于CPU的线程,他包括一部分将数据传送给着色器的内存,以及供着色器执行的寄存器空间。
线程束:使用同样着色器程序的线程组成的组。
例如:要执行2000个线程。英伟达的GPU一个线程束支持32个线程。因此就需要62.5个线程束。每个线程束的执行与单个GPU例子相同。第一个线程束执行,其中32个线程同步执行同一逻辑,32个线程会同时遇到访问内存的延迟的情况,因此整个线程束会停掉,转向第二个线程束。直到所有线程束执行完毕,此时转向第一个线程束,其访问内存的数据返回,那么他开始继续执行逻辑。过程如图所示(图片源自原书):
接下来会引出两个问题:
其一:寄存器使用的数量。每个线程都需要一段寄存器。但是需要的寄存器越多,可以执行的线程越少,那么线程束就越少。线程束的不足,就会引发上述访问内存的延迟。
其二:动态分支:由if和循环引起的。同一线程束中32个线程是同步处理同一逻辑,如果遇到分支,32个线程都只取其中一个分支的结果,那么另一个分支则不会执行。一旦其中一个线程所需结果不同,那么32个线程同时会把两个流程执行一遍,然后各取所需,这也叫线程发散。
GPU流水管线
先上图:
依据可操作性,各步骤涂上了不同的颜色。
绿色是完全可编程模块:顶点着色器,曲面细分着色器,几何着色器,像素着色器;
黄色是不可编程但是高度可配置的模块:屏幕映射阶段,合并阶段;
蓝色是固定执行模块:裁剪,三角形设置和遍历
顶点着色器是完全可编程的阶段,是用来实现几何阶段。几何着色器主要用来操作图元上的顶点,也可以用来执行每图元着色操作,销毁图元,创建图元等。曲面细分着色器和几何着色器是可选的,也并不是所有的GPU都支持这些着色器,尤其是移动平台。
裁剪,三角形设置和三角形遍历阶段是固定执行阶段。屏幕映射受窗口和视口所影响。像素阶段是完全可编程的阶段。尽管合并阶段不是可编程的,但是他是高度可配置的,可以设置执行的各种操作。合并阶段主要是负责颜色,深度缓冲区,混合,模板缓冲区以及其他缓冲区数据的更新。
着色器以及图形API的发展
直接上图:
这一部分主要讲了dx和opengl的一路发展历程,篇幅也很长,这里就不在赘述,有兴趣可以翻翻原书。从图中就可以看出,短短二十几年,图形学发展迅速,从最初的固定渲染管线,到现在的可编程渲染管线,将来可能发展为更高度自由的可编程性。也可以看出,未来的图形学将有不可预估的可能性。
小结:
知识点:对GPU架构的实现有所了解,尤其是线程及线程束的执行机制。对目前阶段的GPU流水阶段各阶段的可操作程度有所掌握。下一篇文章将着重介绍各种着色器的特点。
参考资料:Real-Time-Rendering-4th