OpenGL更适合批量绘制或具有静态VBO

mk1*_*k12 18 opengl performance vbo draw

从效率的角度来看(或者如果它很重要的另一种观点),什么是可取的?

情况
一种OpenGL应用程序,每帧(60 fps)在不同位置绘制许多行.可以说有10行.或100 000行.答案会有所不同吗?

  • #1有一个永不改变的静态VBO,包含一行的2个顶点

每一帧每行都会有一个glDrawArrays调用,而在两者之间会有矩阵变换来定位我们的一行

  • #2用每帧的所有行数据更新VBO

每个帧都有一个绘制调用

ssu*_*ube 28

第二个是非常有效率.

改变状态,尤其是转换和矩阵,往往会导致其他状态的重新计算,并且通常会导致更多的数学运算.

但是,更新几何图形只需要覆盖缓冲区.

使用相当大的带宽总线上的现​​代视频硬件,发送一些浮动是微不足道的.它们旨在快速移动大量数据,这是工作的副作用.更新顶点缓冲区正是它们经常和快速执行的操作.如果我们假设每个32字节(float4位置和颜色),100000个线段小于6 MB,PCIe 2.0 x16大约8 GB/s,我相信.

在某些情况下,根据驱动程序或卡处理变换的方式,更改一个可能会导致某些矩阵乘法并重新计算其他值,包括变换,剔除和剪裁平面等.如果您更改状态,这不是问题,绘制几千个多边形,并重复,但当状态经常发生变化时,它们将会产生巨大的成本.

之前解决的一个很好的例子是批处理的概念,最小化状态变化,因此可以在它们之间绘制更多的几何.这用于更有效地绘制大量几何体.

作为一个非常明显的例子,考虑#1的最佳情况:变换集触发没有额外的计算和驱动程序热情和完美缓冲.要绘制100000行,您需要:

  • 100000个矩阵集(在系统RAM中)
  • 带有函数调用开销的100000个矩阵集调用(对于视频驱动程序,将矩阵复制到缓冲区)
  • 将100000个矩阵复制到视频RAM中,一次性执行
  • 100000线绘制调用

单独的函数调用开销会破坏性能.

另一方面,批处理涉及:

  • 系统RAM中的100000点计算和设置
  • 1个vbo复制到视频RAM.这将是一个很大的块,但一个连续的块,双方都知道会发生什么.它可以很好地处理.
  • 1个矩阵集调用
  • 1矩阵复制到视频RAM
  • 1画一个电话

您可以复制更多数据,但VBO内容很可能不如复制矩阵数据那么昂贵.另外,在函数调用中可以节省大量的CPU时间(200000到2).这简化了您的生活,驱动程序(必须缓冲所有内容并检查冗余调用并优化和处理下载)以及可能的视频卡(可能必须重新计算).为了使其清晰,可视化简单的代码:

1:

for (i = 0; i < 100000; ++i)
{
    matrix = calcMatrix(i);
    setMatrix(matrix);
    drawLines(1, vbo);
}
Run Code Online (Sandbox Code Playgroud)

(现在打开那个)

2:

matrix = calcMatrix();
setMatrix(matrix);
for (i = 0; i < 100000; ++i)
{
    localVBO[i] = point[i];
}
setVBO(localVBO);
drawLines(100000, vbo);
Run Code Online (Sandbox Code Playgroud)

  • 这只是数学.在10个四边形的情况下,成本发生变化; 几何体更小,你不得不有更多的函数调用开销.在您的示例中,大量的点使得它成为一个非常明确的决定.在不同情况下,您需要分析并查看瓶颈所在并进行相应优化. (4认同)
  • 好吧,那么这是否意味着最好先烘焙VBO然后绘制,而不是使用矩阵进行转换?如果我说一把,也许是10个移动的纹理四边形怎么办?计算对象的坐标,重新创建VBO,上传和绘图真的会更好吗?与在每个对象上使用矩阵转换(我优化为简单的2次加法而不是64次乘法和48次加法)相反,然后为每个对象绘制. (3认同)
  • 我想指出的一点是,OpenGL矩阵操作(在以后的版本中不推荐使用)不会在GPU上发生.他们是由司机执行的.正是这个驱动程序端执行,可能会触发级联的依赖状态更改.更糟糕的是,你会在绘图之间进行状态更改,因此你会在其工作流程中破坏GPU; 超标量处理器需要一些任务的工作周期来提高效率.因此,很多个人动作最好在大缓冲区中进行.然而,通过矩阵可以更好地完成整个集合的单个甚至移动. (3认同)