Sign in to follow this  
Medo Mex

Frame Independent Problem

Recommended Posts

Hi Guys,

 

I'm using QueryPerformanceCounter() to get the elapsed time in seconds in a float variable.

 

Then I multiply the elapsed time with the velocity as the following in entity Update(float timeElapsed) function:

void Update(float timeElapsed)
{
velocity *= timeElapsed;
setPosition(getPosition() + velocity); // Now, update the entity position based on velocity
velocity = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
...
...

I can notice that the entity movement speed is not the same when the frame rate drop, for example If I enable VSync during rendering to drop the frame rate to 60, the entity move faster than when the frame rate is 130

 

Any idea what could be causing this problem?

Edited by Medo3337

Share this post


Link to post
Share on other sites

How do you calculate the velocity in the first place? Normalize it before multiplying it with the delta time and see if that makes it move at the same speed regardless of the frame rate. If that helps then it means that the calculations of the velocity themselves depend on the frame rate.

Share this post


Link to post
Share on other sites


I'm using QueryPerformanceCounter() to get the elapsed time in seconds in a float variable.

 

How exactly are you using QPC to get the elapsed time?

 


I can notice that the entity movement speed is not the same when the frame rate drop

 

Whenever your framerate drops the time elapsed increases which is the expected obviously. In your example, when the elapsed time varies, so do an entity speed. A solution to that is use a fixed time step, which won't guarantee that your frame rate is the same every time, but will integrate your position in a more deterministic way. Read this article:

 

http://gafferongames.com/game-physics/fix-your-timestep/

 

Could you paste your game loop here?

Share this post


Link to post
Share on other sites

@GuyWithBeard: Even after I normalize the velocity, I still have the same problem.

 

@Irlan Robson:

 

In class construction:

LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
secondsPerCount = 1.0 / (double)freq.QuadPart;

Every frame:

void FrameUpdate()
{
     LARGE_INTEGER liCurrTime;
     QueryPerformanceCounter(&liCurrTime);
     currentTime = (double)liCurrTime.QuadPart;
     if (lastTime == 0)
     {
         lastTime = currentTime;
     }

     double timeElapsed = (currentTime - lastTime) * secondsPerCount;

     lastTime = currentTime;

     // Code here to do all the game updates...
     // ...
     // ...
}
Edited by Medo3337

Share this post


Link to post
Share on other sites
Whatever behaviour you're trying to achieve, normalizing the velocity wouldn't help at all.

Your game loop is correct. Try disabling the v-sync to make sure your problem isn't related to the CPU and check the results. Also, you should rely on elapsed microseconds or miliseconds per frame rather than FPS for measurement accuracy.

Share this post


Link to post
Share on other sites

@Irlan Robson:

@GuyWithBeard:

I notice that I don't have this problem if the entity is not assigned to the physics engine.

 

I'm using Bullet Physics and doing simulation as the following:

m_DynamicsWorld->stepSimulation(elapsedTime);

Share this post


Link to post
Share on other sites

function Update(float timeElapsed)
{
velocity *= timeElapsed;
setPosition(getPosition() + velocity); // Now, update the entity position based on velocity
velocity = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
...
...


The problem isn’t with the timers, but rather the units. The code is demoting velocity from units per second to units. For example:

1. Set velocity to 10 units per second (u/s).
2. Multiplying velocity by elapsed time in seconds (s) gives you a distance in units (u).
3. The result is then fed back into velocity-- reinterpret_casting from u to u/s, as it were. (BAD!)
4. Multiplying the new velocity in units with a the elapsed time gives you u*s units. (NO!)
5. Repeat steps 3-5, but with u*s^N units for velocity each iteration.


Be mindful of the units of your operands whenever you do any sort of math:
float3 distance_traveled = velocity * timeElapsed;  // u/s * s -> u
setPosition( getPosition() + distance_traveled );   // u + u -> u
If your intention was to have damping:
const float DAMPING = 0.8f;        // (scalar) reduces velocity 20% every 1 second (roughly) 
velocity *= DAMPING * timeElapsed; // u/s * scalar -> u/s
float3 distance_traveled = velocity * timeElapsed; // u/s * s -> u 
setPosition( getPosition() + velocity * timeElapsed ); // u + u -> u
Edited by fastcall22

Share this post


Link to post
Share on other sites


Whatever behaviour you're trying to achieve, normalizing the velocity wouldn't help at all.

 

Well it helps in the sense that it narrows the problem down. I never meant he should leave the normalization in there (obviously).

 

Since the problem is still there after normalization, it means that the velocity value is fine (or at least it means that there is a problem somewhere else, ie. the delta time value).

Share this post


Link to post
Share on other sites

Now, it's clear that the problem is caused by the physics engine, since the movement is frame independent only if I disable the physics engine or if I don't assign the entity to the physics engine.

 

Here is what I'm doing in the physics engine each frame:

1. Iterate through all the body dynamics and update their transformation based on the entities transformation

2. Simulate physics using: m_DynamicsWorld->stepSimulation(elapsedTime);

3. Iterate through all the body dynamics and update the entities transformation based on the rigid bodies transformation.

Edited by Medo3337

Share this post


Link to post
Share on other sites

for a proper implementation of fixed timestep al la gaffer, you get ET, add it to your accumulator, then while the accumulator >=DT, you run one update and subrtract DT from the accumulator. so velocity will never be multiplied by ET, only perhaps by DT and/or some constant to make it run at the correct speed with fixed timestep. where DT is the fixed time step per update.

 

what you want to do is go with the final algo listed in the gaffer article. right now it looks like  your using the 4th or 5th algo out of the six or so listed.  he shows the evolution of the algo from naive brute force implementation up through fully functional with all  cases covered. so only the last algo in the article works correctly with no flaws. what you're doing now looks like one of the algos just before the last one in that article.

 

here's timer code you can use to double check yours:

 

// timers
DWORD Ztimer[10];
LARGE_INTEGER Ztimer_freq,Ztimer2[10];
 

init:

QueryPerformanceFrequency(&Ztimer_freq);

 
// start timer a
void Zstarttimer(int a)
{
Ztimer[a]=GetTickCount();
QueryPerformanceCounter(&Ztimer2[a]);
}
 
// returns elapsedtime of timer a in millisecs
int Zelapsedtime(int a)
{
LARGE_INTEGER c,f;
float g,h;
QueryPerformanceCounter(&c);
f.QuadPart=c.QuadPart-Ztimer2[a].QuadPart;
g=(float)f.QuadPart;
h=(float)Ztimer_freq.QuadPart;
g*=1000.0f;
g/=h;
return((int)g);
}
 
// returns elapsed ticks of timer a
int Zelapsedticks(int a)
{
LARGE_INTEGER c,f;
QueryPerformanceCounter(&c);
f.QuadPart=c.QuadPart-Ztimer2[a].QuadPart;
return((int)f.QuadPart);
}
Edited by Norman Barrows

Share this post


Link to post
Share on other sites

I tried what Irlan Robson suggested, now the frame is independent but sometimes the game lag (when there is sudden frame rate drop) for example, from: 60 FPS to 49 FPS

 

I'm not sure why it's lagging occasionally, any suggestions?

Share this post


Link to post
Share on other sites

According do computer science it can be many things you're doing incorrect. Disable anything but physics and graphics and check the results.

 

Since you've mentioned Bullet, check if it does substepping (steps > 1) internally already. If yes, disable it and use only the substepping scheme you have implemented.

 

Again, my suggestion is to isolate the problem and measure the elapsed time of code executions using a profiler. That's the best way of checking performance bootlenecks IMHO.

Edited by Irlan Robson

Share this post


Link to post
Share on other sites
@Irlan Robson: Here is what I'm doing (correct me if I'm doing something wrong):
double timeElapsed = getElapsedTime();

const static double dt = 0.01;
static double accumulator = 0.0;
if (timeElapsed > 0.25)
    timeElapsed = 0.25;
accumulator += timeElapsed;
while (accumulator >= dt)
{
    Update((float)dt);
    accumulator -= dt;
}

And here is how I call bullet physics stepSimulation():

void Update(float dt)
{
    // ...
    // ...
    // ...
    dynamicsWorld->stepSimulation(dt, 0); // dt is always equal to 0.01, maxSubSteps is equal to 0
}
Edited by Medo3337

Share this post


Link to post
Share on other sites

According to Bullet, when you pass maxSubSteps = 0, it will use only step the world once. Therefore, your code seems to be correct.

 

Try to isolate code sections and compare the elapsed time of their execution using a profiler (scope-based if you'd like convenience; otherwise just measure it manually and dump the elapsed time to the console) to check where is the source of these frame rate spikes. That will save us a couple of words here in trying to guess exactly what is the problem.

Share this post


Link to post
Share on other sites

@Irlan Robson: I created a class to measure the time of execution for a block of code

 

It seems that the problem is not caused by the physics engine, because even when I use free camera to move in the scene I experience lag occasionally.

 

Although the lag occurs only when the frame rate drop few frames (for example: From 60 to 52), running at around 49-55 FPS for few seconds should not make any noticeable difference (should not cause lag) than running at 60 FPS, am I correct?

 

Btw, I have VSync enabled.

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