如何在使用Android的GLSurfaceView.RENDERMODE_CONTINUOUSLY时限制帧率?

时间:2011-01-23 07:04:03

标签: android opengl-es

我在Android中运行JNI的C ++游戏。由于场景复杂性,帧速率从大约20-45fps变化。任何高于30fps的东西对于游戏都是愚蠢的;它只是燃烧电池。我想将帧速率限制为30 fps。

  • 我可以切换到RENDERMODE_WHEN_DIRTY,并使用Timer或ScheduledThreadPoolExecutor来requestRender()。但这增加了一大堆额外的移动部件,这些部件可能会也可能不会一致且正确地工作。
  • 当事情快速运行时,我尝试注入Thread.sleep(),但这对于小时间值似乎根本不起作用。它可能只是将事件支持到队列中,而不是实际上暂停。

API中是否隐藏了“capFramerate()”方法?有任何可靠的方法吗?

5 个答案:

答案 0 :(得分:38)

Mark的解决方案几乎是好的,但并不完全正确。问题是交换本身需要相当长的时间(特别是如果视频驱动程序是缓存指令)。因此,您必须考虑到这一点,否则您将以低于预期的帧速率结束。 事情应该是:

一开始的某个地方(比如构造函数):

startTime = System.currentTimeMillis();

然后在渲染循环中:

public void onDrawFrame(GL10 gl)
{    
    endTime = System.currentTimeMillis();
    dt = endTime - startTime;
    if (dt < 33)
        Thread.Sleep(33 - dt);
    startTime = System.currentTimeMillis();

    UpdateGame(dt);
    RenderGame(gl);
}

这样你就会考虑交换缓冲区所需的时间和绘制帧的时间。

答案 1 :(得分:9)

使用GLSurfaceView时,在Renderer的onDrawFrame中执行绘图,该绘图由GLSurfaceView在单独的线程中处理。只需确保每次调用onDrawFrame都需要(1000 / [帧])毫秒,在你的情况下需要33毫秒。

要执行此操作:(在onDrawFrame中)

  1. 使用System.currentTimeMillis测量开始绘制前的当前时间(让我们称之为startTime)
  2. 执行绘图
  3. 再次测量时间(让我们称之为endTime)
  4. deltaT = endTime - starTime
  5. 如果deltaT&lt; 33,睡眠(33-deltaT)
  6. 就是这样。

答案 2 :(得分:2)

Fili's answer对我来说很好看,很遗憾地将我的Android设备上的FPS限制为25 FPS,尽管我要求30。我发现Thread.sleep()工作不够准确并且睡眠时间比它长应该。

我从LWJGL项目中找到了这个实现来完成这项工作: https://github.com/LWJGL/lwjgl/blob/master/src/java/org/lwjgl/opengl/Sync.java

答案 3 :(得分:0)

Fili的解决方案对某些人来说是失败的,所以我怀疑它一直在下一次vsync之后而不是之前就已经睡了。我还觉得将睡眠移动到函数的末尾会产生更好的结果,因为它可以在下一个vsync之前填充当前帧,而不是尝试补偿前一个。 Thread.sleep()是不准确的,但幸运的是我们只需要精确到最近的vsync周期为1/60秒。 LWJGL代码tyrondis在这种情况下发布了一个似乎过于复杂的链接,它可能是为了在vsync被禁用或不可用时设计的,在这个问题的上下文中不应该这样。

我会尝试这样的事情:

private long lastTick = System.currentTimeMillis();

public void onDrawFrame(GL10 gl)
{
    UpdateGame(dt);
    RenderGame(gl);

    // Subtract 10 from the desired period of 33ms to make generous
    // allowance for overhead and inaccuracy; vsync will take up the slack
    long nextTick = lastTick + 23;
    long now;
    while ((now = System.currentTimeMillis()) < nextTick)
        Thread.sleep(nextTick - now);
    lastTick = now;
}

答案 4 :(得分:-3)

您也可以尝试从onSurfaceCreated()中减少线程优先级:

Process.setThreadPriority(Process.THREAD_PRIORITY_LESS_FAVORABLE);