Skip to content

Commit f5018c8

Browse files
julienamsellemEvergreen
authored andcommitted
[VFX] UI Optimization to improve performance
[Slack channel](https://unity.enterprise.slack.com/archives/C06D7PTSJ9H) We recently noticed that switching tab to a big VFX Graph asset could take several seconds. After investigation, we were told that's because the whole content is being recreated from scratch. In the scope of Unity6 nothing is planned in UIToolkit, nor in GraphView to improve this behavior. So the idea here is to reduce at the minimum the number of UI elements used to build our Graph. In the process of this UI optimization, there I found several small design issues or even bugs that I could easily fix in the process of optimization (a large part of the work was to tweak uss anyway). So far, the performance improvements measure give the following numbers: **~ 30% less UI elements** **~ 40% faster when switching tab** See captures below (beta 8) ![ui-profiling-comparison-annotated](https://media.github.cds.internal.unity3d.com/user/4003/files/85c2a95f-bce6-4cc8-82b4-a0ceabe1e80b) For the record, here are some small UI improvements ![ui-comparison](https://media.github.cds.internal.unity3d.com/user/4003/files/5d8a2b75-9bf1-47f2-a1c3-4c3a8fa47389) Some other improvements: - Node's expand collapse button is nicer and has hover visual feedback - Fixed issue when dragging a link from an "untyped" port (Add, Multiply ...) and using the ALT modifier key
1 parent 14fe715 commit f5018c8

54 files changed

Lines changed: 1155 additions & 1823 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Packages/com.unity.visualeffectgraph/Editor/Controls/VFXBitField.cs

Lines changed: 24 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
using UnityEngine;
21
using UnityEngine.UIElements;
3-
using UnityEditor.UIElements;
42

5-
using System.Collections.Generic;
63
using System.Runtime.InteropServices;
74
namespace UnityEditor.VFX
85
{
@@ -15,50 +12,32 @@ namespace UnityEditor.VFX.UI
1512
{
1613
abstract class VFXBitField<T, U> : VFXControl<U>
1714
{
15+
private Label m_Label;
16+
private bool m_Indeterminate;
17+
1818
protected VisualElement[] m_Buttons;
19-
protected VisualElement m_Background;
20-
protected Texture2D m_BitImage;
21-
protected Texture2D m_BitBkgndImage;
22-
protected Label m_Label;
2319

24-
public VFXBitField()
20+
protected VFXBitField()
2521
{
2622
m_Buttons = new VisualElement[Marshal.SizeOf(typeof(T)) * 8];
27-
m_Background = new VisualElement() { name = "background" };
28-
29-
m_Label = new Label() { name = "tip" };
30-
Add(m_Label);
31-
32-
Add(m_Background);
33-
var buttonContainer = new VisualElement() { name = "button-container", pickingMode = PickingMode.Ignore };
34-
Add(buttonContainer);
3523
for (int i = 0; i < m_Buttons.Length; ++i)
3624
{
37-
var button = new VisualElement();
38-
button.style.flexGrow = button.style.flexShrink = 1;
39-
button.style.marginRight = 1;
25+
var button = new VisualElement { name = "bit-button"};
4026
SetupListener(button, i);
41-
buttonContainer.Add(button);
27+
Add(button);
4228
m_Buttons[i] = button;
4329
}
30+
m_Buttons[0].AddToClassList("first");
31+
m_Buttons[^1].AddToClassList("last");
4432

45-
VisualElement backgroundItem = null;
46-
for (int i = 0; i < m_Buttons.Length; ++i)
47-
{
48-
backgroundItem = new VisualElement();
49-
backgroundItem.style.flexGrow = backgroundItem.style.flexShrink = 1;
50-
if (i != m_Buttons.Length - 1)
51-
backgroundItem.style.paddingLeft = 1;
52-
SetupBkgnd(backgroundItem, i);
53-
m_Background.Add(backgroundItem);
54-
}
33+
m_Label = new Label { name = "tip" };
34+
Add(m_Label);
5535

56-
m_Buttons[m_Buttons.Length - 1].style.marginRight = 0;
57-
RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
36+
RegisterCallback<MouseLeaveEvent>(e => m_Label.text = "");
5837
this.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu));
5938
}
6039

61-
public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
40+
void BuildContextualMenu(ContextualMenuPopulateEvent evt)
6241
{
6342
evt.menu.AppendAction("Check All", CheckAll, DropdownMenuAction.AlwaysEnabled);
6443
evt.menu.AppendAction("Check None", CheckNone, DropdownMenuAction.AlwaysEnabled);
@@ -70,47 +49,21 @@ public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
7049

7150
void SetupListener(VisualElement button, int index)
7251
{
73-
button.AddManipulator(new Clickable(() => ValueToggled(index)));
74-
button.RegisterCallback<MouseEnterEvent>(e => m_Label.text = index.ToString());
75-
button.RegisterCallback<MouseLeaveEvent>(e => m_Label.text = "");
76-
}
77-
78-
void SetupBkgnd(VisualElement button, int index)
79-
{
52+
button.AddManipulator(new Clickable(() => this.ValueToggled(index)));
8053
button.RegisterCallback<MouseEnterEvent>(e => m_Label.text = index.ToString());
81-
button.RegisterCallback<MouseLeaveEvent>(e => m_Label.text = "");
8254
}
8355

8456
protected abstract void ValueToggled(int i);
8557

86-
static readonly CustomStyleProperty<Texture2D> s_BitImage = new CustomStyleProperty<Texture2D>("--bit-image");
87-
static readonly CustomStyleProperty<Texture2D> s_BitBkgndImage = new CustomStyleProperty<Texture2D>("--bit-bkgnd-image");
88-
private void OnCustomStyleResolved(CustomStyleResolvedEvent e)
89-
{
90-
var customStyle = e.customStyle;
91-
customStyle.TryGetValue(s_BitImage, out m_BitImage);
92-
customStyle.TryGetValue(s_BitBkgndImage, out m_BitBkgndImage);
93-
94-
for (int i = 0; i < m_Background.childCount - 1; ++i)
95-
m_Background.ElementAt(i).style.backgroundImage = m_BitBkgndImage;
96-
97-
ValueToGUI(true);
98-
}
99-
100-
bool m_Indeterminate;
101-
10258
public override bool indeterminate
10359
{
104-
get
105-
{
106-
return m_Indeterminate;
107-
}
60+
get => m_Indeterminate;
10861
set
10962
{
11063
m_Indeterminate = value;
11164
foreach (var button in m_Buttons)
11265
{
113-
button.visible = !m_Indeterminate;
66+
button.SetEnabled(!m_Indeterminate);
11467
}
11568
}
11669
}
@@ -120,10 +73,17 @@ class VFX32BitField : VFXBitField<uint, long>
12073
{
12174
protected override void ValueToGUI(bool force)
12275
{
123-
uint value = (uint)this.value;
76+
uint bitMask = (uint)this.value;
12477
for (int i = 0; i < m_Buttons.Length; ++i)
12578
{
126-
m_Buttons[i].style.backgroundImage = (value & 1u << i) != 0 ? m_BitImage : null;
79+
if ((bitMask & 1u << i) != 0)
80+
{
81+
m_Buttons[i].AddToClassList("bit-set");
82+
}
83+
else
84+
{
85+
m_Buttons[i].RemoveFromClassList("bit-set");
86+
}
12787
}
12888
}
12989

Packages/com.unity.visualeffectgraph/Editor/Controls/VFXControl.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
using System;
12
using UnityEngine;
23
using UnityEngine.UIElements;
3-
using UnityEditor.UIElements;
44

55
using System.Collections.Generic;
66

@@ -12,17 +12,27 @@ static class VFXControlConstants
1212
public static readonly Color indeterminateTextColor = new Color(0.82f, 0.82f, 0.82f);
1313
}
1414

15-
abstract class VFXControl<T> : VisualElement, INotifyValueChanged<T>
15+
interface IVFXControl
16+
{
17+
bool indeterminate { get; set; }
18+
event Action onValueDragFinished;
19+
event Action onValueDragStarted;
20+
void ForceUpdate();
21+
void SetEnabled(bool isEnabled);
22+
}
23+
24+
abstract class VFXControl<T> : VisualElement, INotifyValueChanged<T>, IVFXControl
1625
{
1726
T m_Value;
1827
public T value
1928
{
20-
get { return m_Value; }
21-
set
22-
{
23-
SetValueAndNotify(value);
24-
}
29+
get => m_Value;
30+
set => SetValueAndNotify(value);
2531
}
32+
33+
public event Action onValueDragFinished;
34+
public event Action onValueDragStarted;
35+
2636
public void SetValueAndNotify(T newValue)
2737
{
2838
if (!EqualityComparer<T>.Default.Equals(value, newValue))
@@ -64,5 +74,9 @@ public void RemoveOnValueChanged(EventCallback<ChangeEvent<T>> callback)
6474
{
6575
UnregisterCallback(callback);
6676
}
77+
78+
protected void ValueDragFinished(PointerCaptureOutEvent evt) => onValueDragFinished?.Invoke();
79+
80+
protected void ValueDragStarted(PointerCaptureEvent evt) => onValueDragStarted?.Invoke();
6781
}
6882
}

Packages/com.unity.visualeffectgraph/Editor/Controls/VFXEnumField.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,6 @@ public VFXEnumField(string label, System.Type enumType) : base(label)
7070

7171
style.flexDirection = FlexDirection.Row;
7272
Add(m_DropDownButton);
73-
74-
var icon = new VisualElement() { name = "icon" };
75-
icon.AddToClassList("unity-enum-field__arrow");
76-
77-
m_DropDownButton.Add(icon);
7873
}
7974

8075
public VFXEnumField(Label existingLabel, System.Type enumType) : base(existingLabel)
Lines changed: 19 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,35 @@
1-
using UnityEngine;
2-
using UnityEngine.UIElements;
3-
using UnityEditor.UIElements;
1+
using System;
42
using System.Collections.Generic;
53

4+
using UnityEngine.UIElements;
5+
66
namespace UnityEditor.VFX.UI
77
{
88
class VFXEnumValuePopup : VisualElement, INotifyValueChanged<long>
99
{
10-
protected Label m_DropDownButton;
11-
TextElement m_ValueText;
10+
DropdownField m_DropDownButton;
11+
long m_Value;
1212

13-
public string[] enumValues { get; set; }
13+
public IEnumerable<string> choices => m_DropDownButton.choices;
1414

15-
public VFXEnumValuePopup()
15+
public VFXEnumValuePopup(string label, List<string> values)
1616
{
17-
AddToClassList("unity-enum-field");
18-
AddToClassList("VFXEnumValuePopup");
19-
m_DropDownButton = new Label();
20-
m_DropDownButton.AddToClassList("unity-enum-field__input");
21-
m_DropDownButton.AddManipulator(new DownClickable(OnClick));
17+
m_DropDownButton = new DropdownField(label);
18+
m_DropDownButton.choices = values;
19+
m_DropDownButton.value = values[0];
20+
m_DropDownButton.RegisterCallback<ChangeEvent<string>>(OnValueChanged);
2221
Add(m_DropDownButton);
23-
m_ValueText = new TextElement();
24-
m_ValueText.AddToClassList("unity-enum-field__text");
25-
26-
var icon = new VisualElement() { name = "icon" };
27-
icon.AddToClassList("unity-enum-field__arrow");
28-
m_DropDownButton.Add(m_ValueText);
29-
m_DropDownButton.Add(icon);
30-
}
31-
32-
private void OnClick()
33-
{
34-
GenericMenu menu = new GenericMenu();
35-
36-
for (long i = 0; i < enumValues.Length; ++i)
37-
{
38-
menu.AddItem(new GUIContent(enumValues[i]), i == m_Value, ChangeValue, i);
39-
}
40-
menu.DropDown(m_DropDownButton.worldBound);
4122
}
4223

43-
void ChangeValue(object value)
24+
private void OnValueChanged(ChangeEvent<string> evt)
4425
{
45-
SetValueAndNotify((long)value);
26+
SetValueAndNotify(m_DropDownButton.choices.IndexOf(evt.newValue));
4627
}
4728

48-
public long m_Value;
49-
5029
public long value
5130
{
52-
get
53-
{
54-
return m_Value;
55-
}
56-
57-
set
58-
{
59-
SetValueAndNotify(value);
60-
}
31+
get => m_Value;
32+
set => SetValueAndNotify(value);
6133
}
6234

6335
public void SetValueAndNotify(long newValue)
@@ -75,19 +47,12 @@ public void SetValueAndNotify(long newValue)
7547

7648
public void SetValueWithoutNotify(long newValue)
7749
{
78-
m_Value = newValue;
79-
bool found = false;
80-
for (uint i = 0; i < enumValues.Length; ++i)
50+
if (newValue >= 0 && newValue < m_DropDownButton.choices.Count)
8151
{
82-
if (newValue == i)
83-
{
84-
found = true;
85-
m_ValueText.text = enumValues[i];
86-
break;
87-
}
52+
m_Value = newValue;
8853
}
89-
if (!found)
90-
m_ValueText.text = enumValues[enumValues.Length - 1];
54+
55+
m_Value = Math.Clamp(newValue, 0, m_DropDownButton.choices.Count - 1);
9156
}
9257
}
9358
}

Packages/com.unity.visualeffectgraph/Editor/Controls/VFXFlipBookField.cs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
1-
using UnityEngine;
21
using UnityEngine.UIElements;
3-
using UnityEditor.UIElements;
4-
5-
using System.Collections.Generic;
62

73
namespace UnityEditor.VFX.UI
84
{
95
class VFXFlipBookField : VFXControl<FlipBook>
106
{
11-
VFXLabeledField<IntegerField, int> m_X;
12-
VFXLabeledField<IntegerField, int> m_Y;
7+
IntegerField m_X;
8+
IntegerField m_Y;
139

1410
void CreateTextField()
1511
{
16-
m_X = new VFXLabeledField<IntegerField, int>("X");
17-
m_Y = new VFXLabeledField<IntegerField, int>("Y");
12+
m_X = new IntegerField("X");
13+
m_Y = new IntegerField("Y");
1814

19-
m_X.control.AddToClassList("fieldContainer");
20-
m_Y.control.AddToClassList("fieldContainer");
15+
m_X.AddToClassList("fieldContainer");
16+
m_Y.AddToClassList("fieldContainer");
2117
m_X.AddToClassList("fieldContainer");
2218
m_Y.AddToClassList("fieldContainer");
2319

@@ -41,19 +37,19 @@ void OnYValueChanged(ChangeEvent<int> e)
4137

4238
public override bool indeterminate
4339
{
44-
get
45-
{
46-
return m_X.indeterminate;
47-
}
40+
get => m_X.showMixedValue;
4841
set
4942
{
50-
m_X.indeterminate = value;
51-
m_Y.indeterminate = value;
43+
m_X.showMixedValue = value;
44+
m_Y.showMixedValue = value;
5245
}
5346
}
5447

55-
public VFXFlipBookField()
48+
public VFXFlipBookField(string label)
5649
{
50+
var labelElement = new Label(label);
51+
labelElement.AddToClassList("label");
52+
Add(labelElement);
5753
CreateTextField();
5854

5955
style.flexDirection = FlexDirection.Row;
@@ -63,10 +59,10 @@ public VFXFlipBookField()
6359

6460
protected override void ValueToGUI(bool force)
6561
{
66-
if (!m_X.control.HasFocus() || force)
62+
if (!m_X.HasFocus() || force)
6763
m_X.value = value.x;
6864

69-
if (!m_Y.control.HasFocus() || force)
65+
if (!m_Y.HasFocus() || force)
7066
m_Y.value = value.y;
7167
}
7268
}

0 commit comments

Comments
 (0)