Search Unity

Many Text Mesh Pro elements in a scene - what is a possible solution?

Discussion in 'UGUI & TextMesh Pro' started by konstantin_lozev, Apr 21, 2019.

  1. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    I am prototyping a game for Oculus Go. I have this scene:
    https://i.imgur.com/2mrhDNP.jpg

    I use batched instanced rendering for all nodes and all links and get > 1000 fps in the Unity Editor and no hiccups in my Oculus Go. The only gameobjects are empty gameobjects with only a spherecollider.
    This is reflected in the Performance Profiler:
    https://i.imgur.com/U64SjYT.jpg

    However, when I enable the child object of each node that is Text Mesh Pro element, the framerate drops to 230 fps with noticeable hiccups in my Oculus Go.
    https://i.imgur.com/NRROtd2.jpg

    I see that the Text Mesh Pro elements do batch well. However, the 1092 instances of the Text Mesh Pro script add up to more than 1.12 ms:
    https://i.imgur.com/zblrCEg.jpg
    Is there a way to still get the great functionality of Text Mesh Pro without the need to have 1092 scripts running in parallel on the main thread?
    I know that disabling the Text Mesh Pro component depending on the distance from the camera is a potential solution. Another is building the numbers into a texture atlas, but neither of looked satisfactory.
     
  2. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    I suspect you are using an older version of TextMesh Pro as LateUpdate has been replaced by an UpdateManager.

    I would be curious to see the performance differences between those. All newer version require the use of Unity 2018.3 or newer and available via the package manager.

    If you are using a Legacy release of TextMesh Pro (Asset Store), you will need to migrate to the newer package manager releases and use the Project GUID Remapping Tool to convert the project over to the new format. Please be sure to follow the instructions in this sticky post.
     
  3. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    Thanks, will try that out. Indeed, I think I use an old version of TMP. I am also curious whether I will notice good improvement.
    The rest of the scene is really performant using the batched instanced rendering. I really wish TMP elements could benefit from batched instanced rendering too. Also, as far as I understand it is currently not even possible to use Hybrid ECS with TMP. Is my understanding correct?
    I watched this very inspiring talk
    and I think even though I do not use ECS, my code generally follows those guidelines (it is necessary in order to use batched instanced rendering).
    Would it not be possible to have a single TMP script and to use batched instanced rendering or (hybrid) ECS?
     
  4. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Static text objects should have no real performance impact other than LateUpdate() being called on older versions.

    You should test with the newer versions of the product.
     
  5. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    I did thorough testing today with the newest version of TMP and Unity 2019.1.
    I have grouped the results in pairs showing the Editor with the Stats window and then the Profiler.
    There are 4 scenarios:
    1. All 1000 nodes with disabled TMP component, camera is static
    2. All 1000 nodes with disabled TMP component, camera is rotating around the grid
    3. All 1000 nodes with enabled TMP component, camera is static
    4. All 1000 nodes with enabled TMP component, camera is rotating around the grid
    Here are the screenshots for each scenario:

    1. All 1000 nodes with disabled TMP component, camera is static
    Editor: https://i.imgur.com/EYnzWlz.jpg

    Profiler: https://i.imgur.com/EbRcW01.jpg

    As you can see, I get 620 fps in the Editor. I am not very familiar with the Profiler, but what I can gather from it is that the scripts (the blue columns) take 1.3ms and the rendering takes 1.2ms. I think that is a great baseline.

    2. All 1000 nodes with disabled TMP component, camera is rotating around the grid
    Editor:https://i.imgur.com/4dvFZ33.jpg

    Profiler: https://i.imgur.com/Syib0Er.jpg

    The scenario is that the camera orbits around the grid and each node (consisting only of a sphere collider and a disabled TMP component) is constantly facing the camera (billboarding). Here the framerate in the Editor drops to 200fps. The billboarding of the sphere colliders takes 0.88ms from the physics system, the scripts take
    1.4ms (mainly for a transform.LookAt loop). There are also some functions like UpdateRectTransform (0.28ms), PlayerUpdateCanvases (0.44ms) and UpdateAllRenderers (0.4ms). All those add up to 4ms. Rendering is only 0.4ms. So overall we jumped from 2.5ms with static camera to 4.4ms with rotating camera. I intend to separate also the sphere colliders from the TMP components so that there would be no need for billboarding of the sphere colliders. I can also optimise the billboarding part of my script with the new job system.

    3. All 1000 nodes with enabled TMP component, camera is static
    Editor: https://i.imgur.com/2MsHmAB.jpg

    Profiler: https://i.imgur.com/9p1mGp5.jpg

    In this scenario the camera is again static, but all the TMP components that are children to the sphere colliders are now enabled and as children of the objects with sphere colliders they also always face the camera. The framerate in the editor is 231fps. In the Profiler we can see that the scripts (on the left) take 1.2ms, identical to the case #1 when the TMP components were disabled. However, on the right the rendering takes 3.5ms, more that 1.7ms of which is from a new load TMP_UpdateManager.DoRebuilds() that consists of TextMeshPro.InternalUpdate(). So more than double the render time due to that TMP script, the total time is 4.7ms.

    4. All 1000 nodes with enabled TMP component, camera is rotating around the grid
    Editor: https://i.imgur.com/dBXY7oc.jpg

    Profiler: https://i.imgur.com/RHYi1Qc.jpg

    Here the editor runs at only 80 fps, which translates to sub-40 fps on the Oculus Go. Here both the inefficiency of my billboarding script shows in the first column where the physics system spends time on the transform of the spherecolliders and the actual billboarding function based on transform.LookAt, combining with the UpdateRectTransform (0.28ms), PlayerUpdateCanvases (0.44ms) and UpdateAllRenderers (0.4ms) to 5ms. However, the rendering part is even worse at 12.4ms. On the rendering part most of the time is pent by the TMP_UpdateManager.DoRebuilds() that consists of TextMeshPro.InternalUpdate(). So overall 17.4ms when the camera spins around the grid with all TMP component enabled.
    Here is a link to the Profiler data in a zip file https://drive.google.com/file/d/1vuYp40eJ9VQ_kwTUOs2OVIle6VlADRi4/view?usp=sharing
    So, to conclude, I have some work to do on optimising my billboarding script, but even if I optimise it well enough, the function TMP_UpdateManager.DoRebuilds() will still be eating close to 10 ms.
    Is there a way to lower that load, or shall I resort to culling?
    Here is how the culling looks like in Oculus Go:https://i.imgur.com/Si84N3g.jpg

    So even with visible culling I do not reach 60 fps...
    Another option that I tried and did not like was with texture atlas that has the number built in, but it was too fuzzy in VR https://i.imgur.com/eDHInWt.jpg
    (that's why I like Text Mesh Pro for the crisp image)


    Thanks in advance.
     
    Last edited: Apr 22, 2019
  6. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    This callback is only needed to handle potential SDF Scale changes that would be required is the scale of the text object / parents ended up changing. So if the scale of these text objects will not change, comment out the following line of code in the OnEnable() of TMPro_Private.cs and then re-run the relevant tests to see the change.

    Comment this line of code for testing.
    Code (csharp):
    1.  TMP_UpdateManager.RegisterTextObjectForUpdate(this);
    Also keep in mind that profiling can add significant performance overhead, so make sure you test the frame rate in a release build. The profiler is great to provide insight on relative costs / performance of different functions / systems and where you should focus to get the best potential performance improvements but when profiling in the Editor especially deep profile that all these numbers will be much slower than reality.
     
    Last edited: Apr 23, 2019
  7. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    Perfect! It worked! Now my script without the jarring culling actually works faster than with the culling on!
    And the problem was a real one, here is how it looked without the fix (40 fps in VR is a no-no)
    https://i.imgur.com/zmjSWmM.jpg
     
  8. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Was that in a Release build?

    and how is it now with the change I suggested?

    P.S. I also have one more change to test but it will take me a few days to go to it as it would involve testing a new shader that is using Screen Space Derivative and would not require SDF Scale.

    P.S.S. This is something I could actually test on my end if you don't have the time if you could submit a bug report and include the project.
     
  9. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    EDIT: The TMPro_Private.cs seems to "rebuild" itself and the line is no longer commented out, so the performance is again low. The rebuilding happens whenever I reload the project. How can I prevent it from rebuilding?
    That screenshot was with the old version of TMP with LateUpdate(). However, without commenting out that line, even with the new TMP the framerate went down to 40 fps with no culling. I had to do very jarring culling for any TMP Component that was further than 6 units (only!) to get to 58 fps.
    Now with the commented out line I got between 58 and 60 fps in Oculus Go. I am certain that I can get that to 60 fps with some C# job optimisation for the billboarding script.
    Here is how the Editor and Profiler look with the line commented out:
    Editor:
    https://i.imgur.com/HkJXM2Y.jpg
    Profiler - notice how the rendering takes only 1.7ms and the main load is my billboarding script that I am certin to be able to optimise with the Unity's C# Job System (would have been great if I could use ECS with TMP elements, but I think jobifying it would be enough):
    https://i.imgur.com/LgUYrLD.jpg


    And here is how the Profiler looks with the original script without commenting out the line:
    https://i.imgur.com/0bIwg11.jpg

    There is a whole 10ms taken up by TextMeshPro.InternalUpdate().
     
    Last edited: Apr 23, 2019
  10. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    This is because the global cache of the package keeps overwriting the local cache in the Library folder. You will need to make the changes in the global package cache "...\AppData\Local\Unity Cache\cache\packages\packages.unity.com"

    With the line of code commented out, the InterlaUpdate() will not happen and assuming your text objects are static, there should not nothing being added by TMP. The only impact would be the rendering of the text objects which is shader related. Make sure you are using the Mobile Distance Field shader and not the full distance field one.
     
  11. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    I opened the file in the AppData folder C:\Users\konst\AppData\Local\Unity\cache\packages\packages.unity.com\com.unity.textmeshpro@1.2.4\Scripts\Runtime
    But it is different from the file in the Library folder and does not have the line
    Code (CSharp):
    1. TMP_UpdateManager.RegisterTextObjectForUpdate(this);
    Here is a link to the TMPro_Private.cs from the AppData folder: https://drive.google.com/file/d/1jlF4Rgx44UT6keATJ8bXVUb88H-G9sG2/view?usp=sharing
    Here is the link to the TMPro_Private.cs from the Library folder: https://drive.google.com/file/d/1pPAqsPNgFbuD0XBtkvYU_BBAjkTCPAdd/view?usp=sharing
    And here is a pdf comparison between the two: https://drive.google.com/file/d/1LqFtsR_GxbBC9FGMNSCIMWMd_HVrvEzs/view?usp=sharing
    This is quite strange. I hope you could help.
     
  12. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    What version of TextMesh Pro are you using? What does Package Manager say you are using?

    If Packager Manager say version 1.4.1-preview.1 (which is the latest for Unity 2018.3), then the global cached version will located in the following folder "com.unity.textmeshpro@1.4.1-preview.1"
     
  13. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    Ugh, now I feel stupid... I had 2 folders:
    com.unity.textmeshpro@1.2.4
    and
    com.unity.textmeshpro@2.0.0
    The right file was in com.unity.textmeshpro@2.0.0. Changed it and now the project does not revert to the old version, so the text is commented out even if I restart the Editor.
    Awesome, thanks! I consider that solved!
     
  14. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    BTW: What shader are you using on your text objects?
     
  15. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
  16. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    BTW, are there any plans to enable at least hybrid ECS for TMP?
     
  17. Stephan_B

    Stephan_B

    Joined:
    Feb 26, 2017
    Posts:
    6,595
    Not in the short term as I am focused on getting the OpenType features and other improvements in first.
     
  18. konstantin_lozev

    konstantin_lozev

    Joined:
    May 8, 2015
    Posts:
    99
    OK, cool, I am very grateful that Text Mesh Pro exists anyway, since in worldspace the default Unity text elements are a blurry mess. What is the main hurdle in enabling at least hybrid ECS for TMP? Is it that it uses a RectTransform instead of the standard 3D object Transform that is already accessible to ECS?