首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在我的OpenGL应用程序中实现精确的(但可变的) FPS限制/上限?

如何在我的OpenGL应用程序中实现精确的(但可变的) FPS限制/上限?
EN

Stack Overflow用户
提问于 2011-07-15 13:11:07
回答 6查看 8.7K关注 0票数 11

我目前正在开发一个OpenGL应用程序来向用户显示几个3D球,他们可以旋转、移动等等。也就是说,这里没有太多的复杂性,所以这个应用程序的帧率很高(~500FPS)。

显然,这是过火了--甚至120也足够了,但我的问题是,在全速运行应用程序会占用我的CPU,导致过热、耗电量等。我想做的是让用户设置一个FPS上限,这样在不需要的时候CPU就不会被过度使用。

我正在使用freeglut和C++,并且已经将动画/事件处理设置为使用计时器(使用glutTimerFunc)。然而,glutTimerFunc只允许设置一个毫秒的整数值--所以如果我想要120FPS,我能得到的最接近的是(int)1000/120 = 8 ms分辨率,它等于125个FPS (我知道这是一个可读的值,但我仍然想要设置一个FPS限制,如果我知道系统可以更快地渲染,我就会得到这个FPS )。

此外,使用glutTimerFunc来限制FPS从来都不是一致的。假设我把我的申请限制在100 FPS,它通常永远不会比90-95 FPS高。再次,我试图计算出呈现/计算之间的时间差,但是它总是超出限制5-10 FPS (可能的计时器分辨率)。

我想这里最好的比较是一场游戏(例如,半衰期2) --你设定了你的FPS上限,它总是达到这个精确的数量。我知道我可以在渲染每个帧之前和之后测量时间增量,然后循环直到我需要绘制下一个帧,但这不能解决我的100% CPU使用问题,也不能解决时间分辨率问题。

有没有办法在我的应用程序中实现一个有效的、跨平台的、可变的帧速率限制器/上限?或者,以另一种方式,是否有实现高分辨率定时器和睡眠功能的跨平台(开放源码)库?

编辑:我希望找到一个不依赖于最终用户启用VSync的解决方案,因为我将让他们指定FPS上限。

编辑2:对于所有推荐SDL的人(我最终将应用程序移植到SDL的),使用glutTimerFunc函数触发抽签还是使用SDL_Delay在两次绘制之间等待有什么区别吗?每个文档都提到了相同的警告,但我不确定其中一个是否比另一个更有效率。

编辑3:基本上,我试图弄清楚是否有一种(简单的方法)在我的应用程序中实现一个精确的FPS限制器(同样,类似半衰期2)。如果这是不可能的,我将很可能切换到SDL (对于我来说,使用延迟函数比使用glutTimerFunc每毫秒一次调用呈现函数更有意义)。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2013-01-01 21:13:42

我建议使用子毫秒精度系统定时器(QueryPerformanceCounter,gettimeofday)来获取定时数据。这些也可以帮助您分析优化版本构建中的性能。

票数 0
EN

Stack Overflow用户

发布于 2011-07-15 13:31:59

我建议你使用SDL。我个人用它来管理我的计时器。此外,它还可以使用SDL1.3将fps限制在屏幕刷新速率(V)上。这使您能够限制CPU的使用,同时拥有最好的屏幕性能(即使您有更多的帧,它们也无法显示,因为屏幕刷新不够快)。

功能是

SDL_GL_SetSwapInterval(1);

如果您想要一些使用SDL的定时器代码,您可以在这里看到:

我的计时器课

祝你好运:)

票数 4
EN

Stack Overflow用户

发布于 2016-09-22 15:04:26

我认为实现这个目标的一个好方法,无论你使用什么样的图形库,都是在游戏中有一个时钟测量,以考虑到每一个滴答(ms)。这样,平均fps就会和半衰期2中的限制完全一样。希望下面的代码片段能够解释我说的内容:

代码语言:javascript
运行
复制
//FPS limit
unsigned int FPS = 120;

