什么是OpenGL/DirectX
只要接触过图像编程就一定听说过OpenGL
和DirectX
,也知道这两者之间存在着竞争关系。这两者实际上就是图像应用编程接口,这些接口用于渲染二维或三维图形。可以说,这些接口充当了上层应用程序和底层GPU的沟通桥梁。一个应用向这些接口发送渲染命令,而这些接口会依次向显卡驱动发送渲染命令,这些显卡驱动是真正知道如何与GPU通信的,正是它们把OpenGL/DirectX的函数调用
翻译成了GPU能够识别的指令
。另外,一块显卡除了有图像处理单元GPU之外,还拥有自己的内存,这个内存通常被称为显存。GPU可以在显存中存储任何数据,但是对于渲染而言有一些数据类型是必须的,所以一般显存中都包含图像缓存
,深度缓存
,纹理缓存
,顶点缓存
。
因为显卡驱动的存在,几乎所有的GPU都可以和OpenGL/DirectX一起合作。从显卡的角度出发,实际上,它只需要和显卡驱动打交道就可以了。而显卡驱动就好像一个中介,负责和两方沟通。因此,一个显卡制造商为了让他们的显卡可以同时和OpenGL和DirectX合作,就必须提供支持这两个接口的显卡驱动。
什么是HLSL,GLSL,CG
在可编程渲染管线出现之前,为了编写着色器代码,开发者们需要学习汇编语言。为了给开发者们方便,就出现了更高级的着色器语言(Shading Language)
,常见的着色器语言有DiretX的HLSL(High Level Shading Language)
,OpenGL的GLSL(OpenGL Shading Language)
以及NVIDIA的CG(C for Graphic)
。HLSL,GLSL,CG都是“高级”
语言,但是这个高级实际上是相对汇编语言来说的。这些语言会被编译成为与机器无关的汇编语言,也被成为中间语言(Intermediate Language)
。然后这些中间语言才会被显卡驱动来翻译成为真正的机器语言,即GPU可以理解的语言。
什么是 Draw Call
Draw Call本身的含义很简单,就是调用图像编程接口,如OpenGL中glDrawElements
命令或者DirectX中的DrawIndexedPrimitive
命令,来命令GPU进行渲染的操作。一个很常见的误区是,Draw Call中造成性能问题的元凶是GPU,认为GPU上的状态切换是耗时的,其实不是的,真正“拖后腿”
其实是CPU。
CPU和GPU是怎样实现并行工作的?
如果没有流水线化,那么CPU需要等到GPU完成上一个渲染任务才能再次发送渲染指令,这种方法显然会造成效率地下。解决的办法也很简单,就是使用一个命令缓冲区(Command Buffer)。命令缓冲区包含了一个队列,由CPU添加命令,而由GPU从中读取命令,值得注意的是,添加和读取的过程是相互独立的。命令缓冲区的存在是的CPU和GPU可以相互独立。当CPU需要渲染一些对象时,它可以向缓冲区中添加命令,而当GPU完成了上一次渲染任务后,它从命令队列中再取出一个命令并且执行它。
命令缓冲区中的命令有很多种类,而Draw Call只是其中一种,其它命令还有改变渲染状态
等等。
为什么Draw Call多了会影响帧率?
我们先来做一个实验:请创建10000个小文件,每个文件的大小为1KB,然后把它们从一个文件夹复制到另一个文件夹中。你会发现,尽管这些文件的空间总和不超过10MB,但还是要花费很长的时间。然后你再创建一个10MB的文件,把这个文件从一个文件夹复制到另一个文件夹中,这次复制操作所消耗的时间却少很多!会发生这样的现象的原因在于,每一个复制动作需要有很多额外的操作,比如分配内存
,创建各种各样的元数据
,这些操作将造成很多额外的性能开销。
渲染的过程虽然和复制一大堆文件不一样,但是从感性的认知来说这两点是有相同点的。在每一次调用Draw Call前,CPU要向GPU发送很多内容,包括数据,状态和命令等。GPU的渲染能力是很强的,渲染200个还是2000个三角形网络通常没有什么区别,因此渲染速度往往会快于CPU提交命令的速度。如果Draw Call的数量太多,CPU就会把时间大量浪费在提交Draw Call上,造成CPU的过载。
如何减少Draw Call?
一个显然的优化想法就是把很多小的Draw Call合并成为一个大的Draw Call这就是批处理的思想。值得注意的是,由于我们需要在CPU的内存中合并网格,而合并的过程是需要消耗时间的。因此,批处理技术更加适合那些处于静态的物体,例如不会移动的大地,石头等。
Shader究竟是什么?
- GPU流水线上一些可高度编程的阶段,而由着色器编译出来的最终代码是会在GPU上运行的。
- 有一些特定类型的着色器,如顶点着色器,片元着色器等。
- 依靠着色器我们可以控制流水线中的渲染细节,例如顶点着色器来进行
顶点变换
以及传递数据
,用片元着色器来进行逐像素的渲染
。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。