Как оптимизировать алгоритм ограничения fps в программе, использующей glfw для рендеринга окна?

Всем привет.

Я создаю программу с графическим интерфейсом, в рендере главного окна которой будет помогать glfw. Сейчас мне нужно разработать участок кода, который будет задерживать выполнение потока в соответствии с fps. Я уже создал три реализации, но ни одна из них меня не устраивает:

 
//объявлены в области видимости старше:
//bool vsync = false;
//unsigned int fps = 60;
//float max_frame_duration = 1.0f / (float)fps;
//float delta_time = 0.0f;
//float last_frame = 0.0f;

float current_frame = (float)glfwGetTime();
delta_time = current_frame - last_frame;

//#1 Задержка реализована многократным выполнением цикла. Грузит ЦП. Погрешность < 1 миллисекунды
if (!vsync)
{
    while (delta_time < max_frame_duration) 
    {
        current_frame = (float)glfwGetTime();
        delta_time = current_frame - last_frame;
    }
}

//#2 Задержка реализована функцией из windows.h. Погрешность 3-11 миллисекунд. Критично для большого fps
if (!vsync)
{
    if (delta_time < max_frame_duration) 
    {
        Sleep((max_frame_duration - delta_time) * 1000);
        current_frame = (float)glfwGetTime();
        delta_time = current_frame - last_frame;
    }
}

//#3 Задержка реализована функцией из thread. Погрешность 3-12 миллисекунд. Критично для большого fps
if (!vsync)
{
    if (delta_time < max_frame_duration)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(long long((max_frame_duration - delta_time) * 1000)));
        current_frame = (float)glfwGetTime();
        delta_time = current_frame - last_frame;
    }
}

last_frame = current_frame;

Как мне сделать эту часть программы наиболее эффективной (С погрешностью не более миллисекунды и не загружающей ЦП)?

Код немного изменён, для краткости. Но, если есть необходимость, вот ссылка на полный вариант файла с ним: https://drive.google.com/drive/folders/1yXazuY0fCI3buxBt5VxxpYZ1jE4fwv2W?usp=sharing

P.S. Впервые задаю вопрос в каком-то сообществе. Если что, поправьте.


Ответы (1 шт):

Автор решения: Eugene X

Считаю считать код дельта без учёта времени выполнения отрисовки совершенно неточночным способом, поэтому все твои тесты и провалились.

В любом случае нету такой прям магической функции которая бы считала идеально.

Единственное что могу посоветывать это.

  1. Запомнить perf counter... NtQueryPerformanceCounter
  2. Выполнить код фрейма (отрисовка и.т.д.)
  3. Выполнить повторно NtQueryPerformanceCounter
  4. Расчитать оставшееся время задержки след фрейма
  5. Создать задержку через NtDelayExecution

Это всё функии ядра ОС и они будут давать максимально близкий, но не идеальный результат.

Ps: Полезная информация

Pss: Использование функций ядра и идеальной точности не всегда помогают играм. Люди любят старые игры и если думать в первую очередь не о супрер-точности, а об общей работоспособности возможно твоя игра будет работать на Windows XX спустя 10-15 лет. Для примера Lines98 она как работала на 98 так-же работает и на 10 и на 11, чему я очень рад.

→ Ссылка