Skip to content

Commit 795f11c

Browse files
committed
Optimize large lists rendering
1 parent bdcb33d commit 795f11c

File tree

3 files changed

+78
-5
lines changed

3 files changed

+78
-5
lines changed

Editor/Elements/TriListElement.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ namespace TriInspector.Elements
1212
{
1313
public class TriListElement : TriElement
1414
{
15+
private const int MinElementsForVirtualization = 25;
16+
1517
private const float ListExtraWidth = 7f;
1618
private const float DraggableAreaExtraWidth = 14f;
1719

@@ -21,6 +23,8 @@ public class TriListElement : TriElement
2123
private readonly bool _showElementLabels;
2224

2325
private float _lastContentWidth;
26+
private int? _lastInvisibleElement;
27+
private int? _lastVisibleElement;
2428

2529
protected ReorderableList ListGui => _reorderableListGui;
2630

@@ -109,13 +113,22 @@ public override void OnGUI(Rect position)
109113
{
110114
if (!_property.IsExpanded)
111115
{
116+
_lastInvisibleElement = null;
117+
_lastVisibleElement = null;
118+
112119
ReorderableListProxy.DoListHeader(_reorderableListGui, new Rect(position)
113120
{
114121
yMax = position.yMax - 4,
115122
});
116123
return;
117124
}
118125

126+
if (_reorderableListGui.count < MinElementsForVirtualization)
127+
{
128+
_lastInvisibleElement = null;
129+
_lastVisibleElement = null;
130+
}
131+
119132
var labelWidthExtra = ListExtraWidth + DraggableAreaExtraWidth;
120133

121134
using (TriGuiHelper.PushLabelWidth(EditorGUIUtility.labelWidth - labelWidthExtra))
@@ -403,6 +416,36 @@ private void DrawElementCallback(Rect rect, int index, bool isActive, bool isFoc
403416
return;
404417
}
405418

419+
if (_lastInvisibleElement.HasValue && index + 1 < _lastInvisibleElement.Value ||
420+
_lastVisibleElement.HasValue && index - 1 > _lastVisibleElement.Value)
421+
{
422+
return;
423+
}
424+
425+
if (_reorderableListGui.count > MinElementsForVirtualization)
426+
{
427+
if (Event.current.type == EventType.Repaint)
428+
{
429+
var windowRect = GUIClipProxy.VisibleRect;
430+
var rectInWindow = GUIClipProxy.UnClipToWindow(rect);
431+
432+
if (rectInWindow.yMax < 0)
433+
{
434+
_lastInvisibleElement = index;
435+
} else if (_lastInvisibleElement == index)
436+
{
437+
_lastInvisibleElement = index / 2;
438+
_lastVisibleElement = index / 2 + 1;
439+
_property.PropertyTree.RequestRepaint();
440+
}
441+
442+
if (rectInWindow.y < windowRect.height)
443+
{
444+
_lastVisibleElement = index;
445+
}
446+
}
447+
}
448+
406449
if (!_reorderableListGui.draggable)
407450
{
408451
rect.xMin += DraggableAreaExtraWidth;
@@ -421,6 +464,12 @@ private float ElementHeightCallback(int index)
421464
return EditorGUIUtility.singleLineHeight;
422465
}
423466

467+
if (_lastInvisibleElement.HasValue && index + 1 < _lastInvisibleElement.Value ||
468+
_lastVisibleElement.HasValue && index - 1 > _lastVisibleElement.Value)
469+
{
470+
return Mathf.Max(EditorGUIUtility.singleLineHeight, GetChild(index).CachedHeight);
471+
}
472+
424473
return GetChild(index).GetHeight(_lastContentWidth);
425474
}
426475

Editor/TriElement.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class TriElement
99
{
1010
private static readonly List<TriElement> Empty = new List<TriElement>();
1111

12+
private float _cachedHeight;
1213
private bool _attached;
1314
private List<TriElement> _children = Empty;
1415

@@ -17,6 +18,8 @@ public class TriElement
1718

1819
public bool IsAttached => _attached;
1920

21+
internal float CachedHeight => _cachedHeight;
22+
2023
[PublicAPI]
2124
public virtual bool Update()
2225
{
@@ -43,24 +46,29 @@ public virtual float GetHeight(float width)
4346
Debug.LogError($"{GetType().Name} not attached");
4447
}
4548

49+
if (Event.current.type != EventType.Layout)
50+
{
51+
return _cachedHeight;
52+
}
53+
4654
switch (_children.Count)
4755
{
4856
case 0:
49-
return 0f;
57+
return _cachedHeight = 0f;
5058

5159
case 1:
52-
return _children[0].GetHeight(width);
60+
return _cachedHeight = _children[0].GetHeight(width);
5361

5462
default:
5563
{
56-
var height = (_children.Count - 1) * EditorGUIUtility.standardVerticalSpacing;
64+
_cachedHeight = (_children.Count - 1) * EditorGUIUtility.standardVerticalSpacing;
5765

5866
foreach (var child in _children)
5967
{
60-
height += child.GetHeight(width);
68+
_cachedHeight += child.GetHeight(width);
6169
}
6270

63-
return height;
71+
return _cachedHeight;
6472
}
6573
}
6674
}

Unity.InternalAPIEditorBridge.012/GUIClipProxy.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Reflection;
23
using UnityEditor;
34
using UnityEngine;
45

@@ -7,6 +8,8 @@ namespace TriInspectorUnityInternalBridge
78
internal static class GUIClipProxy
89
{
910
private static Func<Vector2, Vector2> _guiClipUnClipVector2;
11+
private static Func<Rect, Rect> _guiClipUnClipToWindowRect;
12+
private static Func<Rect> _guiClipVisibleRect;
1013

1114
[InitializeOnLoadMethod]
1215
private static void Setup()
@@ -16,11 +19,24 @@ private static void Setup()
1619

1720
_guiClipUnClipVector2 = (Func<Vector2, Vector2>) Delegate.CreateDelegate(typeof(Func<Vector2, Vector2>),
1821
guiClipType.GetMethod("Unclip", new[] {typeof(Vector2)}));
22+
23+
_guiClipUnClipToWindowRect = (Func<Rect, Rect>) Delegate.CreateDelegate(typeof(Func<Rect, Rect>),
24+
guiClipType.GetMethod("UnclipToWindow", new[] {typeof(Rect)}));
25+
26+
_guiClipVisibleRect = (Func<Rect>) Delegate.CreateDelegate(typeof(Func<Rect>),
27+
guiClipType.GetProperty("visibleRect", BindingFlags.Static | BindingFlags.NonPublic).GetMethod);
1928
}
2029

30+
public static Rect VisibleRect => _guiClipVisibleRect.Invoke();
31+
2132
public static Vector2 UnClip(Vector2 pos)
2233
{
2334
return _guiClipUnClipVector2.Invoke(pos);
2435
}
36+
37+
public static Rect UnClipToWindow(Rect rect)
38+
{
39+
return _guiClipUnClipToWindowRect.Invoke(rect);
40+
}
2541
}
2642
}

0 commit comments

Comments
 (0)