NVIDIAGameWorks / PhysX

NVIDIA PhysX SDK
Other
3.18k stars 807 forks source link

Rigidbodies teleport and pop after being put to sleep #125

Open proc-sim opened 5 years ago

proc-sim commented 5 years ago

I have a setup where I set the sleep threshold on my rigidbodies to 0, and call putToSleep manually, myself, when certain properties of the rigidbodies match certain manually-calculated thresholds. I call putToSleep before my PhysX step, and calculate the thresholds after the PhysX step (so RBs that match the threshold are put to sleep prior to the next PhysX step).

However, I've run into a very bizarre issue....rigidbodies that are put to sleep will teleport to locations I would have expected them to be had they never been put to sleep, once they wake up.

Prior to teleporting, I've validated that the world pose of the rigidbodies is indeed their sleeping location and that their velocity magnitudes are indeed zero. So I have no idea why they are teleporting or how they even know where to teleport to. I would expect them to simply wake up in-place and go from there, but instead get this weird popping/teleporting behavior.

Here is an example of the problem. You can see I have some rigidbodies that I put to sleep pretty early on (so they freeze in space), who then collide with a kinematic Mesh collider (a cylindrical cup). Some of them are not awoken on contact (and I've verified that no collisions on those frames between the RBs and the kinematic collider in my onContact callback) and tunnel through the base of the cup, and instead pop/teleport later on.

Any idea would could possibly be causing this? Does calling putToSleep revert pose changes from the previous time step? In other words, if RBs are awoken by PhysX during the PhysX calculations, then put to sleep by me immediately after, will that revert any pose/velocity changes that happen between being awoken and put to sleep again? I'm wondering if this is happening due to repeated calls to putToSleep on those RBs between successive time steps...

sleep

kstorey-nvidia commented 5 years ago

Could you explain what problem you are trying to solve by handling sleeping yourself? PhysX's sleep logic operates on islands, not individual bodies. This is really important to make sure that bodies don't end up floating in mid-air when a stack slowly collapses, for example.

If you put a body to sleep, it triggers PhysX to invalidate a number of properties, including contact pairs and cached data. When the body is in contact with multiple bodies or jointed to other bodies, the bodies are quite often immediately woken by the island management because not all bodies in the island are candidates for sleeping. This forces PhysX to reconstruct all the data that was just destroyed/invalidated, causing both performance and behavioural regressions (behavioural because friction correlation data is lost).

Can you please connect to PVD and visualize what PhysX is actually doing internally? I expect you'll see smooth motion rather than teleporting.

Based on the results of your video, it looks like the PhysX simulation is doing the correct thing, which makes me wonder if instead it is just your render that is being skipped being updated. Are you using PhysX's "active actors" feature? I wonder if forcing actors asleep means they don't appear on the active actor list on the frma that they were woken? Or are you forcing to sleep before calling fetchResults? This would likely mean a recently-woken body would not appear in the active actors list. It's public pose may also not be updated because the body is classed as asleep so the copy from internal-to-buffered state may be skipped, while the internal (non-buffered) state is updated.

proc-sim commented 5 years ago

Hmm, well I was hoping to offer users more precise control over individual RB sleeping, allowing them to choose where/when to put specific RBs to sleep, for example. However it sounds like the island approach to sleeping makes calling 'putToSleep' on an individual RB somewhat counter-intuitive or even not-recommended, then?

I'm not using active actors, and do indeed iterate over all RBs after each step, which is where I was checking their global pose to make sure my render objects match the internal state of the RBs. Your last sentence seems like it would explain the issue I'm seeing...is there any way to force a buffered update? And/or, when should putToSleep be called to ensure the buffer is updated? Only immediately after "fetchResults"?

kstorey-nvidia commented 5 years ago

You should be safe to call it after fetchResults() and before the next call to simulate() but I wouldn't recommend it. There have been quite a few attempts to by users to write their own sleep logic and all the attempts I've been informed of have ended up performing worse (in some cases, significantly worse) than just letting PhysX do its thing.

You could alternatively call setWakeCounter(0) on the body - that zeroes the wake counter, which makes it a candidate for sleeping next frame. There is no guarantee it will go to sleep. For this to happen, all the other bodies in the island with it to have a wake counter of 0. This should not result in performance hazards while giving you control over when bodies become candidates for sleeping.

proc-sim commented 5 years ago

So I'm trying to switch my sleep methods over to the built-in PhysX threshold/wakecounters, instead of my own putToSleep/wakeUp methods, but the behavior I'm seeing doesn't make much sense to me.

For example, I have a single rigidbody pushed downward with a small force on the first frame of the simulation. However, even if I set my wake counter to 0 and the sleep threshold to something absurdly high (100000), it doesn't sleep until frame 12 of the sim. Lower values (1000) prevent my RB from sleeping at all, even though its velocity magnitude is 1. The docs don't say much about what the sleep threshold corresponds to. Is it the combined magnitude of the linear/angular velocity (which themselves are stored in per-second units)? That's what I imagined, but my results imply otherwise.

Also, the docs mention that addForce automatically wakes up rigidbodies. So how can I apply custom gravity/wind forces without preventing RBs from ever falling asleep? For example, a directional force (custom gravity: ie, (0,-1,0)) causes RBs to fall downward but never fall asleep even after coming to "rest" on a collider below, because AddForce must be called each step in order to maintain a constant force., which apparently wakes up particles. How is default PhysX gravity applied without waking particles up each step?

proc-sim commented 5 years ago

It seems wakeCounterResetTime in updateWakeCounter in DyBodyCoreIntegrator.h is hardcoded to 20.0f*.02f (line 188), thereby ignoring the user-defined values (wakeCounterResetDefined defined in the scene desc).

Is this a bug?

Also I don't fully understand the logic behind accumulating velocity values in line 344/345 of DyBodyCoreIntegrator.h. I imagined the sleep logic would be "if energy consistently < threshold for x time, then sleep"...but it seems to be more like "if combined energy for all steps over x time < threshold, then sleep". Is that correct?

proc-sim commented 4 years ago

Hate to bump this thread after 6 months but all the issues raised in my previous two posts are still active...any chance I can get some insight on these issues?