methusalah / SplineMesh

A Unity plugin to create curved content in real-time with bézier curves
https://twitter.com/dumas181
MIT License
1.05k stars 104 forks source link

MeshBender and SourceMesh are incredibly slow due to recalculating normals #36

Open rogerscg opened 4 years ago

rogerscg commented 4 years ago

MeshBender is slow and causes significant frame loss when adjusting nodes. I ran a deep profile when bending a mesh of 1720 vertices and discovered that SourceMesh.BuildData() was taking ~300ms to run and updating mesh normals in a nested loop. I hoisted Mesh.normals out of the loop saw significant performance improvement without a loss of accuracy.

methusalah commented 4 years ago

Building the mesh data is indeed slow but mandatory I'm afraid as the normal of the vertices must be precisely bent for a correct illumination of the mesh in all situations.

What you may have missed is that this method is extracted from the very bending process, so it is not run at each frame. In fact, the data has to be rebuilt only when you change the mesh or one of its parameters, which is not supposed to occur very often, and certainly not each frame.

If you really need to build mesh data every frame, could you please describe your use case?

Le dim. 13 sept. 2020 à 18:36, Greg Rogers notifications@github.com a écrit :

MeshBender is slow and causes significant frame loss when adjusting nodes. I ran a deep profile when bending a mesh of 1720 vertices and discovered that SourceMesh.BuildData() was taking ~300ms to run and updating mesh normals in a nested loop https://github.com/benoit-dumas/SplineMesh/blob/0678d8a00cf7863e7cf58970c530cc9ad925056c/Assets/SplineMesh/Scripts/MeshProcessing/SourceMesh.cs#L130. I hoisted Mesh.normals out of the loop saw significant performance improvement without a loss of accuracy.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/benoit-dumas/SplineMesh/issues/36, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB5QKTV5WTJQPD7EH4PRBSDSFTYIFANCNFSM4RKYDYKA .

Novack commented 1 year ago

Hit a similar scenario, maybe my case helps as example @methusalah.

Im placing ropes from pole to pole. During the placement of new poles, player can move the aim around the environment to pick the destination, while seeing a dummy rope -originating on an existing pole- following the aim. This requires the mesh to be rebuilt every frame. I will check @rogerscg approach, because on the dummy rope I really dont need accurate mesh lighting.

Thank you for your work!

Edit: Actually, BuildData() can be called only once as Meth was saying, instead, is MeshBender.FillRepeatStretch() that is completely tanking my framerate, will look into it.

methusalah commented 1 year ago

The method FillRepeatStretch is as slow as the number of vertices in the mesh. The method can be drastically improved with a ParallelForEach or Unity's Jobs, as the computation on each vertices is independent and can be run simultaneously (mult-threaded). This will require to change the way the data is organized, though, but is definitely a good direction if you wish to have a faster update.

Another optimisation would be to send the computation to the GPU but this is far from being a trivial implementation, and has many disadvantage to consider, like the computation being one or more frame late, and the bent mesh not being readable on the CPU side, which is bad in a lot of situations (e.g. if you need to update a mesh collider).

If you go down the multi-threading route, I would be happy to take a look at the result or even implement it in SplineMesh.

Have fun bending things ^^

Novack commented 1 year ago

Turns out the mesh related works were only a part of the issue. A big chunk was consumed by the triggering of unrequired events (on this context) on nodes Position and Direction properties, so created a method for setting them with optional event calling. CreateMeshes() was very unoptimized so in my case was doing per frame stuff that could be made only once, including GetComponent() calls, reworked it. Also FillRepeatStretch indiscriminately allocated lists, producing constantly kicking-in of the GC, so moved those lists to pooling. I checked also some stuff happening at OnEnable at Spline and SplineMeshTilling classes that resulted less than ideal for runtime use, where you show/hide objects for gameplay reasons constantly.

This was all pretty quick and rough, but the result, even after the same mesh stuff done as usual (without the jobs rework) is drastically better. Overall, it looks like for runtime use the SplineMesh could take some improvements. If time allows, at some point I'll offer some PR that may alleviate the overall situation.

Again, thank you Meth!