我知道glutMainLoop()用于一遍又一遍地调用显示,以保持恒定的帧速率。
不!那不是什么glutMainLoop。的目的glutMainLoop是拉动操作系统事件,检查计时器是否已过去,查看是否必须重绘窗口,然后调用用户注册的相应回调函数。这是循环发生的,通常此循环从程序的主入口点开始,因此命名为“ main-loop”。
当他们一起工作时,真正发生了什么?计时器功能是否会增加主循环的帧速率并使其更快?还是会更改主循环的默认刷新率?它们如何协同工作?
如前所述,调度计时器是的一部分责任glutMainLoop,因此如果没有GLUT计时器,您将无法使用。更重要的是,如果未发生任何事件且未发布任何重新显示,并且未注册空闲函数,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();
}
}
Run Code Online (Sandbox Code Playgroud)
同时,如果我还有glutTimerFunc(),它在最后调用glutPostRedisplay(),因此它可以保持不同的帧速率。
GLUT提供的计时器不能保证其精度和抖动。因此,它们不是特别适合帧速率限制。
通常,帧速率受垂直同步的限制(或应该受到垂直同步的限制),但垂直同步的阻止意味着您无法使用该时间来执行有用的操作,因为该过程已被阻止。更好的方法是注册一个空闲函数,在该函数中,您轮询高分辨率计时器(在POSIX兼容系统上clock_gettime(CLOCK_MONOTONIC, …),在Windows上QueryPerformanceCounter),然后执行glutPostRedisplay一个显示刷新间隔减去渲染帧所需的时间。
当然,很难预测渲染将要花费多长时间,因此通常的方法是收集滑动窗口的平均值和偏差并据此进行调整。另外,您还想使该计时器与垂直同步对齐。
这当然是解决的问题(至少在电气工程中),可以通过锁相环解决。本质上,您有一个“相位比较器”(即可以比较您的计时器比您要同步的时钟慢还是快),一个“电荷泵”(您在相位比较器的增量中添加或减去的变量),由电荷泵中的环路滤波值控制的“环路滤波器”(滑动窗口平均值)和“振荡器”(计时器)。
因此,您轮询了v-sync的状态(GLUT功能无法实现,核心OpenGL甚至某些交换控制扩展甚至都无法实现–您必须使用OS特定的功能)并比较计时器是否正常相比之下落后或跑得快。您将该增量添加到“电荷泵”中,对其进行过滤,然后将结果反馈到计时器中。这种方法的好处是,它可以自动调整并过滤渲染帧所花费的时间。