UI Toolkit Runtime Bindings performance

Hi, I started using Ui Toolkit and bindings for all UI to have fewer constraints from code, but it does not seem performant.

I have around 100 elements in ScrollView. Each element has its own ViewData which implements IDataSourceViewHashProvider, INotifyBindablePropertyChanged to ensure no bindings updates are made.

Changing binding-mode from ToTarget to ToTargetOnce changes nothing.

Is there something I can do to reduce ShouldUpdateBindings cost?

Here is like my ViewData code look like for those elements

public class BindableProperty : IDataSourceViewHashProvider, INotifyBindablePropertyChanged
{
	private long viewVersion;

	public long GetViewHashCode()
	{
		return viewVersion;
	}

	public void Publish()
	{
		viewVersion++;
	}

	public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
	
	protected void Notify([CallerMemberName] string property = "")
	{
		propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(property));
	}
}

public class ItemGridViewData : BindableProperty
{
	public string itemId;

	public string level;

	public DisplayStyle notification;

	public Sprite itemSprite;

	public Sprite heroSprite;

	public ItemRarity rarity;

	public Sprite rarityFrameSprite;

	public DisplayStyle locked;

	public DisplayStyle wholePanelLockedDisplayStyle;

	public DisplayStyle wholePanelUnlockedDisplayStyle;

	public DisplayStyle displayStyle;

	public void SetData(
		ItemInstanceData item,
		HeroInstanceData equippedBy,
		ItemRarityConverter itemRarityConverter,
		bool wholePanelLocked = false)
	{
		displayStyle = item != null ? DisplayStyle.Flex : DisplayStyle.None;
		if (item == null)
		{
			itemId = null;
			Publish();
			return;
		}

		itemId = item.id;

		wholePanelLockedDisplayStyle = wholePanelLocked ? DisplayStyle.Flex : DisplayStyle.None;
		wholePanelUnlockedDisplayStyle = !wholePanelLocked ? DisplayStyle.Flex : DisplayStyle.None;

		level = $"Lvl {item.level + 1}";

		//TODO: display notification when?
		notification = DisplayStyle.None;
		itemSprite = Game.AssetsService.ItemsAssetsScriptableObject.GetItemSprite(item.GetBaseItemId());
		heroSprite = equippedBy != null ? Game.VisualEntityData.GetEntityData(equippedBy.id).mainSprite : null;
		rarity = item.rarity;
		rarityFrameSprite = itemRarityConverter.ConvertEnumToSprite(item.rarity);

		locked = item.locked ? DisplayStyle.Flex : DisplayStyle.None;

		Publish();
	}
}

Since the profiler graph is flatlining, it looks like updates happen every frame which is something you’ll want to avoid. The manual mentions the update modes.

I tested all binding modes and update triggers with no results in performance.

When I changed update triggers to Every Frame as expected UI is rebuilt every frame and is worst. However, checking for bindings still takes the same amount of time. Is there a way to disable it?

Far as I understand, the binding system still has to poll per-frame to see if a binding needs to be updated. If you have hundreds of bindable elements that will incur overhead.

How many of these 100 or so elements are actually visible in the scroll view at a given time? It sounds like you should use ListView, which can be used to only bind elements that are being drawn, so you can potentially have a lot less element bound at any given time.

Around 20. I will check ListView but I think it will not resolve all the problems.

Also how to handle showing/hiding windows. Currently, I am using DisplayStyle for handling that but this does not disable bindings/updates.

Since the runtime bindings only know about where the data lives, and nothing else, it has no other option to keep your UI up to date than to constantly poll to check if the data is outdated.

Any polling based, generic system will perform worse than a hand-rolled system where you update when neccessary, so you have to consider tradeoffs carefully. Here it’s probably better to use custom code to maintain the bindings when needed.

That being said, I’m a bit skeptical about the whole Publish thing. It looks to me that as a side-effect of SetData, you Publish, which triggers a rebuild of the UI due to GetViewHashCode. That wouldn’t happen to cause SetData to be called again, would it?

