Search Unity

[Cloth] Cloth performance degrades over time Unity 2018.4.4-2019.2.0

Discussion in 'Physics' started by AndrewRH, Jul 31, 2019.

  1. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    Hi @SeanParkinson, @yant

    Our app Instantiates many skinned characters that are wearing clothes. We only need 6 on screen at once, but we need to display from a pool of 60. To help with performance we tried to enable/disable the characters when they were needed - but this caused massive frame spikes. We tracked it down to the Cloth component. When we enable the cloth component the profiler says it's taking ~30ms per component. This in itself is a shame....it would be great to be able to disable/enable the cloth component without there being such a massive CPU hit.

    We then decided to not enable/disable the characters, but instead keep them offscreen so they would be culled. We also disabled their animation when offscreen, hoping this would make the Cloth processing much lighter. Initially this seemed to work great, but for each character that became enabled (when moved on screen for display), and then disabled (when moved off screen), the Cloth processing in the Profiler seemed to increase. Even though at the end we were still only displaying 6 characters, the CPU load was very high.

    It's as though once the SkinnedMesh/Cloth was used, even after disabling it it would continue to take CPU time.

    In the profiler Physics.UpdateCloth would take 10ms to start with, but it would grow and grow until each of my cloth meshes has been displayed once, and then it would take around 30ms per frame - this is the same amount as when all the clothe objects are simulated at once.

    upload_2019-7-31_16-10-16.png
    It seems be spending more and more time in WaitForJobGroupID, so I wonder if the cloth components that are off-screen are not being removed from some sort of job pool?

    I believe this is a bug, but I fear nobody will investigate even if I take the time to create a reproducible scene.

    If I submit the bug with a reproducible scene would someone take a look? Anyone have any ideas about this?

    We are hoping to solve this in the next few months for the delivery of a very cloth-heavy application.

    Thanks,
     
  2. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    Here is a more detailed example, I have a character model with 3 skinned+cloth meshes. I instantiate them off-camera so there are 64 instances.

    If I then move 2 of them in-camera, ClothUpdate will take about 7ms
    If I then move all of them in-camera, ClothUpdate will take about 100ms
    If I then remove all of them and put just 2 back again, ClothUpdate will take about 30ms.

    I can't now have 2 characters on screen without ClothUpdate taking 30ms, until I go through all Cloth components (even the ones that are now offscreen and shouldn't be updating), and disable+enable the Cloth component...Then ClothUpdate returns to 7ms
     
  3. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    Now with pictures :)

    I render 2 of the 48 character instances and ClothUpdate takes around 5ms:
    upload_2019-7-31_20-24-43.png

    I then render all 48 instances and the ClothUpdate takes 60ms (to be expected):
    upload_2019-7-31_20-26-5.png

    Now back to 2 instances, and ClothUpdate takes 25ms :(
    upload_2019-7-31_20-26-48.png

    This is really affecting the performance of our app. I will submit this as a bug shortly, but if anyone has any thoughts/insights I'm happy to chat about this and try to find a solution.
     
  4. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    Bug report submitted,
    "(Case 1173457) Cloth performance degrades with use"
     
  5. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
  6. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
  7. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    500
    I can't reproduce the issue you're describing... I added several planes with cloth components and measured the performance, then I disabled them and saw that the Physics.UpdateCloth method you listed did decrease operation time as expected. The performance was the same as when they weren't there before, and I even had several other cloths that were always active to make sure UpdateCloth was running. How are you disabling the cloth? Just by toggling the cloth component active through scripting?
     
  8. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    Yeah, if you disable the cloth components - then it's fine - they don't do any calculations.

    I'm trying to disable the cloth update via moving the character off-screen so that it is culled. Initially I spawn the characters offscreen, and then I move them on screen, and then offscreen again. When they're offscreen, the performance does improve, but not to the same level as before they were brought on screen.

    I've uploaded my repro case here (exported from Unity 2018.4.4).

    How to reproduce:
    1. Open the project
    2. Enter Play Mode
    3. Set the "Active" Cloth slider to 2 > observe FPS
    4. Set the slider to 48 > set back to 2
    Expected result: FPS value is the same as it was in step 3
    Actual result: FPS value decreases twice

    Thanks,
     
  9. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    500
    Okay, I tried it out and I see what you mean. The FPS did not return to how it was before the objects were moved. But I feel like that might be by design, there's tons of weird quirks with rendering that could cause this. My guess is there's some sort of buffer/memory space/function consideration that only adds cloth vertices when they're on-screen, and then are still considered even after they move offscreen. Your skinned mesh render has the "update while offscreen" checked, so I checked what would happen turning that option off and it had no impact on the FPS, it still stayed low. My guess is that it only impacts the bounding box and not the cloth sim, so there must be some other consideration there.

    I noticed you are disabling the skinned mesh renderer when they move offscreen though, so maybe it has to do with the fact that cloth only starts to work once the renderer is enabled, then it is considered even while the renderer is off, regardless of where it is onscreen. You'd have to check in with a Unity dev on this because I'm not sure what the intended behavior is, but I feel like UpdateCloth is supposed to always run even while it's offscreen.

    So going beyond the "bug" for now, I can suggest a few things to help improve your performance that don't involve fundementally changing how UpdateCloth should run with a patch/update.
    1. Find a way to disable the cloth components. In my opinion this is the best way to deal with the problem because it completely eliminates items you don't need. I don't see how disabling/enabling the component could cause that much of a lag spike compared to what you're running already, but maybe you have a ton of vertices or many characters that do it at the same time. Or maybe you have constantly running enable/disable code that doesn't need to be running all the time. If you shared a sample of what you tried with this it might be more help. If there's nothing odd with how you do it, then maybe you should reconsider your strategy. Some long operations can be split into multiple ones using Coroutines and the yield statements. This'll help improve the performance of one-time functions such as enabling/disabling many components.
    2. Find other ways to optimize the performance of the cloth component. I noticed you have sphere colliders which are considered, so maybe finding a way to reduce how many you need will help performance. Also optimizing the polycount of your model will help, especially when that model is duplicated many times. Reducing unneeded polys will absolutely speed up your cloth rendering. In addition, you can reduce the number of cloth considerations per second with the "solver frequency" option. I often find I don't need the default 120 solutions per second so I can safely reduce them to 90 for a bit of performance.
    3. Find a way to substitute cloth rendering for a faster solution. Maybe you don't actually need that many cloth components per character? Maybe you can get away with an animated bone chain to simulate jacket motion, or a hinge joint to automate a scarf, etc. I'm just throwing out ideas here but if you're getting close to release and need to fix, sometimes it's better to make sacrifices and come up with an alternative solution. I'm sure you could more efficient cloth renderers online that approximate vertex positions instead of considering every single one. Look up Final Fantasy 15 development for cloth and you'll see how they managed to do it.
    I've voted for your issue tracker submission so that a dev can take a look, but I have a feeling the behavior is not going away any time soon as it seems to be by design. Let me know if you have more questions and I'll try to help as best I can. Good luck with your project!
     
    AndrewRH likes this.
  10. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    Glad you could replicate the issue :D

    Yeah..but when it is Instantiated off-screen, it doesn't contribute to the cloth sim cost.. Only once it has been brought on-screen, then that cloth simulation cost seems to partially remain when it returns to off-screen. A single character is taking say 4ms for cloth, but after a bunch have been displayed, then that same single character is taking 25ms for cloth. It seems that it is doing extra cloth work for nothing.

    Yeah, my use-case is not usual. I'm not making a game. This is for a high-end rendering event. There is no loading screen or menu or anything to hide frame glitch issues. We are building it to run for hours and it can't drop frames while running. I've tried splitting the cloth enable over multiple frames (with and without co-routines), but our cloth meshes are dense, so each one takes around 16ms to enable, which results in a dropped frame. When your display is 12 meters wide, a dropped frame is VERY noticeable :)

    I tried dialing back the solver frequency, but this doesn't seem to change much. The collider and vertex count of our models can't be changed much. We've already fine tuned them to get the best balance of performance vs quality. In our benchmark we can simulate and render with 10 characters on screen and hit 60hz, but it's just these offscreen characters that are slowing it down :(

    Thanks or your very in-depth investigation into my issue :) Do you work for Unity or something? I'm just curious why you would offer so much help :) Thanks again for all of the suggestions.
     
  11. SomeGuy22

    SomeGuy22

    Joined:
    Jun 3, 2011
    Posts:
    500
    Yeah that can be confusing... but obviously the simulation is more complicated than either of us would know so it's hard to say whether there is actually a "leak" there. Maybe having more cloths on screen slows down the processing time of each individual cloth? Maybe once a cloth comes on screen, data is added to start processing but is kept there to keep the cloth folds consistent even while offscreen--if there's wind or something that moves it offscreen it would need to be in a different position for when we look back at it. Idk I'm just guessing here, but all I'm saying is maybe it's just built to work like that and can't be easily changed.

    Ah I see, then yeah that would make things more complicated. Perhaps another solution you can try is splitting the mesh into even more pieces if possible and then having a cloth component on each one. That would allow you to enable/disable over even more steps, though it might hurt onscreen cloth performance when more components are running. But maybe that's a sacrifice you're able to make, I'm not sure.

    Does each cloth enable/disable directly cost 16ms, or is that for the entire piece of logic for each object? For example, if you use GetComponent to access your cloth when you enable it'll slow down even worse than if you cached that value. It's also possible that if you have a long chain of references to get to your cloth that it'll slow worse than if it's just a local variable.

    In computer science we have a term "asynchronous" which you've probably heard of. It's for operations that take as long as they need to over time which don't halt the rest of the program. Coroutines aren't actually asynchronous because they run on the main thread and you must yield them to break out. But imagine if you were able to run the cloth enable/disable operation on a different thread, then you might be able to limit how much CPU is used and make it run over a longer time. I know there are several methods people use for this but I haven't done much research myself. Take a look at the C# Jobs system and the asynchronous await keyword. There are even other options such as the AsyncOperation class but I'm not sure if any of these will work with Cloth.enable because I've never tried it. Just something to look into.

    Hmm well that's a bummer. Sorry I'm writing these all the over the place but I have one more idea that could help related to this. It's possible your cloth sim enabling/disabling takes so long because it has to consider sphere colliders and many vertices. So this is sort of hacky method but maybe... before disabling you could take out the sphere colliders one at a time. This would allow you to use Coroutines to split the operation into even more steps. Likewise with the solver frequency and dampen settings--it's possible that less motion in your cloth could result in less overhead during this operation but that's pure speculation. Regardless, it's more steps you can perform before actually disabling the cloth which could improve the performance of the operation.

    Another idea is that maybe you can utilize LOD. When an LOD group is far away from the camera it gets culled depending on how you set up the percentages in the component. I think it only affects the renderer but maybe there's "Unity magic" there that effects the cloth. It's worth a try, you can even use it to swap out for less detailed models if there's ever a chance to do so.

    No problem! I don't work for Unity, I've just been using it for a long time (back in 3.0!!!) so I know all the little hiccups that come with it. I'm not an expert on the advanced stuff but I've had to solve a ton of optimization issues so I like to solve problems like this. Been having my own issues with cloth in this Unity version so I'm hoping everyone can help each other out and get these projects done. Helping whoever you can is the first step to a healthy community, and the first step to getting help in return :)

    EDIT: Just one more thing I realized. In my own experiments with Coroutines I've found that waiting 1 frame or a couple frames between code blocks is not enough to reduce lag. I found that by increasing the wait time between operations it actually makes the performance impact less noticeable, so maybe you can try that as well. Yield .1 seconds or even more, see if that helps.
     
    AndrewRH likes this.
  12. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
  13. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    @SeanParkinson were you able to replicate this issue? Any idea whether a fix might even be possible? ;)
     
  14. AndrewRH

    AndrewRH

    Joined:
    Jan 24, 2012
    Posts:
    2,592
    This issue is currently our biggest blocker... We have a lot of clothed objects but we can't get good performance due to this issue... Any updates?