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",对其进行过滤并将结果反馈给计时器。这种方法的好处是,它会自动调整并过滤渲染帧所花费的时间。
我知道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",对其进行过滤并将结果反馈给计时器。这种方法的好处是,它会自动调整并过滤渲染帧所花费的时间。