Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Bug / Crash  Overlapping Obi Colliders too much causes the Compute backend to go mental
#1
Bug 
OK, I have a truly bizarre one for you. 

If you put enough Obi Colliders overlapping in the same place, even fully static box colliders, the Compute backend goes absolutely mental. It has some kind of overflow and starts adding random updraughts of force to particles in random areas, some near the colliders (but not touching) and some nowhere near it. Burst is unaffected.

I noticed it in a game where I have a level made out of modular parts and each Wall has an obi collider. I added some more colliders to the level and noticed that my fluid started randomly jumping up all over place and wouldn't stop. Even with the most aggressive smoothing, the fluid never reached a rest state and just kept jiggling. A ton of trial and error to find out that it's caused by obi colliders just being in the scene and switched on and not even necessarily near the fluid, and then I managed to recreate it by stacking enough colliders in the same spot.


Replication steps:
  • Create a new blank URP project
  • Install Obi Fluid 7.03
  • Launch Faucet And Bucket test scene and fix the scene materials
  • Set the Solver to Compute back-end and the emitter to speed 6 and lifespan 8
  • Edit the fluid blueprint to expand it to 12,000 particles and re-generate it
  • Create a 3D Cube at coordinates Vector3(0.879999876,1.23000002,-2.41999984). Add an Obi Collider.
  • Launch the Scene. Now duplicate the cube in place using Ctrl-D until you get to 16 cubes.
  • When you get to 16 cubes, you'll see obvious jets shooting out of the fluid below and near the cube, and another jet randomly on the far edge of the fluid.
Experimentation:
  • If you disable the obi colliders on the cubes 1 at a time you can see the effect diminish with each one.
  • If you move any of the cubes so that their colliders are no longer overlapping, they no longer contribute to the effect.
  • If you move the group of cubes around or re-scale them all, it changes the characteristics of the random bits of force causing the jets
  • Change the box collider size's y value on all the cubes slowly. When it hits 3, an entire quadrant of particles randomly jumps up in the air. Increase it further to just over 8.0, the effect disappears and everything goes back to normal. 8 world units tall seems to be a magic number, is that the unit size of the collider grid or something?
Expected Result: Not fluid going mental

Observed Result: Fluid going mental
Reply
#2
Hi!

