nbody-wasm-sim
nbody-wasm-sim copied to clipboard
Try particular
You're welcome! It's a fun exercise for me as I'm currently working on a similar project using bevy and rapier so if my work can contribute to other projects than it should!
Originally posted by @Canleskis in https://github.com/simbleau/nbody-wasm-sim/issues/59#issuecomment-1213450712
How about maybe using Particular in this next :D
Sounds like a good idea! Ideally I would need to have Canleskis/particular#1 resolved to avoid unnecessary calls to the extend method on the Vec2, otherwise it's already been worked out. I'll push it to my fork so that you can check it out and I'll make a PR once Canleskis/particular#1 is resolved. If you don't mind the extend call we can also merge like this!
Let's wait until you think Particular is in a good state for integration. Then, I'm happy to leverage Particular, and endorse its use. The ultimate goal is to have a high quality sim like the one you made, but leverage raw WGPU for rendering. (since we are on the front page of https://wgpu.rs)
Sounds very reasonable, didn't know your project made it to the front page of WGPU, congrats! I plan to be working on Particular for the next couple of weeks to try and get compute shaders figured out. I'll also resolve https://github.com/Canleskis/particular/issues/1 as soon as possible!
@Canleskis What's the state of progress? :) I'm really interested to see if this could be merged within the next few days.
I've resolved Canleskis/particular#1 and made the changes to my fork to accommodate the changes. I didn't immediately make a PR in case I had overlooked something as I initially wanted to use an array representation for the position of a particle with const generics but that led to performance degradation especially when single-threaded. I will keep looking into this as it will probably make it easier for compute shaders to use arrays.
The current solution makes the API changes minimal and increased performance by about 10% for 2D simulations. I can make a PR if you're happy with the state of my fork!
Regarding compute shaders, I have not made much progress yet but it is going to be my focus starting now hopefully.
You are correct about an array of primitives being more compatible with compute shaders. Essentially to pass data to and from the GPU in a compute pipeline in WGPU the data must be mucked into a standard that WGPU allows. You'd essentially want an array of (posX, posY, mass) f32s.
There's an example already build on wgpu.rs, for 3D particles, but I don't think mass is involved there, and it is using 3D coordinates.
Exactly, so having an array or any other type that allows to iterate over the fields of a position will very likely be needed to be sent to the GPU with an arbitrary number of dimensions. This is probably going to require const generics, which would make the API less friendly so I'm trying to find an alternative.
But for now the current solution allows for an arbitrary number of dimensions without significant API changes and performance is the same as before. I would understand if you prefer to wait until Particular is readier for compute shaders until you want to use it in this project!
Do you mind submitted a pull request?
Done https://github.com/simbleau/nbody-wasm-sim/pull/68 :-)
Hey, I wanted to update you on the state of the compute shaders implementation! I have successfully implemented it in Particular in a somewhat limited way although it would suffice for this project. I'm looking into removing those limitations for Particular, but it might be a bit challenging since using the GPU for such a multi-purpose library is difficult.
It does not work on the web yet, but I hope to be able to solve that soon. I will push my progress to a branch in Particular soon and if I can't work around the limitations you'll be able to use that for this project hopefully!
I think you're doing great work.
It's true, if you plan to integrate with WebGPU you'd need to implement the same lifetime guarantees as them (unless unsafe), which would make your API look similarly flavored. Striking the right architecture sounds difficult, I will admit.
Something similar to this might work well with their API:
let physics_descriptor = ... // Contains all static variables like gravity coefficient, etc. for particular
let mut particular_manager = ParticularManager::new(&physics_descriptor); // ::new() gets the WGPU Context, and loads simulation static data given by the physics_descriptor
...
let data = [...]; // f32 data array containing masses and positions
particular_manager.step(data); // Runs a compute pass
// particular_manager is implicitly dropped when put out of scope, freeing GPU resources
I think you could also use the RAII pattern for the particular manager. For example, on drop, it can let go of GPU resources.
I believe this is similar to what I am doing. Both my new function and my step function take the some relevant resources (device and/or queue), although there is no static data. The main bottleneck for the compute pass is mapping the data from the buffer, since we want the user to have access to the acceleration every frame to allow for integration with a physics engine for example. There is also the fact that the buffer size needs to be resized every time a particle is added, which is not great performance-wise.
I'm also not able to make Particular's demo run on the web using the compute shader version. From my investigation, compute shaders aren't really possible with WebGL. If that's really the case, I'm not sure if switching to compute shaders for this project is what you would want? Since that would mean it would require WebGPU. I was able to run WebGPU on Firefox nightly, and all the wgpu examples using compute shaders do not work ☹️.
I'm quite inexperienced working with GPUs so I'm probably making some mistakes, which would explain why I'm confronted by these things.
Edit: You can check the compute shader code here if you're curious.
I think you're right about nightly. From what the devs on WebGPU have told me, Chrome Dawn is further along than Firefox nightly. Might be better to test with that. I still want to incorporate WebGPU because you can fallback on WebGL easily or just have 2 versions.
As far as memory goes - I think there may be a way to "directly map CPU memory" to use in the GPU which is strikes a balance of performance and GPU latency. This may be worth looking into. Unfortunately my bandwidth is a bit limited but I know the folks on the WebGPU gitter/Zulip are knowledgeable and willing to help, especially Connor Fitzgerald from my experience.
I'll try to get it working on Chrome Dawn then, thanks for the heads-up! Regarding WebGL as a fallback, my understanding is that WebGL doesn't support compute shaders directly but as you said, having two versions is possible.
Directly mapping CPU memory sounds very interesting, I'll look into this. My implementation could highly benefit from something like this for performance!