2017-01-19 17 views
0

В настоящее время я делаю игру, в которой я хотел бы ограничить количество кадров в секунду, но у меня проблемы с этим. Вот что я делаю:Ограничение FPS в C++

Я получаю DeltaTime с помощью этого метода, который выполняется каждый кадр:

void Time::calc_deltaTime() { 
    double currentFrame = glfwGetTime(); 
    deltaTime = currentFrame - lastFrame; 
    lastFrame = currentFrame; 
} 

DeltaTime оказывает значение, я бы ожидать (около 0,012 .... 0,016 ...)

и чем я использую DeltaTime задержать кадр с помощью функции сна окна, как это:

void Time::limitToMAXFPS() { 

    if(1.0/MAXFPS > deltaTime) 
     Sleep((1.0/MAXFPS - deltaTime) * 1000.0); 
} 

MAXFPS равно 60, и я умножив на 1000, чтобы преобразовать SECO nds до миллисекунд. Хотя все, кажется, правильно я подоконник, имеющий более чем 60 кадров в секунду (я получаю около 72 кадров в секунду)

Я также попробовал этот метод, используя в то время как цикл:

void Time::limitToMAXFPS() { 

    double diff = 1.0/MAXFPS - deltaTime; 

    if(diff > 0) { 

     double t = glfwGetTime(); 

     while(glfwGetTime() - t < diff) { } 

    } 

} 

Но до сих пор я получаю больше 60 кадров в секунду, я все еще получаю около 72 кадров в секунду. Я делаю что-то неправильно или есть лучший способ сделать это?

+2

Как вы рассчитываете текущие fps? – SingerOfTheFall

+0

Я бы лично написал выражение 'Sleep' следующим образом:' Sleep (1000/MAXFPS - deltaTime * 1000) ', но это не должно вести себя по-другому. –

+0

@ AlgirdasPreidžius: Что, если 'MAXFPS' является целым типом? – IInspectable

ответ

-1

Вы уверены, что ваша функция сна принимает значения с плавающей запятой. Если он принимает только int, ваш сон будет сна (0), который объяснит вашу проблему.

+3

Если я правильно вычислил без ручки и бумаги, он вызовет Сон с аргументом около 20-40, а не 0. –

+0

@doron Да, это проблема, я отдал Sleep double, ожидая, что DWORD будет unsigned int. Я передаю его в DWORD, как это (DWORD) ((1.0/MAXFPS - deltaTime) * 1000.0) теперь я получаю около 64 кадров в секунду. Я думаю, что Свен Нильссон прав, мне нужно искать альтернативу Сну. –

+0

@MagnoSilva: Обычный подход будет использовать опрос таймеров высокого разрешения и ожидание до тех пор, пока не пройдет необходимое количество времени. В целом «спящий» процесс (т. Е. Приводящий циклы процессора к остальной части системы) приведет к довольно грубному временному разрешению. На современных настольных системах таймер планировщика работает от 250 Гц до 1000 Гц, поэтому эффективная точность синхронизации составляет от 1 мс до 5 мс. Добавьте к этому, что ваш процесс не будет перенесен немедленно, так что вы можете рассчитывать на гранулярность планирования около 10 мс. – datenwolf

3

Сон может быть очень неточным. Общим явлением является то, что фактическое время спал имеет разрешение 14-15 миллисекунд, что дает вам частоту кадров ~ 70.

Is Sleep() inaccurate?

+0

Это не должно повлиять на его второе (spinloop) решение. – ltjax

1

Я отказался пытаться ограничить фпс, как это ... Как вы сказали, Windows, очень непоследовательно с Sleep. Среднее значение fps всегда составляет 64 кадра в секунду, а не 60. Проблема заключается в том, что Sleep принимает в качестве аргумента целое число (или длинное целое число), поэтому я использовал его static_cast. Но мне нужно перейти к нему как к двойному. 16 миллисекунд каждый кадр отличается от 16.6666 ... Это, вероятно, причина этого дополнительного 4 fps (так я думаю).

Я также попытался:

std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<long>(1.0/MAXFPS - deltaTime) * 1000.0)));

и то же самое происходит и с sleep_for. Затем я попытался передать десятичное значение, оставшееся от миллисекунд, до chrono::microseconds и chrono::nanoseconds, используя их вместе, чтобы получить лучшую точность, но угадайте, что я до сих пор получаю freaking 64 fps.

Еще одна странная вещь в выражении (1.0/MAXFPS - deltaTime) * 1000.0) иногда (да, это совершенно случайно), когда я меняю 1000.0 на целое число const, создавая выражение (1.0/MAXFPS - deltaTime) * 1000). Моя fps просто прыгает до 74 по какой-то причине, в то время как выражение полностью равно друг другу, и ничего не должно произойти. Оба они являются двойными выражениями, я не думаю, что здесь происходит какое-либо продвижение по типу.

Итак, я решил принудительно выполнить V-синхронизацию через функцию wglSwapIntervalEXT (1); чтобы избежать разрыва экрана. И тогда я буду использовать этот метод умножения deltaTime с каждым значением, которое может очень сильно зависело от скорости компьютера, выполняющего мою игру. Это будет боль, потому что я могу забыть умножить какую-то ценность и не заметить ее на своем собственном компьютере, создавая непоследовательность, но я не вижу другого способа ... Спасибо всем за помощь.

2

Насколько важно вернуть циклы обратно в CPU? Мне кажется, что плохая идея вообще использовать сон. Кто-то, пожалуйста, поправьте меня, если я ошибаюсь, но я думаю, что функции сна следует избегать.

Почему бы просто не использовать бесконечный цикл, который выполняется, если прошло более определенного интервала времени. Попробуйте:

const double maxFPS = 60.0; 
const double maxPeriod = 1.0/maxFPS; 

// approx ~ 16.666 ms 

bool running = true; 
double lastTime = 0.0; 

while(running) { 
    double time = glfwGetTime(); 
    double deltaTime = time - lastTime; 

    if(deltaTime >= maxPeriod) { 
     lastTime = time; 
     // code here gets called with max FPS 
    } 
} 

Последний раз, когда я использовал GLFW, казалось самоограничивать до 60 кадров в секунду в любом случае. Если вы делаете что-то ориентированное на высокую производительность (игровая или трехмерная графика), избегайте всего, что спит, если вы не хотите использовать многопоточность.

+0

спасибо, я посмотрю, если это сработает. –

0

Я недавно начал использовать glfw для небольшого побочного проекта я работаю, и я использовать std::chrono вдоль стороны std::this_thread::sleep_until для достижения 60fps

auto start = std::chrono::steady_clock::now(); 
while(!glfwWindowShouldClose(window)) 
{ 
    ++frames; 
    auto now = std::chrono::steady_clock::now(); 
    auto diff = now - start; 
    auto end = now + std::chrono::milliseconds(16); 
    if(diff >= std::chrono::seconds(1)) 
    { 
     start = now; 
     std::cout << "FPS: " << frames << std::endl; 
     frames = 0; 

    } 
    glfwPollEvents(); 

    processTransition(countit); 
    render.TickTok(); 
    render.RenderBackground(); 
    render.RenderCovers(countit); 

    std::this_thread::sleep_until(end); 
    glfwSwapBuffers(window); 
} 

добавить вы можете легко настроить FPS предпочтение по регулировка end. теперь с этим сказал, я знаю, что glfw был ограничен 60 кадрами в секунду, но мне пришлось отключить предел с помощью glfwSwapInterval(0); непосредственно перед циклом while.