Not sure my delta time function in C++ has the right logic - c++

I've been working on making a basic game loop in C++ and I'm unsure my logic is completely right as my delta time isn't what I expect. I expect to get 1/60 seconds per frame as my cap is 60fps, but over the average I take I get a delta time of ~0.03 seconds per frame giving me 33fps. I know the program can run faster than this as if I raise the frame cap it has a smaller delta time, which is still inaccurate. Any help? (I've removed unimportant bits of code to focus on the logic)
using namespace std::chrono;
int main(void)
{
//Init time - start and end, with delta time being the time between each run of the loop
system_clock::time_point startTime = system_clock::now();
system_clock::time_point endTime = system_clock::now();
float deltaTime = 0.0f;
//*Game made here*
/* Loop until the user closes the window */
while (window is not closed)
{
//Takes time at start of loop
startTime = system_clock::now();
deltaFrameCount++;
//Handle game processes
//Get time at end of game processes
endTime = system_clock::now();
//Take time during work period
duration<double, std::milli> workTime = endTime - startTime;
//Check if program took less time to work than the cap
if (workTime.count() < (milliseconds per frame cap))
{
//Works out time to sleep for by casting to double
duration<double, std::milli> sleepDurationMS((milliseconds per frame cap) - workTime.count());
//Casts back to chrono type to get sleep time
auto sleepDuration = duration_cast<milliseconds>(sleepDurationMS);
//Sleeps this thread for calculated duration
std::this_thread::sleep_for(milliseconds(sleepDuration.count()));
}
//get time at end of all processes - time for one whole cycle
endTime = system_clock::now();
duration<double, std::milli> totalTime = endTime - startTime;
deltaTime = (totalTime / 1000.0f).count();
}
//cleans game
return 0;
}

Related

Game time speeds up the slower the frame rate, delta time inconsistent across different framerates

Here is how I am calculating the delta time:
currentTime = std::chrono::high_resolution_clock::now();
deltaTime = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - lastCurrentTime).count();
lastCurrentTime = currentTime;
From the very beginning of this project I noticed the delta time did not give me consistent results across different frame rates. Everything that uses this deltaTime speeds up the slower the framerate is, but as an example, here is how the camera moves forward:
playerPosition += playerForward * speed * deltaTime;
I could show you the entire main loop, but that shouldn't matter, however long it takes should be accounted for. I remember one time I had a problem with std::chrono and it was because the compiler "optimized" the start timer to a completely different place. Could that be happening here? And if so, is there a better way of calculating delta time in C++?
Don't use std::chrono::high_resolution_clock, it is implementation-defined. Stick to std::chrono::steady_clock instead. Additionally, your problem is here: std::chrono::duration<float, std::chrono::seconds::period>(currentTime - lastCurrentTime).count();. You are defining the period as a variable-length instead of a constant length which will cause your deltaTime to change "length" between calls.
You can simplify your problem by defining a using for a float-based seconds:
namespace TimeUtils {
using FPSeconds = std::chrono::duration<float>;
using FPMilliseconds = std::chrono::duration<float, std::milli>;
using FPMicroseconds = std::chrono::duration<float, std::micro>;
using FPNanoseconds = std::chrono::duration<float, std::nano>;
using FPFrames = std::chrono::duration<float, std::ratio<1, 60>>;
using Frames = std::chrono::duration<uint64_t, std::ratio<1, 60>>;
template<typename Clock = std::chrono::steady_clock>
[[nodiscard]] decltype(auto) Now() noexcept {
return Clock::now();
}
std::chrono::nanoseconds GetCurrentTimeElapsed() noexcept {
static auto initial_now = Now<std::chrono::steady_clock>();
auto now = Now<std::chrono::steady_clock>();
return (now - initial_now);
}
//...
} //end TimeUtils
static TimeUtils::FPSeconds previousFrameTime = TimeUtils::GetCurrentTimeElapsed();
TimeUtils::FPSeconds currentFrameTime = TimeUtils::GetCurrentTimeElapsed();
TimeUtils::FPSeconds deltaSeconds = (currentFrameTime - previousFrameTime);
previousFrameTime = currentFrameTime;
//...Prevents death-spiral when stepping through debugger
#ifdef DEBUG_BUILD
deltaSeconds = (std::min)(TimeUtils::FPSeconds{TimeUtils::FPFrames{1}}, deltaSeconds);
#endif
//...
Update(deltaSeconds);
Now you can declare your deltaTime as TimeUtils::FPSeconds deltaTime in function arguments. This allows you to express it in any std::chrono::duration format you want as it will be converted automagically:
void Foo(TimeUtils::FPSeconds deltaSeconds);
Foo(TimeUtils::FPMilliseconds{10.0f}); //Automatically converted to 0.010f seconds

Why does this elapsed time (frametime) calculation lockup my game

