Android:了解OnDrawFrame,FPS和VSync(OpenGL ES 2.0)

一段时间以来,我经历了Android游戏中运行的精灵间歇性的“口吃”。 这是一个非常简单的2D OpenGL ES 2.0游戏。 (这是我多次重访的一个持续的问题)。

在我的游戏循环中,我有2个“定时器” – 一个logging前一秒的帧数,另一个logging从当前onDrawFrame迭代结束到下一个迭代开始的时间(以毫秒为单位)。

这是我发现的:

当没有渲染任何东西时,我得到60fps(大部分),并且每次onDrawFrame被调用时,它报告自己花费更长的16.667ms。 现在,如果我渲染一些东西(无论是1 quad还是100 quads,结果都是一样的),我会得到60fps(大部分),但是现在,只有大约20%的onDrawFrame调用报告自己需要更长的时间比上次呼叫的16.667ms。

我不明白为什么会发生这种情况,首先,为什么当onDrawFrame没有做任何事情时,它被称为“缓慢” – 更重要的是,为什么GL调用(一个简单的四元组),仍然在onDrawFrame调用长度超过16.667ms(尽pipeless得多)。

我应该说,当onDrawFrame报告从上次迭代中获取的时间超过16.667ms时,它几乎总是伴随着FPS的下降(到58或59),但并不是所有的时间,有时FPS保持不变。 相反,有时当FPS下降时,在最后一次迭代完成的16.667ms内调用onDrawFrame。

所以……

我试图修复我的游戏循环,并根除这些“口吃” – 其他一些事情要注意:

  • 当我做方法分析时,它显示glSwapBuffers,有时需要很长时间
  • 当我进行GL跟踪时,大多数场景的渲染时间不到1ms,但是有时候,奇数帧需要3.5-4ms – 相同的场景。 除了所花费的时间,没有什么变化
  • 几乎每次帧丢失,或onDrawFrame报告一个长时间的延迟(或两者),有一个视觉故障,但不是每次。 大的视觉故障似乎与多个“延迟”的onDrawFrame调用和/或丢弃的帧一致。
  • 我不认为这是一个场景复杂性问题,原因有两个:1)即使我把场景渲染了两次,也不会使问题变得更糟,我仍然大部分时间是60FPS,偶尔会下降,和以前一样2),即使我裸露的场景,我仍然得到了问题。

我显然是误解了一些东西,所以我们不胜感激。

OnDrawFrame

@Override public void onDrawFrame(GL10 gl) { startTime = System.nanoTime(); fps++; totalTime = System.nanoTime() - timeSinceLastCalled; if (totalTime > 16667000) { Log.v("Logging","Time between onDrawFrame calls: " + (totalTime /(double)1000000)); } //Grab time newTime = System.currentTimeMillis() * 0.001; frameTime = newTime - currentTime; //Time the last frame took if (frameTime > 0.25) frameTime = 0.25; currentTime = newTime; accumulator += frameTime; while (accumulator >= dt){ saveGameState(); updateLogic(); accumulator -= dt; } interpolation = (float) (accumulator / dt); Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mVMatrix, 0); render(interpolation); if (startTime > lastSecond + 1000000000) { lastSecond = startTime; Log.v("Logging","fps: "+fps); fps=0; } endTime = startTime; timeSinceLastCalled = System.nanoTime(); } 

上面这个游戏循环是这篇优秀文章中的一个特色。

Solutions Collecting From Web of "Android:了解OnDrawFrame,FPS和VSync(OpenGL ES 2.0)"

一些想法:

  • 不要使用System.currentTimeMillis()来计时。 它基于挂钟,可以通过networking进行更新。 使用基于单调时钟的System.nanoTime()
  • 有关游戏循环的一些说明,请参阅本附录 。 队列填充对很多事情都很好,但是要明白,你并不是完全依靠VSYNC工作,所以时序往往是不准确的。
  • 某些设备(特别是那些基于qcom SOC的设备)当他们认为闲置时会降低CPU速度。 在触摸屏上主动移动手指时,请始终采取时间安排。
  • 如果你想debugging帧率问题,你需要使用systrace 。 traceview分析在这里没有用处。

请参阅Grafika的 “loggingGL应用程序”活动,以获取一个简单的GLES应用程序的示例,该应用程序会丢弃帧,但会调整animation,使其几乎不可见。