开发者

C++: Where to get frame rate limiter that doesn't use v-sync?

开发者 https://www.devze.com 2023-01-23 17:17 出处:网络
This seems like a huge secret among programmers, nobody wants to share their codes for this. Why? I can\'t find a working FPS limiter that could limit FPS at least to 60 without using v-sync.

This seems like a huge secret among programmers, nobody wants to share their codes for this. Why?

I can't find a working FPS limiter that could limit FPS at least to 60 without using v-sync.

And I want, of course, do it the right way. So i haven't made own yet, because they all say it took them a year to learn all the tricks in fps limiters...

Edit: here is my fps limiter code which isnt perfect, but its best i could do, it still tears though:

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(开发者_C百科)-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: heres my second try, using waitable timers as suggested:

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();

Works better i think. But still tearing... Notice that i added the while loop there because it was tearing more than without it.

--

Another question: is this initialization safe?:

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;
}

I am afraid the last 2 checks could fail even if the waitable timer is supported... so far it hasnt failed. Is this the right approach of checking for its support?


Set a system timer to go off every 16.6666ms and do your rendering on that event. Better yet, do your rendering and page flip on the timer and start rendering the next frame. This is not a big secret, you can do it in windows using the high-resolution timers.

Once you get this working, you'll see tearing and decide to wait for v-sync instead of some arbitrary timer.


In DirectX, you can simply pass an argument to Present() that will idle the current thread until it returns that will cap the FPS at some multiple of the refresh rate. I've used it and can say that my system achieved about 1% CPU use by using this system. Your current code is wrong basically because Sleep is the least reliable function, ever, in short, and you should virtually never use it with a non-zero argument.

You could try using QueryPerformanceCounter along with 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);
}

The best thing to do is still to use a waitable timer - http://msdn.microsoft.com/en-us/library/ms682492(v=VS.85).aspx. This will sleep your thread until the timer alerts it.


Pardon me if there is some magic that makes me wrong, but as far as I understand, VSync and tearing go hand-in-hand. No matter what your frame limit is, you will still get tearing if the frame buffer gets flipped in the middle of a frame. This is what VSync is for.

As for frame limiting code, here are some techniques for doing producer/consumer time stepping:

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

His reasons seem to focus on physics, but it is perfectly applicable to frame limiting, as well.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号