OpenGL顶点数据传输速度优化
2016-03-27 21:42
351 查看
《OpenGL顶点数据传输速度优化》
作者: 游蓝海
文章链接:http://blog.csdn.net/you_lan_hai/article/details/50994121
转载请注明出处
常规的Batch原理:
· 将渲染命令加入到一个缓冲区当中
· 根据需求对渲染命令进行排序
· 合并所有命令的顶点数据
· 提交顶点数据
· 根据不同材质分批次渲染
传输顶点数据用到的两个API:
我在创建顶点buffer的时候,使用
实测之后发现,掉帧特别明显,glBufferSubData操作相当耗时。百思不得其解,就查看了cocos2d-x v3.x渲染部分的代码,发现他每次提交顶点数据的时候,都是直接使用glBufferData分配空间,而且他们刻意的注释掉了使用glBufferSubData的代码。这让我更迷惑了,感觉自己的理论彻底被颠覆了。
所有的OpenGL操作,并不会被立即同步到显卡,而是被缓存在本地,等到合适的时机(比如swapBuffer)批量传输到显卡。对于顶点数据,会使用DMA进行异步传输,并不会阻塞CPU。
那么问题就来了,如果现在顶点buffer的数据还没有传输完成,我们立马又要向顶点buffer写数据了,这会出现什么情况?比如,如下的操作,写数据与渲染交替进行:
结论是,CPU还是会阻塞,直到DMA把顶点buffer中的数据传输完毕后,才允许我们继续向里面写数据。即便是有DMA异步传输,碰到不良的代码,还是会发生同步等待。
当然,还有一种简单方法,我们也可以调用glBufferData,来强制OpenGL在本地分配一块新的内存,供我们存贮新数据。这样DMA会继续传输旧内存地址上的数据,然后再传输新内存地址上的数据,这种方式下CPU也就不用等待DMA了。虽然浪费了额外的内存空间,但是提高了数据传输的吞吐量,还是值得的。
总结来说,OpenGL的操作是异步的,我们要避免同步操作所引发的阻塞行为。
《OpenGL Insights》第28章:http://download.csdn.net/download/you_lan_hai/9787675
作者: 游蓝海
文章链接:http://blog.csdn.net/you_lan_hai/article/details/50994121
转载请注明出处
前言
最近在给cocos2d-x v2.x的一个项目做渲染优化,执行渲染批处理(Batch)的时候,发现顶点数据传输速度很慢,实在是颠覆了我的OpenGL认知。常规的Batch原理:
· 将渲染命令加入到一个缓冲区当中
· 根据需求对渲染命令进行排序
· 合并所有命令的顶点数据
· 提交顶点数据
· 根据不同材质分批次渲染
传输顶点数据用到的两个API:
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage); void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);
glBufferData会分配一块新的内存,供程序存放数据。
glBufferSubData会使用之前已经分配过的内存来存放数据。
问题
我事先创建了一个顶点buffer和索引buffer,索引buffer不用多说,其数据是一次就计算完成的,之后就不用修改了。而顶点数据每帧都会变化,所以需要动态的更新。我在创建顶点buffer的时候,使用
glBufferData分配足够大的空间。然后每次提交顶点数据的时候,使用
glBufferSubData,仅提交实际使用到的数据。理论上来说,这样不会引发额外的内存分配行为,传输效率应该会很高。伪代码如下:
void init() { glGenBuffer(...); glBindBuffer(...); //分配一块很大的空间 glBufferData(GL_ARRAY_BUFFER, 32 * 1024, NULL, GL_DYNAMIC); ... } void draw() { for(int k = 0; k < parts.size(); ++k) { Part *p = parts[i]; //仅提交用到的数据。可能只有几百字节,或者几k。 glBufferSubData(GL_ARRAY_BUFFER, 0, p->vertices.size(), p->vertices.data()); for(int i = 0; i < p->commands.size(); ++i) { drawCommand(p->commands[i]); } } }
实测之后发现,掉帧特别明显,glBufferSubData操作相当耗时。百思不得其解,就查看了cocos2d-x v3.x渲染部分的代码,发现他每次提交顶点数据的时候,都是直接使用glBufferData分配空间,而且他们刻意的注释掉了使用glBufferSubData的代码。这让我更迷惑了,感觉自己的理论彻底被颠覆了。
原因
接着就去google上寻找原因,看到有人推荐看一下《OpenGL Insights》渲染效率优化部分的文章,文章全是英文,看的不是完全懂。大致理解如下:所有的OpenGL操作,并不会被立即同步到显卡,而是被缓存在本地,等到合适的时机(比如swapBuffer)批量传输到显卡。对于顶点数据,会使用DMA进行异步传输,并不会阻塞CPU。
那么问题就来了,如果现在顶点buffer的数据还没有传输完成,我们立马又要向顶点buffer写数据了,这会出现什么情况?比如,如下的操作,写数据与渲染交替进行:
glBufferSubData(GL_ARRAY_BUFFER, 0, bytes, datas); glDrawElements(GL_TRIANGLES, n, GL_UNSIGNED_SHORT); glBufferSubData(GL_ARRAY_BUFFER, 0, bytes, datas); glDrawElements(GL_TRIANGLES, n, GL_UNSIGNED_SHORT); ...
结论是,CPU还是会阻塞,直到DMA把顶点buffer中的数据传输完毕后,才允许我们继续向里面写数据。即便是有DMA异步传输,碰到不良的代码,还是会发生同步等待。
解决办法
书中说到,可以使用多个顶点buffer交替进行,或者使用顶点buffer的不同位置,都可以达到避免阻塞的作用。当然,还有一种简单方法,我们也可以调用glBufferData,来强制OpenGL在本地分配一块新的内存,供我们存贮新数据。这样DMA会继续传输旧内存地址上的数据,然后再传输新内存地址上的数据,这种方式下CPU也就不用等待DMA了。虽然浪费了额外的内存空间,但是提高了数据传输的吞吐量,还是值得的。
总结来说,OpenGL的操作是异步的,我们要避免同步操作所引发的阻塞行为。
更多阅读
顶点数据传输优化: https://www.opengl.org/wiki/Buffer_Object_Streaming《OpenGL Insights》第28章:http://download.csdn.net/download/you_lan_hai/9787675
相关文章推荐
- MySQL 优化
- Google排名优化的几个影响因素
- 解决Vista系统OpenGL驱动问题的方法整理
- DB2优化(简易版)
- Mysql limit 优化,百万至千万级快速分页 复合索引的引用并应用于轻量级框架
- C#中尾递归的使用、优化及编译器优化
- 对优化Ruby on Rails性能的一些办法的探究
- 优化Ruby脚本效率实例分享
- Delphi下OpenGL2d绘图之画四边形的方法
- Delphi下OpenGL2d绘图之画点的方法
- Delphi下OpenGL2d绘图之初始化流程详解
- Asp编码优化技巧
- 如何监测和优化OLAP数据库
- mysql -参数thread_cache_size优化方法 小结
- 深入学习SQL Server聚合函数算法优化技巧
- MySQL常见的底层优化操作教程及相关建议
- 详解mysql的limit经典用法及优化实例
- 数据库学习建议之提高数据库速度的十条建议
- oracle数据库sql的优化总结
- SQL语句性能优化(续)