Testing performance between UI Toolkit and UGUI

Hello,

I’ve been trying to test the performance changes between UI Toolkit and UGUI. I’m loving the workflow of UI Toolkit. So far I’ve heard that the UI Toolkit is better performance wise and I wanted to test that. After testing two identical (as far as possible) UIs the profiler showed that UI Toolkit took way longer than UGUI to do the same thing.

I made two UIs that “spawn” in 100 buttons in. For UGUI with Instantiate. For UI Toolkit with CloneTree. When turning these UIs off and on in code, UI Toolkit takes 2 times as long as UGUI currently. The UGUI UI I turn the parent Panel off with gameObject.SetActive() and the UI Toolkit UI I turn the parent VisualElement off with panel.style.display = DisplayStyle.None.

For both tests I tested in Editor and in a Build, it’s relatively the same. The screenshots are from the Editor tests.

What could be the reason that UI Toolkit is slower?


The UGUI UI.

The UGUI Profiler.

The UI Toolkit UI.

The UI Toolkit Profiler.

The versions I’m using are the following:

Unity 2021.1.0b6.1892
UI Builder 1.0.0-preview.12
UI Toolkit 1.0.0-preview.14

5 Likes

We are currently looking into ways to improve the performance when going from display:none to display:flex, as this use-case currently regenerates the whole geometry from scratch.

A simple workaround is to set the opacity of the parent element to 0 instead of setting display:none. This will keep the rendering hierarchy intact and will simply skip the rendering of that tree.

EDIT: Note that “display:none” and “opacity=0” are not always equivalent, as setting the display:none value will impact the layout.

1 Like

Hello, thanks for sharing this with us.

There also seems to be some unnecessary allocations or re-allocations of text data. We may consider this a bug, I’ll post back here

Thanks

Really interesting, thank you for bringing it up!

One reason why the text allocation is so big is that the TextInfo struct was duplicated in Preview-14 because we couldn’t have a direct dependency on TextCore. This is now fixed in our development branch and will be available to users in Preview-15.

However, there seems to be a deeper issue here. We shouldn’t be regenerating the TextInfos after a change in display. We’ll investigate and keep you updated.

2 Likes

Thanks, I will try the workaround and see how that changes things! As a clarification, my use-case is that I want to turn panels on and off similar to how you do it in UGUI with gameObjects or the equivalent of that in UI Toolkit. What would be the best approach to turn parts of the UI off and on in UI Toolkit, I’ve noticed there are a couple of options:

  1. DisplayStyle
  2. Opacity
  3. Visible
  4. SetEnabled()

Thanks for the quick replies!

Changing the opacity is the fastest option at this time in most situations. Followed by changing the visibility. I think changing the display value is similar to enable/disable (both are slow right now).

1 Like

Good to know, thanks for the quick reply!

I tried using “panel.style.opacity = 0” for making it invisible and used “panel.style.opacity = 1” to make it visible again. UI Toolkit still seems to be slower than UGUI. To be a bit more specific how I implemented the buttons in UI Toolkit: I have a VisualTreeAsset in code that is a SerializeField which I set through the editor. Then to spawn in each button I call “visualElementParent.Add(buttonReference.CloneTree());”

Could this be related to UI Toolkit or to how I initially “spawned” in the buttons in UI Toolkit?

6864431--800738--UIToolkitPerformanceOpacity.png
Below is a screenshot of the profiler when using opacity.

How you create the element should not matter in this case.

What’s odd is that we can see a lot of time is spent regenerating the geometry (TessellateRect/Border). This shouldn’t happen when only changing the opacity.

I’ll make a few tests and come back to you.

1 Like

SetEnabled() on VisualElement doesn’t have the same effect than on GameObjects. SetEnabled will toggle the enable pseudo state and will prevent some events to be dispatched to the elements. They’ll still be visible (think of a disabled text field)
Changing the enabled flag on the UIDocument component is the same as doing Add(x)/x.RemoveFromHierarchy() which will be the slowest of all options.
To your list, I would add another one, event if it might not be the cleanest one:
5. setting usageHint.DynamicTransform and setting ve.transform.position = new Vector3(-10000,-10000, -10000)

To be honest, showing/hiding large parts of the ui tree is problematic performance-wise at the moment and it clearly should not. We’ll improve this.

3 Likes

What happens with tied to the element events? Just because window isnt visible does not means cripts arent active, right? It still will receive event, and process coroutines if any?

I had a look. It turns out that we consider opacity transitions from zero to non-zero in a similar manner as style.visible = true. So the performance isn’t great. Going almost transparent (instead of fully transparent) doesn’t show the same performance issues.

Thanks for reporting, we’ll provide more efficient visibility changes in the future.

3 Likes

Sorry for bringing this topic back up again, did you find what would be the most performant approach to hide elements visually and event wise (so clicking on it does not interact with them and neither bother anything below)?

I am asking this because we use a lot the display flex none approach. Both to display and hide modals entirely and both to change dinamically the view in the open panels.

Example:
You have a modal open with on the left a list of toggles and on the right a container. When the user click on each toggle it will display none the open tab on the right and will display flex the tab requested.

Thanks

From a rendering perspective, setting the opacity to 0% is the most performant way to hide an element. Unfortunately, even at 0% opacity, the element will still respond to events. So, setting the opacity to 0% and moving the element out of the way (by changing its position) may be the most performant solution. When doing that, make sure the element has the DynamicTransform hint set, so that moving the element will be a cheap operation.

We will provide performance improvements in the future to be able to change the visibility and display styles more efficiently.

1 Like

Thanks for the explaination, I will work on that. Please keep us updated on the performances improvements, it was so easy to implement as a flow.

How does UI toolkit performance compares to ugui at this point?

1 Like

Did some reading of the reports and it seems like uGUI, followed best practiced in depth, beats it by a far.
UI Tookit can still buy you with its extensibility and ease of use

2 Likes

The problem with using “opacity” or “visibility” instead of “display” for hiding elements is that they still occupy their space in the layout.

What I realized is that creating new UI elements during runtime is much slower and causes lag spikes in WebGL.
So what I do when I have dynamic lists is that I spawn enough at startup and then reuse them as the list changes. Like a pool.

2 Likes

That’s unfortunate. I really have high hope that UITK would solve the performance problem of UGUI. I have seen old threads comparing UITK and UGUI and UITK always beat it. What happened? :(:frowning:
I have not use UITK personally since it lacks the critical features our company app need (Emoji support, variable height list view, custom shader & ui particle).

In my experience:
During animations there are a lot of vertices generated and Events fired
Creating UI elements creates a lot of garbage that cause lag spikes

1 Like