OpenGL glDrawElements()调用与基本逻辑代码相比有多费力
How taxing are OpenGL glDrawElements() calls compared to basic logic code?
我计划对OpenGL程序进行一些优化(它不需要优化,但我这样做是为了它)。出于好奇,OpenGL绘图函数与基本逻辑代码相比有多贵?目前,我正在开始一个游戏,屏幕上布满了正方形,以表示2D块状景观。这意味着一个正方形(两个三角形)的绘制调用被调用多次。目前,我计划添加一些代码,查看当前帧中块的位置,并将它们分组在一起。例如,如果有一列有7个块高,我可以调用一个函数,在整个屏幕上绘制一个1 x 7的矩形,依此类推,而不是执行7个单独的drawBlock()函数(包含glDrawElements()调用)。
如果计算绘制内容的代码实际上比单独绘制块消耗了更多的CPU,我就不会麻烦这么做了。
glDrawElements
(或任何其他OpenGL渲染命令)的成本实际上无法估计。这是因为它的成本在很大程度上取决于您在绘图调用之间更改的OpenGL状态。调用OpenGL状态更改函数(基本上,任何不是某种形式的glGet或某种形式的glDraw的OpenGL函数)的成本都会相对较快。但这将使下一次抽签的速度变慢。
这段关于OpenGL性能的视频显示了哪些状态更改在绘制时比其他状态更改更昂贵。真正好的部分大约在31分钟后开始。
如果在绘制调用之间没有更改任何OpenGL状态,则绘制调用相对较快。不同的状态对绘制调用有不同的影响。从最快到最慢(根据NVIDIA上面的介绍,请谨慎对待):
- 非UBO统一更新
- 顶点缓冲区绑定(不更改格式)
- UBO绑定
- 顶点格式更改
- 纹理绑定
- 片段后处理状态更改
- 着色器程序更改
- 渲染目标开关
现在,平局调用将比"基本逻辑"更昂贵。它们并不便宜,即使它们之间没有状态变化。如果效率对你的代码很重要,那么分组你的方块是个好主意。
实际数字高度依赖于平台和供应商。不同操作系统上的驱动程序体系结构差异很大,其中一些系统比其他系统更高效。除此之外,驱动程序实现和硬件可能会导致巨大的性能差异。例如,在同一平台上,使用类似的硬件,一个供应商的draw call吞吐量比另一个供应商高10-20倍。
基于此,以下任何数字都只是一个非常粗略的数量级。你真的需要自己根据你关心的配置来衡量这一点。
有了所有这些免责声明,我预计一个draw调用可以在100条指令(CPU周期)的范围内处理。这种情况下,您只需要进行背靠背的绘制调用,并且管道中没有其他瓶颈。
正如@NicolBolas已经指出的,处理draw调用最昂贵的部分通常是处理延迟的状态更改。大多数情况下,在绘制调用之间会发生状态更改。在这种情况下,对于相对便宜的状态更改(如绑定纹理或缓冲区,或更改某些属性),通常只需要100条指令。
切换帧缓冲区通常非常昂贵,并且在某些平台上非常昂贵。除此之外,我在过去优化和基准测试状态变化时测量的数字显示出的顺序与@NicolBolas回答中的列表截然不同。但同样,这是高度依赖于平台和供应商/硬件的。
还有几个方面使衡量这一点变得有些棘手:
- 大部分CPU时间可能不会在线程中消耗掉。许多驱动程序是多线程的,这意味着处理OpenGL调用所需的大部分工作都被卸载到辅助线程。如果您的应用程序没有使用所有的CPU核心,并且您没有受到功率/热限制的限制,这意味着许多驱动程序工作可以并行进行,而不会大大降低应用程序的速度。但特别是在移动设备和笔记本电脑上,性能通常受到功耗的限制,因此驱动程序开销仍然会降低速度
- 驱动程序消耗的CPU时间只是降低应用程序代码速度的一部分。另一个考虑因素是缓存污染。如果在OpenGL实现处理绘制调用时,应用程序使用的缓存内容被收回,则您自己的代码将获得更多的缓存未命中,并且运行速度会变慢。因此,测量OpenGL调用内部花费的时间只能显示部分情况
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 代码在main()中运行,但在函数中出现错误
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 编译包含字符串的代码时遇到问题
- 我在c++代码中生成了一个运行时#3异常
- 如何在linux终端中同时编译和运行c++代码
- 为cl.exe(Visual Studio代码)指定命令行C++版本
- 在Linux for Windows上编译C++代码时出错
- 我的字符计数代码计算错误.为什么
- 孤立代码块在结构中引发异常
- 在编译C++代码(具有dlib和opencv)到WASM时面临问题
- 为什么我的C#代码在调用回C++COM直到Task时会暂停.等待/线程.加入
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 此代码是否违反一个定义规则
- 为什么我的代码在输出中增加了93天
- 我的简单if-else语句是如何无法访问的代码
- 使用动态分配的数组会导致代码分析发出虚假的C6386缓冲区溢出警告
- 为什么在这个代码结束循环中没有得到结束
- 在c代码之间共享数据的最佳方式
- 这个指针和内存代码打印是什么?我不知道是打印垃圾还是如何打印我需要的值