currently I am trying to implement a fixed-step game loop but somehow my code seems to lockup my game.
Uint32 SDL_GetTicks(void) : Returns an unsigned 32-bit value representing the number of milliseconds since the SDL library initialized.
This works:
1.cc)
Uint32 FPS = 60;
Uint32 MS_PER_SEC = 1000 / FPS;
Uint32 current_time, last_time, elapsed_time;
current_time = last_time = elapsed_time = 0;
while(Platform.Poll())
{
current_time = SDL_GetTicks(); // Get time of frame begin
// Clear Window
Renderer.Clear();
// Update Input
//...
// Draw
Renderer.Draw();
// Update Window
Renderer.Update();
last_time = SDL_GetTicks(); // Get time of frame end
elapsed_time = last_time - current_time; // calculate frametime
SDL_Delay(MS_PER_SEC - elapsed_time);
}
However this does not:
2.cc)
Uint32 FPS = 60;
Uint32 MS_PER_SEC = 1000 / FPS;
Uint32 current_time, last_time, elapsed_time;
current_time = elapsed_time = 0;
last_time = SDL_GetTicks();
// Poll for Input
while(Platform.Poll())
{
current_time = SDL_GetTicks();
elapsed_time = current_time - last_time;
// Clear Window
Renderer.Clear();
// Update Input
//...
// Draw
Renderer.Draw();
// Update Window
Renderer.Update();
last_time = current_time;
SDL_Delay(MS_PER_SEC - elapsed_time);
}
I expect the results of 1.cc and 2.cc to be the same, meaning that SDL_Delay(MS_PER_SEC - elapsed_time) does delay by a fixed time minus frametime (here 16 - frametime).
But 2.cc does lockup my game.
Is not the elapsed_time (frametime) calculation from 2.cc equivalent to 1.cc ?
Let's unroll the loop a little...
// First iteration
last_time = SDL_GetTicks();
current_time = SDL_GetTicks();
elapsed_time = current_time - last_time; // Probably zero
...
SDL_Delay(MS_PER_SEC - elapsed_time); // Probably a whole second
// Second iteration
current_time = SDL_GetTicks();
elapsed_time = current_time - last_time; // Probably slightly more than a whole second
...
SDL_Delay(MS_PER_SEC - elapsed_time); // Probably negative (4 billion milliseconds)
That function is based on RTC and have very low accuracy and and uses much time to use, depending on platform. 10-30 milliseocnds accuracy is an optimistic guess.
This might be related: SDL_GetTicks() accuracy below the millisecond level

time based movement sliding object

At the moment i have a function that moves my object based on FPS, if the frames have not passed it wont do anything.
It works fine if the computer can run it at that speed.
How would i use time based and move it based on the time?
Here is my code:
typedef unsigned __int64 u64;
auto toolbarGL::Slide() -> void
{
LARGE_INTEGER li = {};
QueryPerformanceFrequency(&li);
u64 freq = static_cast<u64>(li.QuadPart); // clock ticks per second
u64 period = 60; // fps
u64 delay = freq / period; // clock ticks between frame paints
u64 start = 0, now = 0;
QueryPerformanceCounter(&li);
start = static_cast<u64>(li.QuadPart);
while (true)
{
// Waits to be ready to slide
// Keeps looping till stopped then starts to wait again
SlideEvent.wait();
QueryPerformanceCounter(&li);
now = static_cast<u64>(li.QuadPart);
if (now - start >= delay)
{
if (slideDir == SlideFlag::Right)
{
if (this->x < 0)
{
this->x += 5;
this->controller->Paint();
}
else
SlideEvent.stop();
}
else if (slideDir == SlideFlag::Left)
{
if (this->x > -90)
{
this->x -= 5;
this->controller->Paint();
}
else
SlideEvent.stop();
}
else
SlideEvent.stop();
start = now;
}
}
}
You can update your objects by time difference. We need to have start timestamp and then count difference on each iteration of global loop. So global loop is very important too, it has to work all the time. My example shows just call update method for your objects. All your objects should depend on time not FPS. Fps shows different behavior on different computers and even same computer can show different fps because of others processes running in background.
#include <iostream>
#include <chrono>
#include <unistd.h>
//Function to update all objects
void Update( float dt )
{
//For example
//for( auto Object : VectorObjects )
//{
// Object->Update(dt);
//}
}
int main()
{
typedef std::chrono::duration<float> FloatSeconds;
auto OldMs = std::chrono::system_clock::now().time_since_epoch();
const uint32_t SleepMicroseconds = 100;
//Global loop
while (true)
{
auto CurMs = std::chrono::system_clock::now().time_since_epoch();
auto DeltaMs = CurMs - OldMs;
OldMs = CurMs;
//Cast delta time to float seconds
auto DeltaFloat = std::chrono::duration_cast<FloatSeconds>(DeltaMs);
std::cout << "Seconds passed since last update: " << DeltaFloat.count() << " seconds" << std::endl;
//Update all object by time as float value.
Update( DeltaFloat.count() );
// Sleep to give time for system interaction
usleep(SleepMicroseconds);
// Any other actions to calculate can be here
//...
}
return 0;
}
For this example in console you can see something like this:
Seconds passed since last update: 0.002685 seconds
Seconds passed since last update: 0.002711 seconds
Seconds passed since last update: 0.002619 seconds
Seconds passed since last update: 0.00253 seconds
Seconds passed since last update: 0.002509 seconds
Seconds passed since last update: 0.002757 seconds
Your time base logic seems to be incorrect, here's a sample code snippet. The speed of the object should be same irrespective of speed of the system. Instead of QueryPerformanceFrequency which is platform dependent, use std::chrono.
void animate(bool& stop)
{
static float speed = 1080/5; // = 1080px/ 5sec = 5sec to cross screen
static std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
float fps;
int object_x = 1080;
while(!stop)
{
//calculate factional time
auto now = std::chrono::system_clock::now();
auto diff = now - start;
auto lapse_milli = std::chrono::duration_cast<std::chrono::milliseconds>(diff);
auto lapse_sec = lapse_milli.count()/1000;
//apply to object
int incr_x = speed * lapse_sec ;
object_x -= incr_x;
if( object_x <0) object_x = 1080;
// render object here
fps = lapse_milli.count()/1000;
//print fps
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // change to achieve a desired fps rate
start = now;
}
}

