Sign in to follow this  
AverageJoeSSU

please review my C++11 Chrono render/game loop?

Recommended Posts

I am struggling to get a buttery smooth framerate with chrono.

 

I have a simple animated rectangle that is adding an angle (multipled by delta time) to a sin wave and after seemingly random intervals of time i get a blip. (anywhere between 10 seconds and 5 minutes).

 

Here is my loop (based on Gaffers Fix your timestep!)

using namespace std::chrono;
using Clock = high_resolution_clock;
    init();

    auto kMaxDeltatime = duration<long,std::ratio<1,60>>(1);
    auto lastEndTime = Clock::now();
    auto accumulator = microseconds(0);
    while (!glfwWindowShouldClose(tsxWindow->window)) {
        auto newEndTime = Clock::now();
        const auto frameTime = newEndTime - lastEndTime;
        lastEndTime = newEndTime;
        accumulator += duration_cast<microseconds>(frameTime);

        while ( accumulator >= kMaxDeltatime )
        {
            //accumulator -= duration_cast<microseconds>(kMaxDeltatime);
            Update(duration_cast<milliseconds>(frameTime).count());
            accumulator -= duration_cast<microseconds>(frameTime);
        }
        Draw(duration_cast<milliseconds>(kMaxDeltatime).count());
        glfwPollEvents();
    }

and my objects update:

void Object::Update(long delta_time) {

    angle += 0.05 * (float)delta_time;
    angle = fmod(angle,360);
    double scale = 1;
    transformWorld = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));
    transformWorld = glm::translate(transformWorld, glm::vec3(sin(angle*PI/180.0)*scale, 0.0f, 0.0f));
//    fprintf(stdout, "deltatime: %ld \n angle: %f \n WorldTransform: \n %s \n", delta_time, angle, glm::to_string(transformWorld).c_str());
//    fflush(stdout);
}

and my smoothness is best with a high resolution clock (not a steady one) but even still after a long while (~5 minutes) it can stutter (not tear).

 

I am using linux and clang++ (swap interval is 1).

Edited by AverageJoeSSU

Share this post


Link to post
Share on other sites

Edit: Nevermind,  looking again you are expecting a fixed time step in the update. So not sure.

 

I would think just by a quick glance, you would need to do something to frame time in your while ( accumulator >= kMaxDeltatime loop?

Shouldn't it probably change if you execute the update loop more than once?

Unless you have other counters to let your code know that the second update loop is later in time than the previous one?

 

Otherwise just upon quick observation and guessing, it looks like you would do the same thing twice possibly.

Edited by Eklipse

Share this post


Link to post
Share on other sites

using namespace std::chrono;
using Clock = high_resolution_clock;

 

Are you sure that your high-resolution clock it's the fastest way of receiving time? If yes, skip this alert. If don't, you should probably search for it (since I don't know how you're working).

 

 

 


accumulator += duration_cast(frameTime);

 

You're  accumulating times in floats or doubles I suppose. You should be able to pick the lowest possible time unit in order to accumulate time. Search for the similar data type in your language/framework equivalent to a unsigned long long and keep microseconds to avoid floating point precision errors. Only use floats or doubles to calculate time intervals at some specific points (don't use it for time accumulation).

 

After that, you can transcript your game loop to the above:

CurrentRealTimer.Update() //Increase the time counter by the time elapsed since last update.
unsigned long long CurrentAccumulatedRealTime = CurrentRealTimer.GetCurrentTime();
while ( CurrentAccumulatedRealTime - CurrentUpdatedTime > GAME_UPDATE_TIME_STEP ) {
         CurrentUpdatedTime += GAME_UPDATE_TIME_STEP;
         UpdateGameLogicBy( GAME_UPDATE_TIME_STEP );
}

Now your game update as fast you can, with no float or double precision accumulation errors, and with a unsigned integer data type range being up to almost 600 years from now. 

 

There is one more thing. At the remaining of each frame update, you still have a remaining amout of time. This one should be used to interpolate between rendering (not changing the game logic at all). The amount of interpolation can be computed as:

unsigned long long Lerp = (CurrentAccumulatedRealTime - CurrentUpdatedTime) / GAME_UPDATE_TIME_STEP;

This will give you basically a percentage of how you're close to the next rendering related to the current game logic time, but since you are not really caring for that, it's up to you to decide it.

Edited by Irlan

Share this post


Link to post
Share on other sites

Thanks for the review!

 

High resolution clock is the "fastest", but it is not reported as steady. I think this means that if the CPU is throttled and the system clock is changed because of battery or whatever, then the clock will change, thus becoming inconsistent? I am not sure if this is important or not, since it doesnt need to be 100% consistent forever (perhaps this is what i am seeing in the blips?)

 

The autos might seem a bit tough to decipher if you are not familiar with chrono. accumulator is a duration<int64_t, std::micro> which should be as good as i can get?

 

your snippet of code looks good, i'll plug it in and see if that improves the results!

Share this post


Link to post
Share on other sites


High resolution clock is the "fastest", but it is not reported as steady. I think this means that if the CPU is throttled and the system clock is changed because of battery or whatever, then the clock will change, thus becoming inconsistent?

 

If you know that you already have the equivalent of ::QueryPerformanceFrequency() and ::QueryPerformanceCounter in chrono you don't have a problem. Both these functions are the most precise way of obtaining the current time. The time will never be incosistent with the functions that I've mentioned. The frequency of the ticker it's fixed at system initialization and can only vary if you restart your computer.

 


The autos might seem a bit tough to decipher if you are not familiar with chrono. accumulator is a duration which should be as good as i can get?
,>

 

Looks like the accumulator it's fine (since you're using a long long 64). 

 


your snippet of code looks good, i'll plug it in and see if that improves the results!

 

It will improve the accuracy for sure. wink.png

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this