SetData calling Publish to notify UI to update. Here is all good. I’m calling SetData only when there is a need for that.

I really like this correlation that I wrote just ViewData in code, and Ui takes that and displays instead of searching all UI elements in the code and setting they values. Is there a way do disable elements from beeing checked constantly by the system?

I wonder about:

Did you set the updateTrigger to WhenDirty? And it’s still updating bindings constantly?

It not updating them, but checking if they need to be updated and that’s the issue here

Why is that the case, shouldn’t the system just update when notified via event?

I mean I’m not a Unity dev, so, I can only give you my educated guess. That being that they want bindings to update at a specific timing, and not whenever an event is called. This makes sense, for example, wanting to update bindings before rendering is updated.

So the INotifyBindablePropertyChanged for example tells the system that a given binding for a property is dirty, and to update it at the next update run.

And the IDataSourceViewHashProvider only makes sense to be polled per-frame, as the hash is used to compare the previous value from the current value in an efficient manner.

But this is far from efficient :smiley:

Hi @Eldirfar,

Runtime bindings, especially DataBinding have an overhead compared to handcrafted code. We need to cache the dataSource and and resolved path and if they have changed since the last update, we have to compute a version of the dataSource, we have to cache the changes and during the actual update, we need to extract the value from the source and push it on the element at arbitrary paths.

There are always improvements we can make to the performance (for example, we recently fixed an issue that would degrade performance when pushing change notifications that were not consumed by any binding), but it’s still a system that have an overhead.

Using the change tracking interfaces will help reduce the time spent on the actual update, but it can make the “Should Update Binding” phase a bit slower because we need to perform additional checks. It should be noted that “Should Update Binding” is comprised of computing the binding context, checking if it has changed, sending the notifications and then figure out if a particular binding should be updated or not.