This isn't a bug per se, but a limitation of the Compute backend that has its roots in GPU's (or more accurately, HLSL's) inability to allocate memory dynamically. This means we must allocate a fixed amount of memory, and drop data when we reach that maximum.

Particles in the compute backend have a hardcoded limit of 32 potential contact pairs each. Once you get past this amount of potential contacts, particles will start to randomly drop contacts every frame. This means they may drop their contact against the floor for a few frames, then get launched upwards next frame when they do keep the floor contacts but decide to drop different contacts. Which contacts are dropped and which ones are kept is decided essentially at random, since it depends on the order in which threads are executed as they find and append potential contacts for further processing.

Note that this limit affects potential contacts (not actual contacts/collisions). This is why particles will act up when they're merely near a crowd of colliders, even if they don't touch them.

You can increase this limit by opening up ColliderGrid.compute, near the top you'll see this define:

Code:
#define MAX_CONTACTS_PER_SIMPLEX 32

You can set it to 128 for instance to increase the amount of potential contacts allowed by each particle, at the cost of more memory. I didn't expose this limit in the solver inspector's Memory Budget section (like I did with other memory limits) because we doubted anyone would hit it, but now I see I was wrong  Ángel.

The Burst backend doesn't have this limitation, as it can dynamically allocate more memory if needed.

kind regards,
Reply
#3
(27-09-2024, 10:58 AM)josemendez Wrote: Particles in the compute backend have a hardcoded limit of 32 potential contact pairs each. Once you get past this amount of potential contacts, particles will start to randomly drop contacts every frame. This means they may drop their contact against the floor for a few frames, then get launched upwards next frame when they do keep the floor contacts but decide to drop different contacts. Which contacts are dropped and which ones are kept is decided essentially at random, since it depends on the order in which threads are executed as they find and append potential contacts for further processing.

Note that this limit affects potential contacts (not actual contacts/collisions). This is why particles will act up when they're merely near a crowd of colliders, even if they don't touch them.

Ah-ha, that makes sense! So it splits the game world up into cells and if there are too many potential contacts in a cell it will start to randomly drop contacts, including existing contacts like the floor. That explains why the problem got gradually worse as I added more colliders, increasing the limit to 64 solved my issue. Reading through the ColliderGrid compute shader now, I have a few more questions if you have time:

  1. What counts as a potential contact? Is it an entire collider, or a collider vertex, or a face? would boxcolliders, for example, use up fewer potential contacts?
  2. How does it determine where the grid starts and the size of the grid cells? Is it centered on the Solver, and so should I aim to put the solver in a central location to my level?
  3. Any idea roughly what the memory impact of increasing the contact limit is? And this may be a stupid question but I just want to make sure I'm not getting it wrong, is it storing the data in VRAM or RAM?
The fact that I've hit this limit probably indicates that I may be taking a less than ideal approach to collisions. In my game I have 1 floor collider that spans the entire map and then the corridors made out of modular walls slotted together, where each wall has an Obi collider, and then there are various objects randomly placed that also have Obi Colliders. They're almost all box colliders, with just a few mesh colliders. Do you have any recommendations for optimal approaches to collision to reduce the number of potential contacts?
Reply
#4
Hi!

(27-09-2024, 01:09 PM)Nyphur Wrote: What counts as a potential contact? Is it an entire collider, or a collider vertex, or a face? would boxcolliders, for example, use up fewer potential contacts?

Entire colliders. This is done during the broad phase, where contact pairs are determined (particle "A" and collider "X" are close enough to potentially collide). For simple, convex colliders each potential contact pair may spawn a single contact during the narrow phase. For more complex, possibly convex colliders (like terrain or MeshColliders), then a midphase takes place where the pair may spawn multiple contacts against individual features (triangles in the case of MeshColliders).


(27-09-2024, 01:09 PM)Nyphur Wrote: How does it determine where the grid starts and the size of the grid cells? Is it centered on the Solver, and so should I aim to put the solver in a central location to my level?

The origin of the grid depends on the bounding box of all particles.

Obi uses a multilevel 4D grid, where each cell has 4 coordinates: x,y,z and level. The size of cells is different for each level in the grid, cells in level N+1 are twice as big as cells in level N. Colliders are assigned a grid level according to the size of their bounding box, so that each collider overlaps at most 8 cells (or 4 in 2D). It's not really practical to use this information to fine-tune object or solver positioning, as the origin/cell size of the grid may vary depending on both object size and positioning.

(27-09-2024, 01:09 PM)Nyphur Wrote: Any idea roughly what the memory impact of increasing the contact limit is? And this may be a stupid question but I just want to make sure I'm not getting it wrong, is it storing the data in VRAM or RAM?

We'd be talking about VRAM, since this gets executed on the GPU.

This data gets stored in registers instead of global memory. The problem is not so much memory usage (as in "storage") but performance, as using up more registers increases register pressure: when a thread has run out of registers to store temporary data, it will "spill" data to local memory - which is slightly slower than registers. So the struggle is to use as few registers as possible in order to make data accesses faster, that's why I didn't use 128, 256, or a much larger size.

(27-09-2024, 01:09 PM)Nyphur Wrote: The fact that I've hit this limit probably indicates that I may be taking a less than ideal approach to collisions. In my game I have 1 floor collider that spans the entire map and then the corridors made out of modular walls slotted together, where each wall has an Obi collider, and then there are various objects randomly placed that also have Obi Colliders. They're almost all box colliders, with just a few mesh colliders. Do you have any recommendations for optimal approaches to collision to reduce the number of potential contacts?

Creating a few MeshColliders at runtime is the usual way to do scenery collisions (allows for more flexibility in terms of shape, requires less GameObjects and reduces the amount of work in the collision detection broadphase), though if your levels are procedurally created or use a modular approach it might not be easy or even possible. In that case increasing MAX_CONTACTS_PER_SIMPLEX may be the wisest option. I will consider exposing this value in the solver's inspector in upcoming updates, as it makes sense to modify it under specific circumstances.

kind regards,
Reply