C ++: где взять ограничитель частоты кадров, который не использует v-sync? - PullRequest
3 голосов
/ 02 ноября 2010

Это кажется огромным секретом для программистов, никто не хочет делиться своими кодами для этого.Почему?

Я не могу найти работающий ограничитель FPS, который мог бы ограничить FPS по крайней мере до 60 без использования v-sync.

И я хочу, конечно, сделать это правильно.Так что я еще не сделал сам, потому что все они говорят, что им понадобился год, чтобы выучить все приемы в ограничителях fps ...

Edit: вот мой код ограничителя 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 ]

2 голосов
/ 02 ноября 2010

Установите системный таймер так, чтобы он отключался каждые 16.6666 мс и выполнял рендеринг для этого события.Еще лучше, сделайте свой рендеринг и переверните страницу по таймеру и начните рендеринг следующего кадра.Это не большой секрет, вы можете сделать это в Windows, используя таймеры высокого разрешения.

Как только вы это заработаете, вы увидите разрыв и решите подождать v-sync вместо некоторого произвольного таймера..

1 голос
/ 06 ноября 2010

Извините, если есть какая-то магия, которая делает меня неправильным, но, насколько я понимаю, VSync и слезы идут рука об руку.Независимо от того, какой у вас предел кадра, вы все равно будете разрываться, если буфер кадра перевернут в середине кадра.Это то, для чего предназначен VSync.

Что касается кода с ограничением фреймов, вот несколько приемов для проделывания времени производителем / потребителем:

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

Его причины, по-видимомусосредоточиться на физике, но это также применимо и к ограничению кадров.

1 голос
/ 06 ноября 2010

В DirectX вы можете просто передать аргумент в Present (), который будет простаивать текущий поток до тех пор, пока он не вернется, что ограничит FPS при некотором кратном частоте обновления. Я использовал его и могу сказать, что моя система достигла примерно 1% использования процессора с помощью этой системы. Ваш текущий код неверен в основном потому, что 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. Это будет спать в вашей нити, пока таймер не оповестит ее.

...