C ++:从哪里得到不使用v-sync的帧率限制器?

时间:2010-11-02 12:26:48

标签: c++ windows opengl frame-rate

这似乎是程序员之间的一个秘密,没有人愿意为此分享他们的代码。为什么呢?

我找不到可以在不使用v-sync的情况下将FPS限制在至少60的FPS限制器。

我当然希望以正确的方式做到这一点。所以我还没有自己做,因为他们都说他们花了一年时间学习fps限制器的所有技巧......

编辑:这里是我的fps限制器代码,这不是完美的,但我能做的最好,它仍然流下眼泪:

timeBeginPeriod(1);

frame_start_time = TimerGetTime();

while(!done){
    if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
        if(msg.message == WM_QUIT){
            done = 1;
        }else{
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }else if(active){ 
        draw_everything();
        SwapBuffers(hDC);

        // fps limiter:
        one_frame_limit = 1000.0f/(float)framerate_limit; // 16.666 ms
        while((time_left = one_frame_limit-(TimerGetTime()-frame_start_time)) > 0){
            if(time_left >= 6.0f){
                Sleep((int)(time_left/6.0f));
            }else{
                Sleep(0); // sleep less than 1ms
            }
        }
        frame_start_time = TimerGetTime();
    }
}

EDIT2:继承我的第二次尝试,使用了建议的等待计时器:

float one_frame_limit = 1000.0f/(float)framerate_limit;
float time_left = one_frame_limit-(TimerGetTime()-frame_start_time); // 4.7432ms
liDueTime.QuadPart = -(LONGLONG)(time_left*10000.0f);
if(SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)){
    WaitForSingleObject(hTimer, INFINITE);
}
while((time_left = one_frame_limit-(TimerGetTime()-frame_start_time)) > 0.003f){
    Sleep(0);
}
frame_start_time = TimerGetTime();

我觉得效果更好。但仍然撕裂......请注意,我在那里添加了while循环,因为它比没有它时更容易撕裂。

-

另一个问题:这个初始化安全吗?:

HANDLE hTimer = NULL;
LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -100000LL; // testing timer: wait for 10ms

hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
if(hTimer == NULL){
    waitable_timer_supported = 0;
}else if(!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0)){
    waitable_timer_supported = 0;
}else if(WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0){
    waitable_timer_supported = 0;
}

即使支持等待计时器,我担心最后2次检查可能会失败...到目前为止它还没有失败。这是检查其支持的正确方法吗?

3 个答案:

答案 0 :(得分:2)

将系统计时器设置为每16.6666ms关闭一次并对该事件进行渲染。更好的是,在定时器上进行渲染和翻页并开始渲染下一帧。这不是一个大秘密,你可以使用高分辨率计时器在Windows中进行。

一旦你开始工作,你会看到撕裂并决定等待v-sync而不是任意计时器。

答案 1 :(得分:1)

在DirectX中,您可以简单地将一个参数传递给Present(),该参数将使当前线程空闲,直到它返回,这将使FPS达到刷新率的某个倍数。我已经使用过它,并且可以说我的系统通过使用这个系统实现了大约1%的CPU使用率。您当前的代码基本上是错误的,因为Sleep是最不可靠的函数,简而言之,您几乎不应该使用非零参数。

您可以尝试使用QueryPerformanceCounter和Sleep()。

LARGE_INTEGER freq, begin, end;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&begin);
QueryPerformanceCounter(&end);
const unsigned __int64 waitinticks = static_cast<unsigned __int64>(static_cast<double>(1000)/60));
while(end.QuadPart - begin.QuadPart < waitinticks) {
    Sleep(0); // If someone else wants to run, let them.
    QueryPerformanceCounter(&end);
}

最好的办法仍然是使用等待计时器 - http://msdn.microsoft.com/en-us/library/ms682492(v=VS.85).aspx。这将使你的线程休眠,直到计时器提醒它。

答案 2 :(得分:1)

请原谅我,如果有一些魔法让我错了,但就我所知,VSync和撕裂是相辅相成的。无论你的帧限制是什么,如果帧缓冲区在帧的中间翻转,你仍然会撕裂。这就是VSync的用途。

对于帧限制代码,这里有一些用于进行生产者/消费者时间步进的技术:

http://gafferongames.com/game-physics/fix-your-timestep/

他的理由似乎集中在物理学上,但它也非常适用于帧限制。