GPU 着色器编程实际上有两种不同的途径:
可编程渲染
此模型是在 Direct3D 8.1 中引入的,它是“变换和光照”部分和“纹理混合级联”的旧固定功能旧模型的演变。 Vertex Shader 是一个替代“Transform & Light”的着色器程序,Pixel Shader 替代了“Texture blend cascade”。
要渲染单个对象(点、线或三角形),您需要设置一个顶点着色器,每个点运行一次,作为顶点缓冲区中的原始数据提供(并且可能由索引缓冲区索引)。然后,硬件光栅化器为对象在 2D 屏幕上绘制时触摸的每个像素执行像素着色器。对于着色器和相关状态的特定组合,您可以一次绘制单个像素或 40 亿个三角形。
如果您想更改任何状态或着色器程序本身的任何方面,则必须提交新的绘图。您可以在每次绘制之间更改部分或全部状态,并且在某些情况下,您可能需要显式清除某些状态以避免来自调试层的验证警告。
整个场景的高效状态管理是一项挑战,因此请务必避免假设状态、依赖未设置的默认状态以及假设状态在Present 调用之间逐帧持续存在。
即使在现代 DirectX 管道中,所有装饰包括硬件镶嵌、几何着色器和基于物理的逐像素照明,都是对同一基本概念的扩展。
对于图形渲染,这通常是一个很好的概念模型,并且可以很好地映射到习惯于在旧的 T&L + TextureBlending 模型中思考的人们。它还可以通过多种方式进行扩展,并在关键点非常有效地使用固定功能硬件来完成一般计算中昂贵的事情,例如多边形扫描线转换、z 缓冲区深度剔除和 alpha 混合。
计算
人们使用以前的模型用传统的可编程管道做有趣的非图形工作,你根本不关心光栅化或顶点着色器在做什么,你只需要在整体上绘制一个大矩形渲染目标,然后你在像素着色器中做了很多事情。然后,您将生成的渲染目标视为某种“通用缓冲区”。
这里的问题是,将问题映射到这个“绘图但做其他事情”的模型有点痛苦,这就是我们得到“计算着色器”的地方(DirectX 称之为 DirectCompute)。在此模型中,您根本不使用顶点缓冲区、索引缓冲区、点/线/三角形、光栅化器或纹理混合。您只需运行一个写出缓冲区的着色器。您可以通过Dispatch 控制直接运行多少个着色器实例。
顶点着色器、像素着色器、计算着色器等都可以做基本相同的事情,即查找部分资源(纹理或被视为通用缓冲区),对它们进行计算,并将结果写入其他一些视频内存.由于隐含的输入,着色器的顶级调用有所不同,但仅此而已。
计算着色器可以做的一件事是其他模型的着色器无法做到的,那就是通过少量共享内存与其他着色器实例进行(有限的)通信。这打破了您从以前的模型中获得的极其强大的多线程优势,但使得解决某些问题变得更加容易。在不造成重大停顿的情况下,这件事很难做到,而且您通常需要手动计算所有同步才能获得有效的结果。
我应该提到,CUDA 所做的还有第三条路径,它是一个隐藏所有低级着色器内容并伪装成 C 的系统。它仍然在做与“计算”相同的事情封面。