Я написал код ниже с использованием библиотеки std :: chrono c ++, то, что я пытаюсь сделать, это
исправить приложение FPS
на 60, но я получаю 50 FPS
, не проблема производительности наверняка
потому что я ничего не вычисляю. но это, безусловно, неправильное использование или ошибка.
TARGET_FPS
макрос установлен на цель FPS
что я хочу получить, то окно консоли
отображает реальный фактический FPS, следующие строки показывают значения, которые я установил TARGET_FPS
и каждый связан с финалом FPS
,
TARGET_FPS---->FPS
60----->50
90----->50
100----->100
1000----->100
10000----->100
whatever ----->100
Даже если я определю TARGET_FPS
до 1000000000 я получаю 100 FPS
, даже если я определю его как 458 или любое другое значение больше 100, я получу 100 FPS
в качестве вывода.
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#define TARGET_FPS 60// our target FPS
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
const frame_len_type target_frame_len(1); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
void app_logic(){ /** ... All application logic goes here ... **/}
int main() /// our main function !
{
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
std::cout<< fsecond(1) / ( sys_clock::now() - frame_begin) <<std::endl; /// this will show ass the current FPS
}
return 0; /// return to OS
} /// end of code
Временное разрешение std :: chrono зависит от системы:
По истечении интервала ожидания поток готов к работе. Если
Вы указываете 0 миллисекунд, поток освободит остаток
его времени, но оставаться готовым. Обратите внимание, что готовая тема не
гарантированно запустить сразу. Следовательно, поток может не работать
до истечения некоторого времени после сна.
sleep_for
независимо от того, какую ОС вы используете: 30.3.2 / 7: Эффект: блокирует вызывающий поток на относительное время ожидания (…)
Последствие:
app_logic()
очень быстро, ваш поток будет спать не менее 15,6 мс. Если для выполнения логики требуется 1 мс, вы будете точно на 60 FPS. Однако, согласно документации API, если [время ожидания] больше одного тика, но меньше двух, ожидание может находиться в диапазоне от одного до двух тиков, так что среднее время ожидания будет между 15,6 и 31,2 мс, что означает, наоборот, что ваш FPS будет между 60 и 32 FPS. Это объясняет, почему вы достигаете только 50 FPS.
Когда вы устанавливаете FPS на 100, каждые 10 мс должен быть кадр. Это ниже точности таймера. Там может быть не спать вообще. Если ни один другой поток не готов к запуску, функция немедленно вернется, так что вы получите максимальную пропускную способность. Если вы установите более высокий FPS, вы будете в точно такой же ситуации, как ожидаемое время ожидания всегда будет ниже точности таймера. Следовательно, результат не улучшится.
Задача решена 🙂
#include <chrono> /// to use std::chrono namespace
#include <iostream> /// for console output
#include <thread> /// for std::this_thread::sleep_for()
#include <windows.h>
#define TARGET_FPS 500 /// our target fps as a macro
const float target_fps = (float)TARGET_FPS; /// our target fps
float tmp_target_fps = target_fps; /// used to adjust the target fps depending on the actual real fps to reach the real target fps
using frame_len_type = std::chrono::duration<float,std::ratio<1,TARGET_FPS>>; /// this is the duration that defines the length of a frame
using fsecond = std::chrono::duration<float>; /// this duration represents once second and uses 'float' type as internal representation
fsecond target_frame_len(1.0f/tmp_target_fps); /// we will define this constant here , to represent on frame duration ( defined to avoid construction inside a loop )
bool enable_fps_oscillation = true;
void app_logic()
{
/** ... All application logic goes here ... **/
}
class HeighResolutionClockKeeper
{
private :
bool using_higher_res_timer;
public :
HeighResolutionClockKeeper() : using_higher_res_timer(false) {}
void QueryHeighResolutionClock()
{
if (timeBeginPeriod(1) != TIMERR_NOCANDO)
{
using_higher_res_timer = true;
}
}
void FreeHeighResolutionClock()
{
if (using_higher_res_timer)
{
timeEndPeriod(1);
}
}
~HeighResolutionClockKeeper()
{
FreeHeighResolutionClock(); /// if exception is thrown , if not this wont cause problems thanks to the flag we put
}
};
int main() /// our main function !
{
HeighResolutionClockKeeper MyHeighResolutionClockKeeper;
MyHeighResolutionClockKeeper.QueryHeighResolutionClock();
using sys_clock = std::chrono::system_clock; /// simplify the type name to make the code readable
sys_clock::time_point frame_begin,frame_end; /// we will use these time points to point to frame begin and end
sys_clock::time_point start_point = sys_clock::now();
float accum_fps = 0.0f;
int frames_count = 0;
while (true)
{
frame_begin = sys_clock::now(); /// there we go !
app_logic(); /// lets be logical here :)
frame_end = sys_clock::now(); /// we are done so quick !
std::this_thread::sleep_for( target_frame_len- (frame_end.time_since_epoch()-frame_begin.time_since_epoch()) ); /// we will take a rest that is equal to what we where supposed to take to finish the actual target frame length
float fps = fsecond(1) / ( sys_clock::now() - frame_begin) ; /// this will show ass the current FPS
/// obviously we will not be able to hit the exact FPS we want se we need to oscillate around until we
/// get a very close average FPS by time .
if (fps < target_fps) /// our real fps is less than what we want
tmp_target_fps += 0.01; /// lets asl for more !
else if (fps > target_fps ) /// it is more than what we want
tmp_target_fps -=0.01; /// lets ask for less
if(enable_fps_oscillation == true)
{
/// now we will adjust our target frame length for match the new target FPS
target_frame_len = fsecond(1.0f/tmp_target_fps);
/// used to calculate average FPS
accum_fps+=fps;
frames_count++;
/// each 1 second
if( (sys_clock::now()-start_point)>fsecond(1.0f)) /// show average each 1 sec
{
start_point=sys_clock::now();
std::cout<<accum_fps/frames_count<<std::endl; /// it is getting more close each time to our target FPS
}
}
else
{
/// each frame
std::cout<<fps<<std::endl;
}
}
MyHeighResolutionClockKeeper.FreeHeighResolutionClock();
return 0; /// return to OS
} /// end of code
Я должен был добавить timeBeginPeriod()
а также timeEndPeriod()
на платформе Windows, благодаря этому удивительному, потерянному на ветру веб-сайту http://www.geisswerks.com/ryan/FAQS/timing.html от Райан Гейс .
Подробности :
Поскольку мы не можем на самом деле достичь желаемого значения частоты кадров (очень немного выше или ниже, но до 1000 кадров в секунду и ниже до 1 кадра в секунду благодаря timeXPeriod (1)), поэтому я использовал некоторую дополнительную переменную дамп fps для настройки целевого числа кадров в секунду. мы увеличиваем и уменьшаем его …, что позволит нам контролировать фактический fps приложения, чтобы он достиг нашего реального целевого fps как среднего (вы можете включить и отключить это, используя флаг ‘enable_fps_oscillation’), это исправляет проблему для fps = 60, потому что мы не можем поразить его (+/- 0,5), но если мы установим fps = 500, мы поразим его, и нам не нужно будет колебаться ниже и выше