在OpenGL程序中计时?

时间:2019-05-14 18:05:03

标签: opengl timing

我已经学到了足够的OpenGL / GLUT(使用PyOpenGL),以提出一个简单的程序,该程序可以设置片段着色器,绘制全屏四边形并生成与显示同步的帧(shadertoy样式)。我也在某种程度上了解了图形管道。

我不了解的是OpenGL程序和图形管道如何配合在一起。特别是在我的GLUT显示回调中,

# set uniforms
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)  # draw quad
glutSwapBuffers()

我想我通过glDrawArrays赋予顶点着色器来激活顶点着色器,这会产生碎片(像素)。但是,片段着色器是否在glDrawArrays之后立即启动?有碎片,所以它可以做一些事情。另一方面,仍然有可能使用其他绘制命令来创建更多顶点,这可能会a)产生新的片段,b)覆盖现有片段。

我分析了该程序,发现99%的时间都花在glutSwapBuffers中。当然这部分是由于等待垂直同步,但是当我使用非常苛刻的片段着色器时会保持这种状态,这会大大降低帧速率。这表明片段着色器仅在glutSwapBuffers中的某个地方被激活。正确吗?

我知道片段着色器是在GPU而不是CPU上执行的,但是在glutSwapBuffers内,看来CPU(程序)一直等到GPU(着色器)完成为止。

1 个答案:

答案 0 :(得分:4)

  

我分析了该程序,发现99%的时间都花在glutSwapBuffers中。当然这部分是由于等待垂直同步,但是当我使用非常苛刻的片段着色器时会保持这种状态,这会大大降低帧速率。这表明片段着色器仅在glutSwapBuffers中的某个地方被激活。是   正确吗?

不。这种逻辑是完全有缺陷的。这里的要点是片段着色器运行在GPU 上,它与CPU完全异步工作。您不是在测量片段着色器,而是在测量一些隐式的CPU-GPU同步-看起来您的实现在缓冲区交换上同步(如果排队的帧过多),那么您所测量的只是CPU时间必须等待GPU。而且,如果您在不显着增加CPU工作负载的情况下增加了GPU工作负载,则CPU只会花费更多时间等待。

OpenGL本身未定义任何此类内容,因此所有详细信息最终都是完全特定于实现的。规范只是保证了实现的行为就像片段是按照绘制基元的顺序生成的(例如,启用混合功能后,实际顺序将成为相关的场景覆盖场景)。但是什么时候会生成片段,以及在顶点处理和片段着色器调用之间可能发生哪些优化,完全超出了您的控制范围。 GPU可能会采用基于图块的栅格化方案,其中实际片段着色会稍微延迟(如果可能),以提高效率并避免过度着色。

请注意,大多数GPU驱动程序完全异步工作。当您调用gl*()命令时,它会在处理之前返回。它可能仅排队等待后续处理(例如在另一个驱动程序线程中),并且最终将在某些GPU特定的命令缓冲区中进行转换,然后将这些缓冲区传输到GPU。您可能最终会隐式地进行CPU-GPU同步(或带有驱动程序线程的CPU-CPU),例如,当在一次draw调用后读回帧缓冲区数据时,这意味着所有以前的GL命令将被刷新以进行处理,并且CPU将在检索图像数据之前等待处理完成-这也是使此类回读如此缓慢的原因。

因此,任何CPU方面的OpenGL代码测量都是完全没有意义的。您需要在GPU上测量时间 ,这就是Timer Queries的目的。