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

标签 android performance opengl-es-2.0 game-loop

一段时间以来,我的 Android 游戏中运动的 Sprite 间歇性“卡顿”。这是一款非常简单的 2D OpenGL ES 2.0 游戏。 (这是一个持续存在的问题,我已经多次重新访问过)。

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

这是我发现的:

当不渲染任何东西时,我得到 60fps(大多数情况下),并且每次调用 onDrawFrame 时,它​​都会报告自己花费的时间超过 16.667 毫秒。现在,如果我渲染某些东西(无论是 1 个四边形还是 100 个四边形,结果都是一样的),我得到 60fps(大部分)但是现在,只有大约 20% 的 onDrawFrame 调用报告自己花费了更长的时间距离上次调用的时间少于 16.667 毫秒。

我真的不明白为什么会发生这种情况,首先,为什么当 onDrawFrame 没有做任何事情时,它被调用得如此“缓慢” - 更重要的是,为什么任何 GL 调用(一个简单的四边形)仍然使onDrawFrame 调用之间的时间超过 16.667 毫秒(尽管频率要低得多)。

我应该说,当 onDrawFrame 报告从上一次迭代开始花费的时间超过 16.667 毫秒时,它几乎总是伴随着 FPS 下降(降至 58 或 59),但并非所有时间,有时,FPS 保持不变.相反,有时当 FPS 下降时,会在最后一次迭代完成后的 16.667 毫秒内调用 onDrawFrame。

所以......

我正在尝试修复我的游戏循环并消除这些“卡顿”——其他一些需要注意的事项:

  • 当我进行方法分析时,它显示 glSwapBuffers,有时需要很长时间
  • 当我执行 GL Trace 时,它​​说的大多数场景渲染时间不到 1 毫秒,但有时奇数帧需要 3.5-4 毫秒 - 相同的场景。除了花费的时间,没有什么改变
  • 几乎每次丢帧或 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();        
}

上面的这个游戏循环是这个优秀 article 中的特色游戏循环.

最佳答案

一些想法:

  • 不要使用 System.currentTimeMillis() 来计时。它基于挂钟,可以通过网络更新。使用基于单调时钟的 System.nanoTime()
  • 参见 this appendix有关游戏循环的一些注释。队列填充对很多事情都很好,但要明白你并没有完全脱离 VSYNC,所以计时往往不准确。
  • 某些设备(特别是那些基于 qcom SOC 的设备)在认为自己空闲时会降低 CPU 速度。始终在触摸屏上积极移动手指的同时记下时间。
  • 如果您想调试帧速率问题,您需要使用 systrace . traceview 分析在这里没那么有用。

参见 Grafika's “记录 GL 应用程序”Activity 是一个简单的 GLES 应用程序示例,该应用程序会丢帧,但会调整动画以使其很少被注意到。

关于Android:了解 OnDrawFrame、FPS 和 VSync (OpenGL ES 2.0),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38512936/

相关文章:

安卓市场(谷歌播放)。获取买家信息

java - 如何在 Activity 之间传递 Seek Bar 变量?

java - 使用 The Grinder 进行 Web 应用程序性能测试

google-chrome - 为什么 WebGL 渲染速度如此不一致?

java - 过滤列表后单击ListView Item

java.security.cert.CertPathValidatorException : Trust anchor for certification path not found

ios - iOS 3D 中的 Opengl 还是 Unity?

android - AdMob 和不同屏幕分辨率/DPI/比率

c# - 在 C# 中使用值参数模拟模板

performance - 为什么循环指令很慢?英特尔就不能有效地实现它吗?