使用 gettimeofday 钳制 FPS

Posted

技术标签:

【中文标题】使用 gettimeofday 钳制 FPS【英文标题】:Clamping FPS with gettimeofday 【发布时间】:2012-07-11 19:56:51 【问题描述】:

我正在尝试通过使用 gettimeofday 将我的游戏循环限制在特定的 FPS 上。这是一个非常初级的游戏,所以它一直在消耗我所有的处理能力。

无论我将 FRAMES_PER_SECOND 设置得有多低,它都会继续尝试以尽可能快的速度运行。

我非常了解 deWiTTERS 对游戏循环的看法,但我使用的是 gettimeofday 而不是 GetTickCount b/c 我在 Mac 上。

另外,我正在运行 OSX 并使用 C++、GLUT。

这是我的主要外观:

int main (int argc, char **argv)

    while (true)
    
    timeval t1, t2;
    double elapsedTime;
    gettimeofday(&t1, NULL); // start timer

    const int FRAMES_PER_SECOND = 30;
    const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;
    double next_game_tick = elapsedTime;
    int sleep_time = 0;

    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH); 
    glutInitWindowSize (windowWidth, windowHeight); 
    glutInitWindowPosition (100, 100);
    glutCreateWindow ("A basic OpenGL Window"); 
    glutDisplayFunc (display); 
    glutIdleFunc (idle); 
    glutReshapeFunc (reshape);
    glutPassiveMotionFunc(mouseMovement); //check for mouse movement
    glutMouseFunc(buttonPress); //check for button press

    gettimeofday(&t2, NULL); // stop timer after one full loop
    elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;      // compute sec to ms
    elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0;   // compute us to ms

    next_game_tick += SKIP_TICKS;
    sleep_time = next_game_tick - elapsedTime;
    if( sleep_time >= 0 )
        
        sleep( sleep_time );
        

    glutMainLoop (); 
    

我尝试将 gettimeofday 和 sleep 函数放置在多个位置,但我似乎找不到它们的最佳位置(考虑到我的代码甚至是正确的)。

【问题讨论】:

如果您使用 GLUT,为什么不使用 glutGet(GLUT_ELAPSED_TIME) 非常感谢您的评论。我希望有一天能使用 GLUT 的替代品,我认为如果我开始至少让我的计时器功能非 GLUT 特定,那将是朝着正确方向迈出的一步。 【参考方案1】:

这只会被调用一次。我相信你需要将 FPS 逻辑放在 display 函数中,因为 glutMainLoop 永远不会返回。 (这也意味着不需要你的 while 循环。)

edit:或者更有可能它应该进入您的空闲函数。我已经有一段时间没有使用 glut 了。

【讨论】:

非常感谢您的回复。但是,我将整个 FPS 代码移到了我的 glutIdleFunc 中 - 程序在运行时就崩溃了……我的印象是我不得不将 gettimeofday(&t1, NULL);gettimeofday(&t2, NULL); 与我的 glutDisplayFunc 分开? Update1:​​我已将所有 FPS 代码移至 glutIdleFunc(如您在上面建议的那样)。但是,只有注释掉我的if( sleep_time >= 0 ) 语句,我才能让程序运行。也许我的sleep 代码有问题? Update2:我已将 sleep( sleep_time ); 更改为 usleep( sleep_time ); b/c 我正在处理微秒。至少不再冻结我 :) 然而,即使将我的 FRAMES_PER_SECOND 更改为 5,我也没有看到帧速率发生变化,因为程序继续以极快的速度运行。还在尝试。 更新 3:已修复 - 谢谢! :) B/c 我现在以微秒为单位计算我的睡眠,我不得不乘以 sleep_time 本身:usleep( sleep_time * 1000 ) 谢谢,谢谢,谢谢 :)【参考方案2】:

glutMainLoop():

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

【讨论】:

非常感谢您的评论。我在 main 中完全删除了 while 循环。谢谢你。另外,我正在尝试将其余的 FPS 代码移动到 glutIdleFunc 中。仍在努力。【参考方案3】:

在您从elapsed_time 初始化next_game_tick 时,您还没有初始化elapsed_time

【讨论】:

非常感谢您的回复。我已经继续并将elapsedTime 初始化为任意值:float elapsedTime = 1; 但是,在将 FPS 代码移动到 glutIdleFunc 之后,程序对我来说是冻结的。仍在尝试。谢谢。

以上是关于使用 gettimeofday 钳制 FPS的主要内容,如果未能解决你的问题,请参考以下文章

钳制真实(固定/浮点)值的最快方法?

如何在不产生 y 旋转抖动的情况下统一使用触摸输入来钳制游戏对象的 z 旋转

do_gettimeofday使用方法

Linux时间函数之gettimeofday()函数之使用方法

macOS 上的 gettimeofday() 是不是使用系统调用?

gettimeofday()函数的使用方法