The main idea for performance is to reduce the overall amount of bindings you have concurrently. For example, to update a Vector3Field updated, you can use 3 different bindings to keep x, y, zupdated or you can use a single one that keepsvalue` updated. The latter is much more efficient because we do less work and the work we do is faster because the path is shorter.

In your case, you have 100 elements and end up with 664 bindings. Let’s assume you only have bindings on these elements, this amounts to more than 6 bindings per element on average. The first thing to do, as mentioned in this thread, would be to use a ListView to reduce the amount of elements inside the ScrollView. You said you have around 20 elements visible at any given time, which would reduce the binding count to about 135. 6 bindings per element is still quite a lot and you should be able to reduce that number, either by creating custom elements for the UI where you can bind the value as a whole instead of each individual properties or by using CustomBindings to combine, for example, the styling changes.

As mentioned above, there are ways to improve performance and we’ll be looking at those eventually, such as:

  • Adding an option to “disable” a binding when display is not flex. This needs to be an option and not always the case because you could have bindings on invisible elements that would trigger the display of that element.
  • Using codegen/reflection to figure out if we can skip some updates completely. For example, if you create a CustomBinding and do not override the Update method, we could skip the binding update completely.
  • Change the way we compute if a bindig should be updated by doing a pre-pass on the data sources so that we query each source once per update. We did a prototype of this and it improved the performance, but it required to stop processing the bindings in hierarchical order. It’s something we could bring back using a project option.
  • Etc.

There are stil plenty of performance to be squeezed out, but at the moment, we’re focusing on quality.

It should also be noted that deep profile also include the profiling instrumentation overhead and since we do a lot calls during the update, it can pile up. What numbers do you get in a regular profiling when debugging is not enabled?

Hope this helps!

Hi @martinpa_unity, thanks for the response.

Without Deep Profile performance is a bit better.

we recently fixed an issue that would degrade performance when pushing change notifications that were not consumed by any binding)

Is it already released?

creating custom elements for the UI where you can bind the value as a whole

You mean creating custom Uxml elements? ([UxmlElement]) Do you have any example of binding value to them as a whole? It still use bindings or are set form the script? The main thing is I would love to avoid searching for specific VisualElements by their type and name in the script. In the old UI, you could just drag&drop references to the objects in the editor. In UI Toolkit it is impossible and bindings were a substitute for this or even better in my opinion.
To be honest, I don’t care about auto-updating UI when binding changes. I can update data and notify UI that it needs to refresh bindings if I could/know how.

Adding an option to “disable” a binding when display is not flex

I wouldn’t bind it to display property. Some kind of flag in Panel/VisualElement would be awesome.

There are stil plenty of performance to be squeezed out, but at the moment, we’re focusing on quality.

Sure but also remember if it is not performant enough at the end of the day nobody will use it.

And final thought why there is no performance difference between those two bindings modes ToTarget and ToTargetOnce?

Without Deep Profile performance is still quite low:

Can you send me your project? I’d like to take a look at it.

Is it already released?

It is being validated and pushed internally, so not released and I don’t think it would change much for you.

You mean creating custom Uxml elements? ([UxmlElement]) Do you have any example of binding value to them as a whole?

Yes. It should be something like:

public struct MyType
{
   public float someFloat;
}

[UxmlElement]
public partial class MyElement : VisualElement
{
    private FloatField m_Field;
    private MyType m_Value;

    [CreateProperty]
    public MyType value
    {
        get => m_Value;
        set
        {
            if (m_Value == value)
                return;
            m_Value = value;
            UpdateUI();
            NotifyPropertyChanged(nameof(value));
        }
    }

    public MyElement()
    {
        // Here, you can either construct the UI by code or through UXML.
        m_Field = new FloatField();
        // Register for changes on the sub-hierarchy to keep the value updated.
        m_Field.RegisterValueChangedCallback(evt => 
        {
            var current = value;
            current.someFloat = evt.newValue;
            value = current;
        });
        Add(m_Field);
    }

    private void UpdateUI()
    {
        m_Field.SetValueWithoutNotify(value.someFloat);
    }
}

The main thing is I would love to avoid searching for specific VisualElements by their type and name in the script. In the old UI, you could just drag&drop references to the objects in the editor.

I understand and I share that pain, but at the moment, it’s necessary. We would love to be able to drag references between elements in a way similar to UGUI, but we are still missing a few things for this.

I wouldn’t bind it to display property. Some kind of flag in Panel/VisualElement would be awesome.

You wouldn’t need to bind to display for this. I meant that the binding update could be skipped entirely if the element is not displayed. It’s not useful to bind a float value if the element is not displayed.

Sure but also remember if it is not performant enough at the end of the day nobody will use it.

We’re aware, of course.

And final thought why there is no performance difference between those two bindings modes ToTarget and ToTargetOnce?

This is probably because we currently still need to compute the binding context to figure out if it should not update or not (i.e. changing the binding context will make the binding as dirty. This is definitely an area we can improve. I can at least add more profiler markers to make it clearer what is done in which part.

Can you send me your project? I’d like to take a look at it.

I updated earlier my response to “Without Deep Profile performance is a bit better.” I forgot I had UpdateEveryFrame debug on

Yes. It should be something like:

Thanks, as I thought. Having a template I need to search for type/name.

Regarding ListView. I assume it does not support grid-like view? :<

Unfortunately, it doesn’t. What I would do for this is decide on a number of items to be displayed in each row and then create a list for the UI where each entry contains that number of items.

Yes, this is what I planned to do but those extra steps are sometimes frustrating.

Replaced ScrollViews with ListViews. Number of bindings goes down. Albo on hiding panels remember to unbind everything from it to reduce bindings updates further.

1 Like

@martinpa_unity
Do you know maybe what is the best way to hide the window which is a separate UI Document? Currently, I use flex none but that does not remove the bindings form being processed.
Remove it from the hierarchy? Would be nice not to have heavy showing again it.