考虑到GPU具有任务队列并且是异步的,计算FPS的正确方法是什么?

cma*_*t85 7 opengl profiling gpu

我一直认为计算FPS的正确方法是简单地计算绘制循环迭代所花费的时间.互联网的大部分似乎都是一致的.

但!

现代图形卡被视为异步服务器,因此绘制循环发送已经在GPU上的顶点/纹理/等数据的绘图指令.在GPU上的请求完成之前,这些调用不会阻塞调用线程,只是将它们添加到GPU的任务队列中.那么"传统"(而且无处不在)的方法肯定只是衡量呼叫发送时间吗?

让我提出的问题是我实施了传统的方法并且它始终提供了极其荒谬的高帧率,即使渲染的内容导致动画变得不连贯.重新阅读我的OpenGL SuperBible让我进入glGenQueries,它允许我计算渲染管道的各个部分.

总而言之,计算FPS的"传统"方式是否完全与(几乎没有)现代图形卡相关?如果是这样,为什么GPU分析技术相对未知?

Bah*_*bar 13

测量fps很难.由于各种想要测量fps的人不一定想要测量相同的东西,这一点变得更加困难.所以问问自己这个.你为什么想要一个fps号码?

在我继续探讨所有陷阱和潜在解决方案之前,我想指出这绝不是"现代图形卡"特有的问题.如果有的话,过去更糟糕的是,使用SGI类型的机器,渲染实际上发生在可能远离客户端的图形系统上(如物理远程).GL1.0实际上是根据客户端 - 服务器定义的.

无论如何.回到手头的问题.

fps,即每秒帧数,实际上是试图在一个数字中传达一个关于应用程序性能的粗略概念,其数量可以直接与屏幕刷新率等相关.对于性能的第一级近似,它做得很好.一旦您想深入研究更精细的分析,它就会完全破裂.

问题实际上是对应用程序的"平滑感"最重要的事情是当你画的画面最终出现在屏幕上时.重要的第二件事是在触发动作的时间与其效果在屏幕上显示的时间(总延迟)之间需要多长时间.

当一个应用程序绘制一系列帧时,它会在时间s0,s1,s2,s3,......提交它们,并最终在t0,t1,t2,t3,...屏幕上显示...

为了感觉顺利,您需要以下所有内容:

  1. tn-sn不是太高(延迟)
  2. t(n + 1)-t(n)小(小于30ms)
  3. 模拟增量时间也有一个严格的约束,我将在后面讨论.

当您测量渲染的CPU时间时,最终测量s1-s0以接近t1-t0.事实证明,平均而言,这并不是事实,因为客户端代码永远不会"太远"(假设您一直在渲染帧.请参阅下面的其他案例).实际上发生的事情是,当它试图走得太远时,GL将最终阻塞CPU(通常在SwapBuffer时间).阻塞时间大致是GPU与单帧上的CPU相比所花费的额外时间.

如果您真的想要测量t1-t0,正如您在自己的帖子中提到的那样,查询更接近它.但是......事情从来没有那么简单.第一个问题是,如果你受CPU限制(意味着你的CPU不够快,不能总是为GPU提供工作),那么时间t1-t0的一部分实际上是空闲的GPU时间.这不会被查询捕获.您遇到的下一个问题是,根据您的环境(显示合成环境,vsync),查询实际上可能只测量应用程序在渲染到后台缓冲区时所花费的时间,这不是完整的渲染时间(因为显示尚未显示)当时更新).它确实让您大致了解渲染需要多长时间,但也不准确.另请注意,查询也受图形部分的异步性影响.因此,如果您的GPU在一段时间内处于空闲状态,则查询可能会错过该部分.(例如,假设您的CPU需要很长时间(100毫秒)才能提交帧.GPU会在10毫秒内执行完整帧.您的查询可能会报告10毫秒,即使总处理时间接近100毫秒......).

现在,关于"基于事件的渲染",而不是我到目前为止所讨论的连续渲染.这些类型的工作负载的fps没有多大意义,因为目标不是尽可能多地绘制f.GPU性能的自然度量是ms/f.也就是说,它只是图片的一小部分.什么真正重要的有从你决定你想更新屏幕和它发生的时间的时间花费时间.不幸的是,这个数字很难找到:它通常在您收到触发流程的事件时开始,并在屏幕更新时结束(您只能通过摄像头捕获屏幕输出来测量...).

问题是,在2和CPU之间存在潜在的重叠,或者没有(或者甚至在CPU停止提交命令和GPU开始执行命令之间有一些延迟).这完全取决于实施的决定.您可以做的最好的事情是在渲染结束时调用glFinish以确保GPU已完成处理您发送的命令,并测量CPU上的时间.该解决方案确实会降低CPU方面的整体性能,如果您打算在......之后立即提交下一个事件,则可能会降低GPU方面的整体性能.

最后讨论"模拟增量时间的硬约束":

典型动画使用帧之间的增量时间来向前移动动画.主要的问题是,对于完全平滑的动画,你真的希望在s1将帧提交为t1-t0时使用的增量时间(这样当t1显示时,实际上从前一帧花费的时间确实是t1 -T0).问题当然是你不知道在提交s1时t1-t0是什么...所以你通常使用近似值.许多人只使用s1-s0,但这可能会崩溃 - 例如,SLI类型的系统可能会在各种GPU之间的AFR渲染中产生一些延迟.您还可以尝试通过查询使用t1-t0(或更可能是t0-t(-1))的近似值.这种错误的结果很可能是SLI系统上的微观口吃.

最强大的解决方案是说"锁定到30fps,并始终使用1/30s".它也是允许在内容和硬件上留有最小余地的那个,因为你必须确保你的渲染确实可以在那些33ms内完成......但是一些控制台开发人员选择这样做(固定硬件使它更简单).