从效率角度(或其他重要角度)来看,哪种方案更可取呢?
情境
一个OpenGL应用程序每帧绘制许多不同位置的线条(60 fps)。假设有10条线。或者有100,000条线。答案会有所不同吗?
- #1 使用永远不会更改的静态VBO,其中包含一条线的2个顶点
每帧将使用一个glDrawArrays调用来绘制每条线,并且在此之间会进行矩阵变换以定位我们的一条线
- #2 每帧更新包含所有线数据的VBO
每帧仅使用一个绘图调用
从效率角度(或其他重要角度)来看,哪种方案更可取呢?
情境
一个OpenGL应用程序每帧绘制许多不同位置的线条(60 fps)。假设有10条线。或者有100,000条线。答案会有所不同吗?
每帧将使用一个glDrawArrays调用来绘制每条线,并且在此之间会进行矩阵变换以定位我们的一条线
每帧仅使用一个绘图调用
第二个方法效率更高。
改变状态,特别是变换和矩阵操作,往往会导致其他状态的重新计算,通常需要更多的数学运算。
然而,更新几何图形只涉及重写缓冲区。
随着现代视频硬件在非常大的带宽总线上的发展,仅仅发送一些浮点数是微不足道的。它们被设计用于快速移动大量数据,这是工作的副作用。更新顶点缓冲区正是它们经常快速执行的任务。如果我们假设每个点占用32个字节(float4位置和颜色),那么100000条线段就不到6 MB,而PCIe 2.0 x16约为8 GB/s。
在某些情况下,具体取决于驱动程序或显卡处理转换的方式,改变一个可能会导致矩阵乘法和其他值的重新计算,包括转换、裁剪平面等。如果你改变状态、绘制几千个多边形并重复进行,这不是问题,但是当状态更改频繁时,会产生相当大的开销。
批处理是解决此问题的好方法,通过最小化状态更改,可以更有效地绘制大量几何图形。
一个非常明显的例子是#1的最佳情况:变换设置不触发任何其他计算,并且驱动程序缓冲处理得非常完美。要绘制100000条线,你需要:
仅函数调用开销就足以使性能崩溃。
另一方面,批处理涉及:
你确实复制了更多的数据,但是VBO的内容很可能仍然不如复制矩阵数据那样昂贵。此外,你节省了大量的CPU时间用于函数调用(从200000降至2)。这简化了你、驱动程序(必须缓冲所有内容并检查冗余调用以优化和处理下载)和视频卡(可能需要重新计算)的生活。为了使它更加清晰,请想象一下实现它的简单代码:
for (i = 0; i < 100000; ++i)
{
matrix = calcMatrix(i);
setMatrix(matrix);
drawLines(1, vbo);
}
(现在取消包装)
matrix = calcMatrix();
setMatrix(matrix);
for (i = 0; i < 100000; ++i)
{
localVBO[i] = point[i];
}
setVBO(localVBO);
drawLines(100000, vbo);