Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Unity Job System with Oni
#1
Hey Virtual Method guys!

I work for an indie studio using Obi Fluid and Rope in the production of one of our current development projects, so far to fantastic effect!

I've been extending the systems provided to match our game's requirements and so far have implemented things like :

- Container Volumes which allow a container to preserve particles within them, to allow the rest of the particles to have a short lifespan to reduce overhead.
- Fluid makeups within containers to cache the percentages of the fluids within a container, adjusting how much is in each etc.
- Leave stains and trails on collision by writing to a render texture with colour matching from the particles.

And quite a bit more - so far this system has been extremely adaptable and truly you guys deserve a lot of credit for a brilliant system!

This next level of optimisation though is something quite important to the performance of our game, so I wanted to ask your opinions before I committed to it for our next milestone.

We do lots of things with collisions, especially with fluid particle collisions, and some of the more intricate containers in our games are made up of quite a few primitive colliders, and so when subscribing to Oni's collision events like so:

Code:
foreach (Oni.Contact contact in e.contacts)
{
    // this one is an actual collision:
    if (contact.distance < MIN_REQUIRED_PARTICLE_COLLISION_DISTANCE)
    {
        Component collider;
        if (ObiCollider.idToCollider.TryGetValue(contact.other, out collider))
        {
        }
    }
}
 
we'll sometimes be looping through 7000+ contact points (for multiple containers all full of fluid). While most of these early out since they don't come inside the collision distance (we're also working to 1 Unit = 1metre, however these containers are the size of bowls which are obviously quite small - to the point where the collision distance is tiny (0.0001f)).

One solution I've made with some improvements to performance is to interleave looping contacts over frames (normally around 6 frames per cycle), caching contacts on frame 0 and looping over a chunk of the collisions over the following frames, before resetting after 6.

This isn't ideal though as to scale this we'd start running into collisions being missed! So this got me thinking about multi-threading as a potential next solution.

(Long intro!) So my question is have you experimented with running something like this through Unity's ECS feature? I have some experience using ECS systems, and Unity's one in particular, so I'm comfortable setting that up, however I wanted to know answers to any of these:

1) you had any alternative ideas to multi-threading?
2) you've used Obi with ECS?
3) there is a way to minimize the amount of contacts made (lowering the distance for collisions to be registered in a Solver?
4) masking layers for Oni Collisions, to allow us to only subscribe to collision events between certain layers? (It would be fantastic if I could mask out the collisions so that it was only the Container Volumes 1 trigger volume, and the particles firing collision events).
5) is it possible to subscribe to collision events that happen on particular colliders?

Massively appreciate your time and support on this, as Fluid simulation is quite a large part of our game we're working on, and we'd love to be able to keep going with this solution up to release, so your time is really valuable to me!

Thanks in advance,

Tim
Reply
#2
(30-04-2019, 02:48 PM)TimLewis Wrote: Hey Virtual Method guys!

I work for an indie studio using Obi Fluid and Rope in the production of one of our current development projects, so far to fantastic effect!

I've been extending the systems provided to match our game's requirements and so far have implemented things like :

- Container Volumes which allow a container to preserve particles within them, to allow the rest of the particles to have a short lifespan to reduce overhead.
- Fluid makeups within containers to cache the percentages of the fluids within a container, adjusting how much is in each etc.
- Leave stains and trails on collision by writing to a render texture with colour matching from the particles.

And quite a bit more - so far this system has been extremely adaptable and truly you guys deserve a lot of credit for a brilliant system!

This next level of optimisation though is something quite important to the performance of our game, so I wanted to ask your opinions before I committed to it for our next milestone.

We do lots of things with collisions, especially with fluid particle collisions, and some of the more intricate containers in our games are made up of quite a few primitive colliders, and so when subscribing to Oni's collision events like so:

Code:
foreach (Oni.Contact contact in e.contacts)
{
// this one is an actual collision:
if (contact.distance < MIN_REQUIRED_PARTICLE_COLLISION_DISTANCE)
{
Component collider;
if (ObiCollider.idToCollider.TryGetValue(contact.other, out collider))
{
}
}
}
 
we'll sometimes be looping through 7000+ contact points (for multiple containers all full of fluid). While most of these early out since they don't come inside the collision distance (we're also working to 1 Unit = 1metre, however these containers are the size of bowls which are obviously quite small - to the point where the collision distance is tiny (0.0001f)).

One solution I've made with some improvements to performance is to interleave looping contacts over frames (normally around 6 frames per cycle), caching contacts on frame 0 and looping over a chunk of the collisions over the following frames, before resetting after 6.

This isn't ideal though as to scale this we'd start running into collisions being missed! So this got me thinking about multi-threading as a potential next solution.

(Long intro!) So my question is have you experimented with running something like this through Unity's ECS feature? I have some experience using ECS systems, and Unity's one in particular, so I'm comfortable setting that up, however I wanted to know answers to any of these:

1) you had any alternative ideas to multi-threading?
2) you've used Obi with ECS?
3) there is a way to minimize the amount of contacts made (lowering the distance for collisions to be registered in a Solver?
4) masking layers for Oni Collisions, to allow us to only subscribe to collision events between certain layers? (It would be fantastic if I could mask out the collisions so that it was only the Container Volumes 1 trigger volume, and the particles firing collision events).
5) is it possible to subscribe to collision events that happen on particular colliders?

Massively appreciate your time and support on this, as Fluid simulation is quite a large part of our game we're working on, and we'd love to be able to keep going with this solution up to release, so your time is really valuable to me!

Thanks in advance,

Tim

Hi Tim!

Thanks a lot for your detailed description of what you've already done with Obi, and what you're trying to do. I'll try to explain in detail too.

Internally, Obi uses a sort of custom ECS paradigm: each particle is just an index (like entities in ECS) and per-particle data (components) are stored sequentially in memory using arrays. A work-stealing thread pool takes care of consuming jobs, and on top of that most math-intensive routines are written using hand-optimized SIMD (which is the kind of instructions that the Burst compiler strives to emit).

So of course, we've considered porting all this to Unity's own ECS, since mapping between our current system and ECS is pretty much 1:1. We've had some success (still there's functionality missing from Unity's ECS that prevents us from getting everything ported), and we're still working on it as it has many advantages: full engine source would ship with the asset, automatic support for all platforms, faster development iterations, less of a usability barrier for extending/modifying the system, etc.

Your use case falls into this last category. By having contacts as blittable structs in a NativeArray<T>, using a Job to iterate trough them would be straightforward. Currently we have our own internal equivalent of NativeArray<T>, which is ObiList<T>. We might be able to hack together a way to use it from jobs, so we'll look into it.

However, you can use standard C# threads to iterate trough it in parallel. I'd recommend trying this in the meanwhile.
Reply