控制OpenGL应用程序中的FPS限制

时间:2013-02-19 22:32:03

标签: opengl 3d frame-rate

我正在尝试找到一种可靠的方法,以便能够准确设置我希望我的OpenGL应用程序在屏幕上呈现多少FPS。我可以通过休眠1000 / fps毫秒在某种程度上做到这一点,但这没有考虑渲染所需的时间。 将fps限制在所需数量的最一致方法是什么?

7 个答案:

答案 0 :(得分:7)

您可以在opengl中使用wglSwapIntervalEXT同步到vblank。它不是很好的代码,但确实有用。

http://www.gamedev.net/topic/360862-wglswapintervalext/#entry3371062

bool WGLExtensionSupported(const char *extension_name) {
    PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL;

    _wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");

    if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL) {
        return false;
    }

    return true;
}

PFNWGLSWAPINTERVALEXTPROC       wglSwapIntervalEXT = NULL;
PFNWGLGETSWAPINTERVALEXTPROC    wglGetSwapIntervalEXT = NULL;

if (WGLExtensionSupported("WGL_EXT_swap_control"))
{
// Extension is supported, init pointers.
    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");

// this is another function from WGL_EXT_swap_control extension
    wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
}

答案 1 :(得分:5)

由于OpenGL只是一个低级图形API,因此您不会直接在OpenGL中找到这样的内容。

但是,我认为你的逻辑有点缺陷。而不是以下:

  1. 画框
  2. 等待1000 / fps毫秒
  3. 重复
  4. 你应该这样做:

    1. 启动计时器
    2. 画框
    3. 停止计时器
    4. 等待(1000 / fps - (停止 - 开始))毫秒
    5. 重复
    6. 这样一来,如果你只是等待你应该的数量,你应该每秒非常接近60(或任何你想要的)帧数。

答案 2 :(得分:5)

OpenGL本身没有任何允许限制帧率的功能。周期。

然而,在现代GPU上,有很多功能包括帧速率,帧预测等等。有一些John Carmack的问题,他推出了一些可用的功能。还有NVidia的自适应同步。

这对你意味着什么?把它留给GPU。假设绘图是完全不可预测的(就像你只坚持使用OpenGL时那样),自己定时事件并将逻辑更新(如物理)与绘图分开。这样,用户将能够从所有这些先进技术中受益,您不必再担心这一点。

答案 3 :(得分:2)

不要使用睡眠。如果这样做,那么应用程序的其余部分必须等待它们完成。

相反,请记录已经过了多少时间,并且仅在满足1000 / fps时才进行渲染。如果未满足计时器,请跳过它并执行其他操作。

在单线程环境中,很难确保以1000 / fps的速度绘制,除非这绝对是你唯一的事情。更通用和更健壮的方法是在一个单独的线程中完成所有渲染,并在计时器上启动/运行该线程。这是一个更复杂的问题,但会让你最接近你的要求。

此外,跟踪发布渲染所需的时间将有助于在渲染时动态调整。

static unsigned int render_time=0;
now = timegettime();
elapsed_time = last_render_time - now - render_time;
if ( elapsed_time > 1000/fps ){
    render(){
        start_render = timegettime();

        issue rendering commands...

        end_render = timegettime();
        render_time = end_render - start_render;
    }
    last_render_time = now;
}

答案 4 :(得分:1)

在绘图和调用交换缓冲区之后放置它:

//calculate time taken to render last frame (and assume the next will be similar)
thisTime = getElapsedTimeOfChoice(); //the higher resolution this is the better
deltaTime = thisTime - lastTime;
lastTime = thisTime;

//limit framerate by sleeping. a sleep call is never really that accurate
if (minFrameTime > 0)
{
    sleepTime += minFrameTime - deltaTime; //add difference to desired deltaTime
    sleepTime = max(sleepTime, 0); //negative sleeping won't make it go faster :(
    sleepFunctionOfChoice(sleepTime);
}

如果你想要60fps,minFrameTime = 1.0/60.0(假设时间以秒为单位)。

这不会给你vsync,但意味着你的应用程序不会失控,这会影响物理计算(如果它们不是固定步骤),动画等等。只记得 睡眠后输入 !我尝试过平均帧时间,但到目前为止效果最好。

对于getElapsedTimeOfChoice(),我使用的是here,即

答案 5 :(得分:0)

另一个想法是使用WaitableTimers(如果可能,例如在Windows上)

基本理念:

while (true) 
{ 
    SetWaitableTimer(myTimer, desired_frame_duration, ...); 
    PeekMsg(...) 
    if (quit....) break; 
    if (msg)
        handle message; 
    else 
    { 
        Render(); 
        SwapBuffers();
    }
    WaitForSingleObject(myTimer); 
}

更多信息:How to limit fps information

答案 6 :(得分:0)

一种简单的方法是使用GLUT。这段代码大致可以完成这项工作。

static int redisplay_interval;

void timer(int) {
    glutPostRedisplay();
    glutTimerFunc(redisplay_interval, timer, 0);
}

void setFPS(int fps)
{
    redisplay_interval = 1000 / fps;
    glutTimerFunc(redisplay_interval, timer, 0);
}