PhysX framerate issue

Started by
8 comments, last by jdavis 16 years, 2 months ago
For some reason when I get lower framerates the physics seems to (speed up) so all my velocities are wrong and etc. I can only assume it's something to do with the fetchResults() function, however, can someone explain when I'm suppose to call the update, flushstream and fetchresults? In the PhysX samples that call them all together as follows: m_pScene->simulate(fDeltaTime); m_pScene->flushStream(); m_pScene->fetchResults(NX_RIGID_BODY_FINISHED, true); Using the code above, everything works fine until I drop under 60fps and then everything is sensitive. A force value that normally worked fine is now making it shoot across the screen. Upon reading the fetchresults description, it's suppose to tell PhysX when your scene is finished rendering (I'm guessing) but when I put it at the end of my render function it wont even let me move or do anything. Anyone have an idea of the order or what is wrong? Thanks, Jon
Advertisement
Quote:Original post by jdavis
A force value that normally worked fine is now making it shoot across the screen.


Is this a force that you are calculating and applying yourself? If so you need to take the frame time into account.

The force will be applied over the entire time step, and if that time step varies then the effect of the force on the object will be different. The change in momentum is equal to Force*Time (Impulse).


Quote:Original post by jdavis
Upon reading the fetchresults description, it's suppose to tell PhysX when your scene is finished rendering (I'm guessing) but when I put it at the end of my render function it wont even let me move or do anything.


fetchResults basically gathers the simulation results from your simulate() call and puts them in your NxActor data structures so that you can read them and apply them back to your visual scene. It does other stuff too...collision callbacks, etc., but the net effect is that it makes sure NxActor data is up-to-date and readable.

The way your doing it now is valid, though there are other arrangements that would do better thread or multicore load balancing (e.g., enable PhysX to work on the next time step within another thread or on another CPU while you are using results from the prior time step in your main rendering thread).

PhysX is built to enable physis to run asynchronously with rendering. But, by calling fetchResults in blocking mode immediately after you call simulate(), you are basically forcing it into a synchronous mode....wait for physics to finish before doing anything else. There's not necessarily anything wrong with that. BUT, if PhysX ends up taking a VERY long time for something (resolving many collisions, constraint force solving, etc.), then you can get into a situation where the frame time grows larger and larger for each frame...I won't go into how this happens exactly, but it can happen if things are done naively.

What I would do is call checkResults(), and only if checkResults() returns true then call fetchResults. I also would use NX_ALL_FINISHED instead of NX_RIGID_BODY_FINISHED, for both checkResults() and fetchResults(). Further, you should not call simulate() more than once without calling fetchResults(), so you'd have to have a flag and even if you render several frames before physics finishes, don't call simulate() again until you are able to fetchResults(). Make sense? More stuff. The fDeltaTime should reflect the time between calls to simulate(), not just your frame time...important in the case that a single physics simulation is spread across multiple frames. Now, all this should keep your app responsive even if physics is not completing fast. It doesn't mean your physics will be what you want it to be, but it should at least keep things going. The kind of game loop I'm talking about is:

bool m_bCompletedLastFrame; // in class definition
float m_fTimeSinceLastCallToSimulate; // in class def

if (bCompletedLastFrame)
{
m_pScene->simulate(m_fTimeSinceLastCallToSimulate);
m_bCompletedLastFrame = false;
m_fTimeSinceLastCallToSimulate = 0.0;
}

m_fTimeSinceLastCallToSimulate += fDeltaTime;

if (m_pScene->checkResults(NX_ALL_FINISHED, false))
{
m_pScene->fetchResults(NX_ALL_FINISHED, true);
m_bCompletedLastFrame = true;
}

(My management of m_fTimeSinceLastCallToSimulate is probably not quite right, but this shows the idea.)
Graham Rhodes Moderator, Math & Physics forum @ gamedev.net
Original post by grhodes_at_work
Quote:Original post by jdavis

What I would do is call checkResults(), and only if checkResults() returns true then call fetchResults. I also would use NX_ALL_FINISHED instead of NX_RIGID_BODY_FINISHED, for both checkResults() and fetchResults().



I don't know what SDK you are running but 2.7.3 says

NX_ALL_FINISHED
NX_RIGID_BODY_FINISHED Refers to the primary scene and all compartments having finished, the new results being readable, and everything being writeable.


enum NxSimulationStatus
{
NX_RIGID_BODY_FINISHED = (1<<0),
NX_ALL_FINISHED = (1<<0), //an alias as the above is misnomer
NX_PRIMARY_FINISHED = (1<<1),
};
In the game loop you showed an example of. When exactly do you render your objects? Before or after fetchresults?
Make sure that you're not applying forces when you should be applying impulses, and vice versa (both go through the same PhysX function call).