//double holding clocktime on last measurement
double clock = 0;

while (cont) {
    //double holding difference between clocktimes
    double deltaticks;

    //double holding the clocktime in this new frame
    double newclock;

    //do stuff, update stuff, render stuff...

    //measure clocktime of this frame
    //this function can be replaced by any function returning the time in ms
    //for example clock() from <time.h>
    newclock = SDL_GetTicks();

    //calculate clockticks missing until the next loop should be
    //done to achieve an avg framerate of FPS 
    // 1000 / 120 makes 8.333... ticks per frame
    deltaticks = 1000 / FPS - (newclock - clock);

    /* if there is an integral number of ticks missing then wait the
    remaining time
    SDL_Delay takes an integer of ms to delay the program like most delay
    functions do and can be replaced by any delay function */
    if (floor(deltaticks) > 0)
        SDL_Delay(deltaticks);

    //the clock measurement is now shifted forward in time by the amount
    //SDL_Delay waited and the fractional part that was not considered yet
    //aka deltaticks
    the fractional part is considered in the next frame
    if (deltaticks < -30) {
        /*dont try to compensate more than 30ms(a few frames) behind the
        framerate
        //when the limit is higher than the possible avg fps deltaticks
        would keep sinking without this 30ms limitation
        this ensures the fps even if the real possible fps is
        macroscopically inconsitent.*/
        clock = newclock - 30;
    } else {
        clock = newclock + deltaticks;
    }

    /* deltaticks can be negative when a frame took longer than it should
    have or the measured time the frame took was zero
    the next frame then won't be delayed by so long to compensate for the
    previous frame taking longer. */


    //do some more stuff, swap buffers for example:
    SDL_RendererPresent(renderer); //this is SDLs swap buffers function
}

我希望这个带有SDL的例子有所帮助。每帧只测量一次时间是很重要的,因此每一帧都被考虑在内。我建议在一个使您的代码更清晰的函数中模块化这个定时。这段代码在最后一段中没有注释,它们只是让您感到厌烦:

代码语言:javascript
运行
复制
unsigned int FPS = 120;

void renderPresent(SDL_Renderer * renderer) {
    static double clock = 0;
    double deltaticks;
    double newclock = SDL_GetTicks();

    deltaticks = 1000.0 / FPS - (newclock - clock);

    if (floor(deltaticks) > 0)
        SDL_Delay(deltaticks);

    if (deltaticks < -30) {
        clock = newclock - 30;
    } else {
        clock = newclock + deltaticks;
    }

    SDL_RenderPresent(renderer);
}

现在,您可以在主循环中调用此函数,而不是调用swapBuffer函数(SDL_RenderPresent(renderer)在SDL中)。在SDL中,您必须确保关闭SDL_RENDERER_PRESENTVSYNC标志。这个函数依赖于全局变量FPS,但是您可以考虑其他存储方法。我只是把整件事放在我图书馆的命名空间里。

如果由于对deltaticks的30 to限制,在多个帧的循环时间上没有很大的差异,这种限制框架的方法提供了理想的平均帧。deltaticks限制是必需的。当FPS限制高于实际框架时,deltaticks将无限期地下降。另外,当框架再次超过FPS限制时,代码将尝试通过呈现每一个帧来补偿丢失的时间,从而立即生成一个巨大的框架,直到deltaticks恢复到零。您可以修改30毫秒,以满足您的需要,这只是一个估计由我。我用Fraps做了几个基准测试。它与每一个可想象的框架真的工作,并提供美丽的结果,从我已经测试过。

我必须承认,我昨天才对它进行了编码,所以它不太可能有某种bug。我知道这个问题是五年前提出的,但给出的答案并没有使我感到安心。也可以随意编辑这篇文章,因为这是我的第一篇文章,而且可能有缺陷。

编辑:据我所知,SDL_Delay在某些系统上非常不准确。我听说了一个案子,它在android系统上耽搁了太多时间。这意味着我的代码可能并不能移植到您想要的所有系统。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6707475

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档