[高级篇]合并DrawCall

2018/11/24 Map

[高级篇]合并DrawCall

导语 做地图引擎开发时,老司机告诉我,减少drawcall的调用,收益你懂的。 当时我不懂,现在写这篇文章,我懂了。

文章可能大多数是思路方面,代码层面讲解不会太多,欢迎大神们进行批评指正。

  1. 什么是Draw Call?

一个 Draw Call,等于呼叫一次 DrawIndexedPrimitive (DX) or glDrawElements (OGL)**,等于一个 Batch”

​ 每进行一次Draw Call, CPU 通过 “驱动程式” 将顶点资料送往 GPU,GPU接手后将物件画在画面上。由此可知,越多 Draw Call,CPU 就越忙碌。这下更清楚知道 Draw Call 数量所影响的是 CPU 效能而非 GPU。

​ 在地图渲染场景中,存在大量要素的实时绘制,在初始设计架构的时候,考虑到要素的维护,会对每一个要素进行归类,每一类要素进行统一绘制。那么,理论上,地图存在多少类要素,就有多少次DrawCall的调用。所以,在地图场景中,可以理解为一次Draw Call的调用,就是一类要素的绘制过程。

  1. 减少Draw Call收益在哪?

​ Stack overflow有关于Draw Call的讨论,其中提到,GPU的绘制速度大大高于CPU提供给它的时间,如果存在大量带有很少三角形绘制的Draw Call,GPU会长时间处于空闲状态,地图App中,GPU的使用大多数都被地图所占有,让GPU在单位时间内充分负载,解放CPU的占用率,无疑可以提升当前程序的性能。并且,单个地图瓦片中,存在很多类要素,整理成供渲染的三角形后,Draw Call的三角形数目往往只有几个,这也表明了合并的可行性和重要性。

  1. 地图为什么要合并Draw Call?

​ 地图中,主要就是面状要素、线状要素、点要素的绘制。其中,对应基础底图,主要就是基础面、基础线、文字、POI点、路况以及Overlay的绘制。在单个瓦片中,面要素的分类最大是25个,线要素的分类最大是45个,路况的分类最大是8个,在地图倾斜的情况下,屏幕范围内能存在16个瓦片。因此,这里Draw Call的数目是难以想象的,直接的影响就是地图卡顿、闪烁。

  1. 怎么合并Draw Call?

​ 这里拿出路况为例,其余要素解决方案和其类似。

​ 合并Draw Call前,先介绍一个概念,在opengl 2.0及以上中,可以在Shader中从CPU传递数据到GPU,它就是Uniform。

​ Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

​ 每类要素都有其绘制样式,样式随着比例尺的变化而变化,体现在宽度和颜色两个方面。针对于一类要素而言,其宽度和颜色通过shader的uniform来搞定,每类要素从原始数据转化为opengl需要的三角形片段只需要一次。具体渲染流程可以看我之前写的文章。因此,合并Draw Call的难点就在于合并多类要素的宽度和颜色。

​ 我认同一句话,怎么传递给shader数组,图片啊~

img

​ 图片的X轴是比例尺级别,Y轴是要素的ID,想要设置某个要素的颜色,直接通过在片元着色器中,

vec2 pos = vec2(scaleLevel , id);
 gl_FragColor = texture2D(texUnit, tex_pos);

​ 有了理论基础,那么,在进行要素绘制之前,当样式进行更新的时候,通过遍历样式文件,生成一个以比例尺级别为x轴,ID信息为纵轴的图片,每一个坐标存储当前信息的颜色。那么,在组织数据之后,进行绘制的时候,每一个顶点自己去寻找自己的颜色信息,就达到了合并Draw Call的目的。

​ 其实,这里有个坑。

​ 很理所当然的,一个颜色RGB值是0~255范围的,存个width不是轻而易举么。但是,顶点坐标需要在顶点着色器就赋值好,而宽度是其必备的参数,但是大多数android机器不支持顶点着色器获取纹理,那么,把宽度也按照这种方式存储的方式搁浅了。

​ 于是,宽度怎么存呢?目前一个Draw Call中同类要素不会超过16种,于是,用了一个mat4矩阵进行将就,暂时未想出更好的解决方案。

​ 不过,这并不影响Draw Call的合并进度,进行这一系列改造后,地图性能得到了显著的提升,具体对比等我拿到数据来看看。但至少,通过xcode的GPU调试工具可以明显看到Draw Call的减少。

​ 后续该方案应用到了基础线要素,面要素的渲染中,表现良好~

PS .Xcode GPU调试看到的道路分比例尺颜色图片~

img

Search

    Table of Contents