As a guideline - if the reason for changing the velocity happens practically instantaneously - e.g. reaction to an explosion, gunshot hit etc, you should be using impulses. If it happens over a finite period of time (more than one physics update) - e.g. wind/drag, rocket thrust etc you should use forces.

As another guideline - if you need to take the frame time into account when calculating the force/impulse, you're probably using the wrong one.

Edit: It _might_ be that somewhere in you're code you are calculating a force/impulse using the frame time, and you "tuned" the scaling of this force/impulse so that it's right when the frame rate is 60FPS. If your dependency on the frame time was actually inverted (i.e. proportional to 1/FPS instead of FPS, or vice versa), then you wouldn't notice if the frame rate didn't change much, but it could create larger (instead of smaller) forces/impulses in the frame rate drops a lot.
Quote:Original post by MrRowl
Make sure that you're not applying forces when you should be applying impulses, and vice versa (both go through the same PhysX function call).

As a guideline - if the reason for changing the velocity happens practically instantaneously - e.g. reaction to an explosion, gunshot hit etc, you should be using impulses. If it happens over a finite period of time (more than one physics update) - e.g. wind/drag, rocket thrust etc you should use forces.

As another guideline - if you need to take the frame time into account when calculating the force/impulse, you're probably using the wrong one.

Edit: It _might_ be that somewhere in you're code you are calculating a force/impulse using the frame time, and you "tuned" the scaling of this force/impulse so that it's right when the frame rate is 60FPS. If your dependency on the frame time was actually inverted (i.e. proportional to 1/FPS instead of FPS, or vice versa), then you wouldn't notice if the frame rate didn't change much, but it could create larger (instead of smaller) forces/impulses in the frame rate drops a lot.


It doesn't matter what I'm applying. I've tried not using deltatime in my force calculations and I'm still experiencing the speed up. Also with adding torque.

if (GetKeyState(VK_UP) & 0x80)
{
forceVec = look * 200000 * fDeltaTime;
m_pPhysXBall->addForce(forceVec);
}

Keep in mind in the code above I'm using delta time but with or without, the problem still persists. 60+ FPS everything is fine. When I drop below, it's almost as if the forces are tripled.

I made a recording to show the result. I hope this helps.
http://www.headwarstudios.com/problem.wmv


Anyone else have any thoughts?
Quote:Original post by jdavis
It doesn't matter what I'm applying. I've tried not using deltatime in my force calculations and I'm still experiencing the speed up. Also with adding torque.

if (GetKeyState(VK_UP) & 0x80)
{
forceVec = look * 200000 * fDeltaTime;
m_pPhysXBall->addForce(forceVec);
}

Keep in mind in the code above I'm using delta time but with or without, the problem still persists. 60+ FPS everything is fine. When I drop below, it's almost as if the forces are tripled.


Impulse = Force * time

so...

Force = Impulse / time

try...

if (GetKeyState(VK_UP) & 0x80)
{
forceVec = look * 200000 / fDeltaTime;
m_pPhysXBall->addForce(forceVec);
}

You'll need to adjust your 200000 multiplier (make it smaller) and check that fDeltaTime is never zero (or very small).

or alternatively just use Impulses directly; is there an addImpulse function you can use?
Quote:Original post by jdavis
Keep in mind in the code above I'm using delta time but with or without, the problem still persists. 60+ FPS everything is fine. When I drop below, it's almost as if the forces are tripled.


Using fDeltaTime (multiplying, dividing, whatever) in your force calculation like this is just wrong.

I wonder - when do you clear the key state? Perhaps you're getting this function which adds/accumulates forces called multiple times per physics step when the framerate is low.

Assuming you want the ball acceleration to be gradual whilst the player holds down a key, then whilst this is happening the total force that you added to the body using addForce should be the same every time you call simulate on the scene. It's annoying you can't actually get this accumulated force to check it, but you could just printf the force vector every time you call addForce, and add the values up yourself (printf every time you call simulate).

Another reason for not using fDeltaTime in your force calculation is that if you're using NX_TIMESTEP_FIXED then the actual simulation time will generally be different to the time you actually request, resulting in a (probably) small and essentially random error in the resulting accelerations if you apply force*time when you really want impulses.
I understand that using delta time in the force calculation is wrong. Regardless, either way is still giving me the issue.

This topic is closed to new replies.

Advertisement