Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Canvas.BuildBatch issue, killing performance quickly and effectively.

Discussion in 'UGUI & TextMesh Pro' started by VipHaLongPro, May 13, 2015.

  1. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    I have a requirement to keep about 1500 Texts out of camera view and show some back at any time. They should be ready because SetActive is expensive, transform.SetParent is also expensive, Destroy and Instantiate are even more expensive. I don't have a choice to use a global pool for all the Texts. Because then I have to use transform.SetParent every time showing a Text above enemy. In fact each enemy has a Text (animated Text) above. So each enemy has a separate pool. The frequency of showing Text is fairly high.

    So when about 1500 Texts out of camera view are placed everywhere possible, each small move in the camera view (when the enemy moves or any other possible moves) can raise the so-called Canvas.BuildBatch and it kills the performance so quickly and effectively. Per my test, when the enemies are moving, I don't let any weapons shoot it, just keep them moving. Then the framerate drops from 70 to just about 30 (really effective performance killer). Note that all the Texts are out of camera view and in static state (no animation). When all the enemies have moved over, the scene becomes static, the framerate increases up back to ~70 immediately.

    Of course I used profiler to know what's killing performance and it's the so-called Canvas.BuildBatch:



    So the question is is there any chance for me to keep those 1500 static Texts alive without being effected by Canvas.BuildBatch? I did not think 1500 static Texts out of camera view could cause any issue due to just some change which might not involve UI at all (I mean the moving enemies). So looks like the UI design is not very good at that part? or that's simply impossible to improve more? I'm currently using Unity 4.6.1f if that matters.

    Thank you all for your supports.
     
  2. Yukichu

    Yukichu

    Joined:
    Apr 2, 2013
    Posts:
    420
    1. You said animated texts. Do you stop the animation when they're off-screen? Have you compared with all animations off?

    2. More than once you refer to them as static texts. Are they marked as static? You are moving their positions and they are marked static? Should they be?

    3. Why can't you pool them? I pool my texts that are used above objects / player heads. You can change color, font size, offset position, style, etc. on the fly.

    4. When they go off-screen, do you just disable the text component? You don't need to disable the whole gameobject.
     
  3. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    1. The animations should stop because they're not looping and the duration of animation should pass over (just about 0.6 seconds). Do you mean I have to stop it manually even the duration has passed (and it has come to Idle state, it has just 2 states: Idle and Show). Also I tried stopping it with Animator.StopPlayBack() but it has no effect.

    2. When saying static, I mean its animation is not playing (not the static check in the Inspector). I don't think this could be an issue.

    3. I think you misunderstood this. I use pool but not just one pool, I use multiple pools, each one for each enemy. As I said a global pool may reduce the number of alive Texts but then I have to use transform.SetParent to reparent the Text every time showing it. However as I've experienced, transform.SetParent with a high frequency (about 1 animated Text shown per 0.03 second) is expensive enough.

    4. Well I did not try this but I don't believe in its magic. I just cannot understand why 2000 static Text (or even higher) can be rendered OK normally but if there is any thing changed in the scene (moving, text changing, ...) the framerate drops immediately to 20-30 due to Canvas.BuildBatch. That should be a bug or limit in the current UI design.

    Finally thank you for your support.
     
  4. Yukichu

    Yukichu

    Joined:
    Apr 2, 2013
    Posts:
    420
    I see, misunderstood some things. Yes, there are performance issues with the new UI design especially when it is not set up correctly, or some of the nuances are unknown.

    I'd say post a bug report.

    It's Canvas.BatchSort that's using all the resources according to your screenshot. Are you using perspective camera? Sorting layer? What kind of screen space? Screen overlay? Worldspace?

    Why do you have to reparent the text every time you show it? I don't understand why, when enemy is created, you can't assign it a text object from the pool, and when enemy is dead, remove it from the pool? Check on lateupdate is the text is out of screen bounds, and if so, disable text. Enable text when it returns. Not sure. 1500 sounds like a lot. Maybe it's not. Can't really say much more without more details, code snippets, etc.
     
  5. iivo_k

    iivo_k

    Joined:
    Jan 28, 2013
    Posts:
    314
    Put the texts in Canvases of their own. That means one draw call per text, but it also means the UI doesn't have to rebuild all the text meshes when one of them changes.
     
  6. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    @Yukichu
    I think it's some kind of limit in the current design, not really a bug. The Canvas has render mode of Screen Space Overlay. There is no sorting layer defined. I used 3 Canvases, one for the ingame GUI. One contains all HealthBars and the other contains all animated texts (some kind of damage texts). The 2 canvases containing healthbars and texts are non-interactive (because they have just a Canvas component, no graphics raycaster ...). Those 3 canvases have different Sort Orders so that I can control which overlap others (the main ingame UI should cover the texts, texts should cover healthbars). That kind of canvases organization in fact helps improve the performance considerably. (as before when I used just 1 Canvas, every movement of my mouse on the interactive UI can raise the Canvas.BuildBatch and drop framerate to 20-25). So this should not be the issue.

    Thanks to you mentioning about disabling Texts, I've tried disabling them and even disabling the Animators, now the Canvas.BuildBatch has less effect (but not totally eliminated). When shooting (texts are played), the top framerate consumers now are Camera.Render, Canvas.SendWillRenderCanvases, Animator.Update. But when the number of generated Texts are large enough, the Canvas.BuildBatch is still on top, it still consumes about 35 - 40%. I think it's hard to improve more. It's some kind of limit in rendering text font and the Animator itself. I'll try not using Animator and using my own script to animate it. I don't really think it may be better. But before doing that, I'll try to improve it more. Using Animator has its own benefits, e.g: changing the style of animation is easier with curve edtior ...

    For your question about reparenting the Texts. That's because if some Text has been parented to some panel and pushed back to pool, what if that is popped out and used for another panel? In fact each enemy has a panel (which will track enemy's screen position). This panel contains all the animated Texts belonging to the enemy. Also animated Text using AnimationClip should have a panel so that you can move it around on screen without breaking the animation (I added a curve to animate Anchored position, this is local and relative to the parent) . So I have only 1 choice to move the panel tracking the enemy position. It may be even hard to update position of being-animated Texts so that they follow enemy without using any panel. BTW my game is a 3D game, just a simple 3D tower defense game. I've never seen any similar game (both 2D and 3D) out there trying to show animated damage texts with such a high frequency like the one I'm trying to make now. But at least I know more about the new UI system when trying to do that :)
     
    Last edited: May 13, 2015
  7. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    As I understand I did exactly what you said. All texts are placed on 1 Canvas. All the other UIs have their own Canvas (see my post above about 3 canvases I used). And in fact it helps improve performance considerably. But it cannot eliminate Canvas.BuildBatch when the Text and Animator are still enabled.
     
  8. Ferazel

    Ferazel

    Joined:
    Apr 18, 2010
    Posts:
    513
    Last I checked canvases are still rendered even if they are not on screen.

    I think we're trying to tell you is that when a canvas has a lot of renderers, as soon as you move one of those renderers the canvas needs to perform a sort/build on every renderer that canvas is responsible for. So if every label is given its own canvas only canvases that move/animate will need to batch/redraw. So in this case you would need 1500 canvases and then when you move the individual canvases you will not incur the same performance penalty of batching and sorting.

    However, I'm not convinced that your use case really needs to have 1500 labels. Is there ever going to be a situation where you have all 1500 labels on the screen at the same time? If so, you may want to rethink the design. If not, you should be able to dynamically set the text and use a pooling mechanic.
     
  9. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    Adding Canvas for each Text can really help improve performance? I thought Canvas is something costly and it is just some kind of top level UI containing others. In fact 1500 texts won't never show on screen at the same time. I hide them out of camera view and need them to be alive (or ready) so that I can show any one of them as quickly as possible (no cost for SetParent, SetActive, enabled, ..., only transform.position and Animator.SetTrigger - these 2 ones are fairly less costly and acceptable per my profiler result). Well I'll try adding Canvas as you suggested.
     
  10. Ferazel

    Ferazel

    Joined:
    Apr 18, 2010
    Posts:
    513
    Adding a canvas can help break up the batching issues involved with sorting the dynamic/changing UI to determine depths. There is a canvas overhead associated with each in the scene (an additional draw call).

    However, I don't think that having 1500 labels around that you don't use is helpful at all, and will muck up performance in other ways than just the canvas sorting. I'd really be surprised if setActive gave your worse performance than sorting 1500 labels every frame of animation. If you really need to keep the labels active, just have a pool of active labels. Say 20 labels or how many need to be displayed in a general use situation and then set the text of the label via code. Then moving the label to the on-screen position it needs to be. Canvas sorting 20 labels is going to be a LOT faster than 1500.
     
  11. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    Well I've just tried using each Canvas to host each Text (instead of a simple panel with Image component as before). Now looks like Canvas.BuildBatch is eliminated more (together with disabling Text and Animator). I talked about an issue when the Texts are not being played. Another issue is while it's being played the Canvas.SendWillRenderCanvases becomes the top CPU consumer. Looks like it's hard to improve it more. Thank you.
     
  12. VipHaLongPro

    VipHaLongPro

    Joined:
    Mar 8, 2015
    Posts:
    49
    Well you can think about the scenario of a Tower Defense game. At the same time there may be about 5 or 6 enemies shot with fast reloading bullets. Each enemy can have about 30 texts above at the same time. So 20 is not enough. Also as what I've experienced, SetActive Text is fairly expensive. Maybe I'll try double checking it with the old solution I tried before. I think the current performance is faster when shooting but slower when stopped (due to Canvas.BuildBatch) - compared to the old solutions I tried. I'm really stuck to choose which one.
     
  13. QI

    QI

    Joined:
    Oct 27, 2012
    Posts:
    229
    I just faced the same problem, I am pooling the text and now it only have about 3 text element in the scene and showing repeatly.

    But the Canvas.BuildBatch is really a nightmare, my texts is animated, when the text is playing animation, it cost about 40% CPU and suddenly reduces the frame rate on iPad 2 to 10 FPS, although iPad 2 it kind of old, but I want my game to support it.
    Now I added a CanvasGroup to each text and mark "interactive" and "block raycast" to disable since player don't have to click the texts, it decrease the cost of Canvas.BuildBatch for a small amount but it truly works.

    Just wondering if there is any thing can help to workaround this.
    Say can we just cancel to batching? Just give them to GPU and let GPU render them as normal...