Okay, so I've played with a few more optimisations, but it's becoming clear quickly that currently the big performance hit is in the custom hashGrid broadphase stuff, and there's no easy way to solve it. I've considered a few techniques (and tried a couple, without much success), before I realised that I've been barking up the wrong tree, because really that custom broadphase is the thing we're wanting to remove anyway, and replace with the one that's running in Box2D already. I'm prepared to give this a go, but I'm so inexperienced with the internals of Box2D that I'm not even sure which questions I should be asking to get started. I'll have a go, though, and see if anyone can provide food for thought
Earlier in the thread, ewjordan talked about how he imagined these fluid particles might work in the engine:
What I had considered before was creating a new type of shape that represented the neighbor range for a fluid particle, stored any dynamic SPH per-particle variables, and reported a special type of fluid contact which would be passed along to the SPH solver. If that's the route you want to take, you need to figure out a lot of stuff, for instance SPH "contacts" can't be solved pair by pair like normal contacts (pressures depend on all of a particle's neighbors, and pairwise interactions depend on pressures), so you've got to store away some of the data, figure out the best time to process the group, etc, or maybe decide to use the old values, or something like that. It gets a bit messy, esp. when you realize that you have to also integrate a whole new contact type, which means digging into the internals to figure out exactly what need changing.
This sounds quite complex, but I'm not 100% convinced about how much of it is necessary. It seems to me that having something that behaves like a sensor shape (representing the neighbour range, but otherwise not doing any rigid body collision) is a reasonable starting point. With regards to the specifics of the SPH fluid constraint solving stuff, I'd be happy as a first pass to stick with something external to the core of the engine for now, so that there can be some flexibility in how users work out the specifics of the maths to tune things for the right behaviour. Perhaps eventually we can work out some universal system for solving the SPH and just allow the user to tweak a few values based around ranges, density, viscosity, etc, but for now all I'm thinking about is how these particles should behave with regards to the broadphase.
So, what would we need to do for basic stuff like flagging a shape in a way that says "this is a sensor, but it's one I'm using for a fluid particle?". The main thing as far as I can see is simply that a "fluid sensor" should register contacts with other fluid contacts as normal, but:
1 - Should possibly ignore contacts with shapes which aren't flagged as also being fluid sensors (not sure of exactly how we'll handle interactions between fluids and non-fluids yet, so I don't know how applicable this is)
2 - Should avoid creating a pair when it detects a contact with another fluid sensor. The fluid simulation doesn't need or use pairs, and given the high amount of overlap between particles, this is the first thing that makes Box2D assert when dealing with any notable number of particles.
Now, I don't really understand yet how fundamental pairs are to the engine. So I don't know if just turning off the bit of code that generates pairs for fluid sensors is a reasonable thing to do. Is anyone able to explain how the Box2D broadphase works, exactly? How and why it generates pairs, and what they're used for? Whether it's reasonable to just not generate pairs for certain types of shapes if we know we won't need them, or whether that's nonsensical - and if it is nonsensical, why?