glutMainLoop()vs glutTimerFunc()?

时间:2018-03-23 09:49:46

标签: animation opengl graphics glut freeglut

我知道glutMainLoop()用于反复调用显示,保持恒定的帧速率。同时,如果我还有glutTimerFunc(),它最后调用glutPostRedisplay(),那么它可以保持不同的帧速率。

当他们一起工作时,究竟发生了什么?定时器功能是否会增加主循环的帧速率并使其更快?或者它是否会更改主循环的默认刷新率?它们如何协同工作?

2 个答案:

答案 0 :(得分:4)

  

我知道glutMainLoop()用于反复调用显示,保持帧速率不变。

没有!那不是glutMainLoop所做的。 glutMainLoop的目的是提取操作系统事件,检查计时器是否已经过时,查看是否必须重新绘制窗口,然后调用用户注册的相应回调函数。这发生在循环中,通常这个循环是从程序的主入口点开始的,因此名称为" main - loop"

  

当他们一起工作时,究竟发生了什么?定时器功能是否会增加主循环的帧速率并使其更快?或者它是否会更改主循环的默认刷新率?它们如何协同工作?

正如已经说过的,调度计时器是glutMainLoop的责任的一部分,所以没有它就没有GLUT计时器。更重要的是,如果发生 no 事件并且没有重新显示,并且如果没有空闲功能注册,glutMainLoop将"块"该程序直到一些有趣的事情发生(即没有消耗CPU周期)。

基本上就像

void glutMainLoop(void)
{
    for(;;){
        /* ... */
        foreach(t in timers){
            if( t.elapsed() ){
                t.callback(…);
                continue;
            }
        }
        /* ... */
        if( display.posted ){
             display.callback();
             display.posted = false;
             continue;
        }
        idle.callback();
    }
}
  

同时,如果我还有glutTimerFunc(),它最后调用glutPostRedisplay(),那么它可以保持不同的帧速率。

GLUT提供的定时器不保证其精度和抖动。因此,它们并不特别适合帧速率限制。

通常帧速率受v-sync(或应该是)的限制,但v-sync上的阻塞意味着您无法使用该时间执行某些有用的操作,因为该进程已被阻止。更好的方法是注册 idle 函数,在该函数中,您可以在Windows clock_gettime(CLOCK_MONOTONIC, …)上轮询高分辨率计时器(在POSIX兼容系统QueryPerformanceCounter上)并执行{{1在一个显示刷新间隔减去之后,渲染帧所需的时间已经过去。

当然很难预测渲染的确切时间,所以通常的方法是收集滑动窗口的平均值和偏差并随之调整。您还希望将该计时器与v-sync对齐。

这当然是一个解决的问题(至少在电气工程中),可以通过锁相环来解决。基本上你有一个"相位比较器" (即,如果你的计时器比你想要同步的东西运行得更慢或更快,则可以比较一些东西),一个"电荷泵" (从相位比较器中添加或减去增量的变量),一个"环路滤波器" (平均滑动窗口)和一个"振荡器" (定时器)由电荷泵中的环路滤波值控制。

因此,您可以轮询v-sync的状态(GLUT功能无法实现,甚至无法使用核心OpenGL甚至某些交换控制扩展) - 您必须使用特定于OS的功能)并比较一下你的计时器是否比那些迟到或跑得快。您将该增量添加到"电荷泵",将其过滤并将结果反馈给定时器。这种方法的好处是,它会自动调整并过滤渲染帧所花费的时间。

答案 1 :(得分:1)

来自glutMainLoop doc pages

  

glutMainLoop进入GLUT 事件处理循环。在GLUT程序中,该程序最多应调用一次。一旦被调用,该例程将永远不会返回。它将根据需要调用已注册的任何回调。 ( grifos mine

这意味着glutMainLoop的想法只是处理事件,调用已安装的任何内容。实际上,我不相信它会一直调用显示,但只有当有事件请求重新显示时才会显示。

这是glutTimerFunc()进入游戏的地方。它会注册一个定时器事件回调,以便在触发此事件时由glutMainLoop调用。请注意,这是可以注册的几个可能的其他事件回调之一。这解释了为什么在doc中他们使用表达式at least

  

(...)glutTimerFunc将计时器回调函数注册为至少msecs毫秒。 (...)