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

Zip*_*ppy 12 performance android game-loop opengl-es-2.0

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

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

这就是我发现的:

当不渲染任何东西时,我得到60fps(大部分),并且每次调用onDrawFrame时,它报告自己需要更长的16.667ms.现在,如果我渲染一些东西(无论是1个四边形还是100个四边形,结果是相同的),我得到60fps(大部分)但是现在,只有大约20%的onDrawFrame调用报告自己需要更长的时间比上次通话时的16.667毫秒.

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

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

所以......

我正在尝试修复我的游戏循环并根除这些"口吃" - 其他一些注意事项:

  • 当我进行方法分析时,它会显示glSwapBuffers,有时需要很长时间
  • 当我执行GL跟踪时,大多数场景表示在不到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();        
}
Run Code Online (Sandbox Code Playgroud)

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

fad*_*den 8

一些想法:

  • 不要System.currentTimeMillis()用于计时.它基于挂钟,可由网络更新.使用System.nanoTime(),基于单调时钟.
  • 有关游戏循环的一些注意事项,请参阅此附录.队列填充对许多事情都很好,但是要知道你并没有完全脱离VSYNC,所以时间往往是不准确的.
  • 有些设备(特别是那些基于qcom SOC的设备)在认为它们处于空闲状态时会降低CPU速度.在触摸屏上主动移动手指的同时,始终采取计时.
  • 如果要调试帧速率问题,则需要使用systrace.traceview分析在这里没有用.

请参阅Grafika的 "记录GL应用程序"活动,以获取一个简单的GLES应用程序的示例,该应用程序可以删除帧,但会调整动画以使其很少引人注意.