glutMainLoop() 与 glutTimerFunc()?

glutMainLoop() vs glutTimerFunc()?

我知道glutMainLoop()是用来一遍又一遍的调用display,保持恒定的帧率。同时,如果我还有glutTimerFunc(),它在最后调用glutPostRedisplay(),那么它可以保持不同的帧率。

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

来自glutMainLoop doc pages:

glutMainLoop enters the GLUT event processing loop. This routine should be called at most once in a GLUT program. Once called, this routine will never return. It will call as necessary any callbacks that have been registered. (grifos mine)

这意味着 glutMainLoop 的想法只是处理事件,调用任何已安装的东西。确实,我不相信它会不停地调用display,而是只有在有事件要求它重新显示时才会调用。

这就是 glutTimerFunc() 发挥作用的地方。它注册了一个计时器事件回调,当此事件被触发时,glutMainLoop 将调用该回调。请注意,这是可以注册的几个可能的其他事件回调之一。这解释了为什么在文档中他们使用表达式 at least.

(...) glutTimerFunc registers the timer callback func to be triggered in at least msecs milliseconds. (...)

I know that glutMainLoop() is used to call display over and over again, maintaining a constant frame rate.

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

When they are working together, what really happens ? Does the timer function add on to the framerate of main loop and make it faster ? Or does it change the default refresh rate of main loop ? How do they work in conjunction?

如前所述,调度计时器是 glutMainLoop 职责的一部分,因此没有它就不可能有 GLUT 计时器。更重要的是,如果发生 no 事件并且没有发布 re-display 并且如果没有注册 idle 函数,glutMainLoop 将"block" 程序直到发生一些有趣的事情(即没有 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();
    }
}

At the same time, if I also have glutTimerFunc(), which calls glutPostRedisplay() at the end, so it can maintain a different framerate.

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

通常帧率受限于 v-sync(或应该如此),但阻塞在 v-sync 意味着你不能利用这段时间做一些有用的事情,因为进程被阻塞了。更好的方法是注册一个 idle 函数,在其中轮询高分辨率计时器(在 POSIX 兼容系统 clock_gettime(CLOCK_MONOTONIC, …) 上,在 Windows QueryPerformanceCounter) 并在一个显示刷新间隔 减去 渲染帧所需的时间后执行 glutPostRedisplay

当然很难准确预测渲染需要多长时间,所以通常的方法是收集滑动 window 平均值和偏差并据此进行调整。您还想将该计时器与 v-sync.

对齐

这当然是一个已解决的问题(至少在电气工程中),可以通过 锁相环 来解决。本质上你有一个 "phase comparator"(即比较你的计时器 运行 比你想要同步的东西慢或快的东西),一个 "charge pump"(你添加到或从中减去的变量来自相位比较器的增量),一个 "loop filter"(滑动 window 平均值)和一个 "oscillator"(计时器)由电荷泵中的环路滤波值控制。

所以你轮询 v-sync 的状态(对于 GLUT 函数不可能,甚至对于核心 OpenGL 甚至一些交换控制扩展也不可能 - 你必须使用 OS 的特定功能)并比较您的计时器是否滞后或 运行 与之相比更快。您将该增量添加到 "charge pump",对其进行过滤并将结果反馈给计时器。这种方法的好处是,它会自动调整并过滤渲染帧所花费的时间。