Calculating glut framerate using clocks_per_sec much too slow

I'm trying to calculate the framerate of a GLUT window by calling a custom CalculateFrameRate method I made at the beginning of my Display() callback function. I call glutPostRedisplay() after calculations I perform every frame so Display() gets called for every frame.
I also have an int numFrames that increments every frame (every time glutPostRedisplay gets called) and I print that out as well. My CalculateFrameRate method calculates a rate of about 7 fps but if I look at a stopwatch and compare it to how quickly my numFrames incrementor increases, the framerate is easily 25-30 fps.
I can't seem to figure out why there is such a discrepancy. I've posted my CalcuateFrameRate method below
clock_t lastTime;
int numFrames;
//GLUT Setup callback
void Renderer::Setup()
{
numFrames = 0;
lastTime = clock();
}
//Called in Display() callback every time I call glutPostRedisplay()
void CalculateFrameRate()
{
clock_t currentTime = clock();
double diff = currentTime - lastTime;
double seconds = diff / CLOCKS_PER_SEC;
double frameRate = 1.0 / seconds;
std::cout<<"FRAMERATE: "<<frameRate<<endl;
numFrames ++;
std::cout<<"NUM FRAMES: "<<numFrames<<endl;
lastTime = currentTime;
}
The function clock (except in Windows) gives you the CPU-time uses, so if you are not spinning the CPU for the entire frame-time, then it will give you a lower time than expected. Conversely, if you have 16 cores running 16 of your threads flat out, the time reported by clock will be 16 times the actual time.
You can use std::chrono::steady_clock, std::chrono::high_resolution_clock, or if you are using Linux/Unix, gettimeofday (which gives you microosecond resolution).
Here's a couple of snippets of how to use gettimeofday to measure milliseconds:
double time_to_double(timeval *t)
{
return (t->tv_sec + (t->tv_usec/1000000.0)) * 1000.0;
}
double time_diff(timeval *t1, timeval *t2)
{
return time_to_double(t2) - time_to_double(t1);
}
gettimeofday(&t1, NULL);
... do stuff ...
gettimeofday(&t2, NULL);
cout << "Time taken: " << time_diff(&t1, &t2) << "ms" << endl;
Here's a piece of code to show how to use std::chrono::high_resolution_clock:
auto start = std::chrono::high_resolution_clock::now();
... stuff goes here ...
auto diff = std::chrono::high_resolution_clock::now() - start;
auto t1 = std::chrono::duration_cast<std::chrono::nanoseconds>(diff);

Design fps limiter

I try to cap the animation at 30 fps. So I design the functions below to achieve the goal. Unfortunately, the animation doesn't behave as fast as no condition checking for setFPSLimit() function when I set 60 fps (DirectX caps game application at 60 fps by default). How should I fix it to make it work?
getGameTime() function counts the time like stopwatch in millisecond when game application starts.
//Called every time you need the current game time
float getGameTime()
{
UINT64 ticks;
float time;
// This is the number of clock ticks since start
if( !QueryPerformanceCounter((LARGE_INTEGER *)&ticks) )
ticks = (UINT64)timeGetTime();
// Divide by frequency to get the time in seconds
time = (float)(__int64)ticks/(float)(__int64)ticksPerSecond;
// Subtract the time at game start to get
// the time since the game started
time -= timeAtGameStart;
return time;
}
With fps limit
http://www.youtube.com/watch?v=i3VDOMqI6ic
void update()
{
if ( setFPSLimit(60) )
updateAnimation();
}
With No fps limit http://www.youtube.com/watch?v=Rg_iKk78ews
void update()
{
updateAnimation();
}
bool setFPSLimit(float fpsLimit)
{
// Convert fps to time
static float timeDelay = 1 / fpsLimit;
// Measure time elapsed
static float timeElapsed = 0;
float currentTime = getGameTime();
static float totalTimeDelay = timeDelay + getGameTime();
if( currentTime > totalTimeDelay)
{
totalTimeDelay = timeDelay + getGameTime();
return true;
}
else
return false;
}