bepu / bepuphysics2

Pure C# 3D real time physics simulation library, now with a higher version number.
Apache License 2.0
2.33k stars 268 forks source link

Activity management #21

Closed RossNordby closed 6 years ago

RossNordby commented 6 years ago

At the moment, all bodies are permanently active. That's not ideal!

Activity management has two parts:

  1. Graph-traversing deactivation. In a deferred fashion, look at the active set to find deactivation candidates. Once found, remove them from the active set and push them into the inactive set.
  2. Narrowphase-triggered activation. When a new constraint is created, any inactive involved body must be woken up. That brings its whole island with it.

Deactivation thresholds should be defined on a per-body basis to avoid issues with global thresholds being inappropriate at different scales.

Deactivated objects should be near-zero overhead. They are taken out of the simulation completely, with the exception of their collidable residing in the inactive/static tree.

Some questions remain about what API interactions force islands to wake up. Is the user allowed to move an object in a deactivated island? Can you remove an object directly from a deactivated island, or does it force the island awake before performing the removal? Answers likely depend on what requires the least development work.

RossNordby commented 6 years ago

As activity management comes together, we have some more probable answers:

  1. Users can reach into inactive body and constraint sets and fiddle with state, but the engine makes no guarantee about the validity of doing so.
  2. It is likely at this point that users can remove directly from body and constraint sets without waking the island up. This will be the default behavior- any time a removal occurs targeting an inactive object, no auto-activation occurs. This is relatively easy to support and makes some specific use cases faster. If the user wants to force things awake, they can do so explicitly before doing a removal. By making it the user's responsibility to wake objects up, it is possible to batch many activations together. Batched modifications will tend to be more efficient when dealing with many or large islands. The activation logic should be the same used by the narrowphase.
  3. Forceful deactivation is also allowed, though it only operates at the level of islands. It should fully share the logic of the deferred deactivator.
RossNordby commented 6 years ago

A change to number 2 above: consider an attempt to add a constraint between two inactive bodies. As is, deactivated islands do not persist their contained body handles to save on memory (a nontrivial amount- the simplest approach to storing that information would dominate the engine's total memory usage in pathological cases).

Without a way to efficiently detect body handle containment in an inactive constraint batch, inactive constraint adds would have to resort to extremely inefficient brute force means or more complicated special case storage formats.

In the interest of actually shipping at some point, adding a constraint will instead force all involved bodies awake. For consistency and to reduce surprises, removing constraints and bodies will also force the associated islands awake. This has a side effect of simplifying some code paths.

It is still possible for the user to batch activations together ahead of time for speedier processing. This only affects the use case where an inactive island is being modified without activation. Now, the user will have to force deactivate such a modified island once they're done modifying it. This will cost a bit more, but the rarity of the use case and the simplicity gained makes it a clear win for now.

Another significant change: kinematic and dynamic bodies will likely end up unified as far as deactivation goes. Kinematics will not block traversals through island graphs despite not being able to propagate any impulses. This avoids some complexity surrounding the solver's access of inactive kinematic velocities- now, if any involved body is active, the solver has a guarantee that they all are. The solver only ever has to access the active set.

It is likely that kinematics will still have a special case deactivation threshold of 0. The configurable deactivation threshold in the Activity struct will need to be specifically marked as applying only when a body is dynamic. The reasoning is that, later on, we may choose to optimize this further by preventing traversals across stationary kinematics. Many of the implementation options require that kinematics only deactivate at 0 velocity, so establishing that ahead of time avoids a very sneaky breaking change.

The unification will likely be low impact. Most people won't even notice, since v2 more easily supports statics that can have any shape. If they use a kinematic body instead of a static, the implication is that it is going to have a nonzero velocity, so dynamic bodies interacting with it won't often have an opportunity to deactivate anyway.

RossNordby commented 6 years ago

Currently going with kinematics having no special case at all- if that changes later, it'll need some sneaky breaking change notifications.

Things work now, though the performance could be better. Over 50% of the cost of activation/deactivation is tied up in locally sequential broadphase add/removes. Improving that depends almost entirely on the tree structure, so we're done with the activation/deactivation side of things for now.