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 ?

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*;*

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 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();