- Home /
ScrollView performances with huge number of GUIContents.
I am wondering if there is a better way to display a huge number of entries (1k+, or 1m+) in an area with a scroll than using GUI(Layout).BeginScrollview which get slower the more entries you have, even if only ten labels are actually displayed.
The only alternative I found is to use a slider instead of a scrollview and to display only an interval, like from slider-5 to slider+5. However, I'm not quite satisfied with that solution. Visually, it's not a smooth transition but the label just appear and disappear according to the interval. If a text is to long for the area, you won't be able to read it no matter what.
Any ideas ?
Answer by Bunny83 · Mar 18, 2012 at 05:59 PM
I had this issue some time ago. I solved it by using a fix-size item height. That way you can calculate which items are visible. I still used GUILayout, but I replaced all non-visible items with a single GUILayout.Space().
If you're interested, you can download my editor script for searching unused assets here. It's still under development, but it works very well. The only thing that's a bit of a problem is that GUILayout can't calculate the width of all items since i display only the visible ones. If your content doesn't change you can precalculate the max width at start.
edit
Here is the relevant part:
//C#
m_ScrollPos = GUILayout.BeginScrollView(m_ScrollPos,true,true);
int FirstIndex = (int)(m_ScrollPos.y / m_ItemHeight);
FirstIndex = Mathf.Clamp(FirstIndex,0,Mathf.Max(0,m_FilteredResult.UnusedAssets.Count-m_ViewCount));
GUILayout.Space(FirstIndex * m_ItemHeight);
for(int i = FirstIndex; i < Mathf.Min(m_FilteredResult.UnusedAssets.Count, FirstIndex+m_ViewCount); i++)
{
string item = m_FilteredResult.UnusedAssets[i];
GUILayout.BeginVertical("box", GUILayout.Height(m_ItemHeight));
// [...]
GUILayout.EndVertical();
}
GUILayout.Space(Mathf.Max(0,(m_FilteredResult.UnusedAssets.Count-FirstIndex-m_ViewCount) * m_ItemHeight));
GUILayout.EndScrollView();
I think it should be possible to create some generic scrollview like this for arbitrary content.
This seems to work so far. Here is a script to display a sequence of integers with that technique (you can display 1 million without a difference with displaying a hundred).
Now, I'm gonna try with different sizes, like "0\\n\\n 123 \\n" etc. Any ideas ?
[Edit] So far, sounds like the calculations needed to obtain the index of the top value will be higher than just drawing the entire array in the all scroll view ...
Well, if you have varient content heights you have to store them in an array. You can calculate the height with GUIStyle.CalcHeight and store it along with your content. If the content doesn't change you could store the accumulated heights. So if you have those heights:
10,20,50,30,20
you would store
10,30,80,110,130
This way each item knows it absolute starting point. If you want to be able to change the content you have to accumulate the preceding and succeeding heights at redraw.
That's what I had in $$anonymous$$d, and here is the best I could come up with. The initialization takes some time and it can't handle more than 10k, but it seems to be faster than drawing everything.
I think I can solve the all thing by breaking any entries into as many string as their is lines. That way, the techniques above can be used. The problem remains if their is different font sizes / pictures of different sizes though.
Answer by Vladerx · Apr 26, 2019 at 06:06 AM
I faced a similar performance issue while rendering lots of GUI elements inside a scroll view, here is something similar to @Bunny83 Answer but here instead I use a grid inside an EditorWindow:
# region Calculate scroll view bounds
int scrollBarWidth = 12;
int headerHeight = 37;
int footerHeight = 46;
int verticalMargin = 15;
Rect scrollViewBounds = new Rect();
scrollViewBounds.y = headerHeight;
scrollViewBounds.width = Screen.width - scrollBarWidth;
scrollViewBounds.height = Screen.height - headerHeight - footerHeight;
# endregion
# region Calculate grid
int gridCellWidth = 90;
int gridCellHeight = 90;
int gridCountX = Mathf.FloorToInt( scrollViewBounds.width / gridCellWidth );
int gridCountY = Mathf.FloorToInt( count / gridCountX ) + 1;
float offsetX = ( scrollViewBounds.width - gridCountX * gridCellWidth ) / 2;
float offsetY = verticalMargin;
int rowsVisibleCount = ( int ) ( scrollViewBounds.height / gridCellHeight );
int firstRow = (int) ( scrollPosition.y / gridCellHeight );
int lastRow = firstRow + rowsVisibleCount + 1;
int startIndex = firstRow * gridCountX;
float spaceTop = firstRow * gridCellHeight;
float spaceBottom = ( gridCountY - lastRow + rowsVisibleCount + 1 ) * gridCellHeight + verticalMargin;
# endregion
int iconIndex = startIndex;
scrollPosition = EditorGUILayout.BeginScrollView( scrollPosition );
{
GUILayout.Space( spaceTop );
for( int y = firstRow; y < lastRow; ++y )
{
for( int x = 0; x < gridCountX; x += 1 )
{
if( iconIndex > totalIconsCount - 1 ) break;
Rect cellBounds = new Rect( x * gridCellWidth, y * gridCellHeight, gridCellWidth, gridCellHeight );
cellBounds.x += offsetX;
cellBounds.y += offsetY;
DrawIcon( cellBounds, ++ iconIndex );
}
}
GUILayout.Space( spaceBottom );
}
EditorGUILayout.EndScrollView();