Skip to content

Commit

Permalink
Use System.Object[] indexers on UnityEngine.Object[]
Browse files Browse the repository at this point in the history
- The indexers on any array types that derive from UnityEngine.Object scan the entire array for no reason when accessed, work around this by using object[] indexers which don't get scanned...
- Reduce redundant calls to the symbolCsType in the indexer checks
  • Loading branch information
MerlinVR committed Nov 28, 2020
1 parent 42ec245 commit 1cf3175
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 13 deletions.
40 changes: 27 additions & 13 deletions Assets/UdonSharp/Editor/UdonSharpExpressionCapture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,10 @@ public SymbolDefinition ExecuteGet()
SymbolDefinition arraySymbol = accessValue.symbol;
System.Type elementType = null;

System.Type arraySymbolType = arraySymbol.symbolCsType;

string getIndexerUdonName;
if (arraySymbol.symbolCsType == typeof(string))
if (arraySymbolType == typeof(string))
{
// udon-workaround: This is where support for Udon's string indexer would go, IF IT HAD ONE
//getIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbol.symbolCsType.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(e => e.Name == "get_Chars").First());
Expand Down Expand Up @@ -517,21 +519,27 @@ public SymbolDefinition ExecuteGet()
visitorContext.uasmBuilder.AddPush(subStrCharArrSymbol);
visitorContext.uasmBuilder.AddPush(visitorContext.topTable.CreateConstSymbol(typeof(int), 0)); // 0 index
}
else if (arraySymbol.symbolCsType == typeof(Vector2) ||
arraySymbol.symbolCsType == typeof(Vector3) ||
arraySymbol.symbolCsType == typeof(Vector4) ||
arraySymbol.symbolCsType == typeof(Matrix4x4))
else if (arraySymbolType == typeof(Vector2) ||
arraySymbolType == typeof(Vector3) ||
arraySymbolType == typeof(Vector4) ||
arraySymbolType == typeof(Matrix4x4))
{
elementType = typeof(float);

getIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbol.symbolCsType.GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "get_Item" && e.GetParameters().Length == 1));
getIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbolType.GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "get_Item" && e.GetParameters().Length == 1));

visitorContext.uasmBuilder.AddPush(arraySymbol);
visitorContext.uasmBuilder.AddPush(arrayIndexerIndexValue.symbol);
}
else
{
getIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbol.symbolCsType.GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "Get"));
// udon-workaround: VRC scans UnityEngine.Object arrays in their respective methods, so those methods are useless since they get disproportionately expensive the larger the array is.
// Instead use the object[] indexer for these objects since it does not get scanned
if (arraySymbolType.GetElementType() == typeof(UnityEngine.Object) || arraySymbolType.GetElementType().IsSubclassOf(typeof(UnityEngine.Object)))
getIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(typeof(object[]).GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "Get"));
else
getIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbolType.GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "Get"));

elementType = arraySymbol.userCsType.GetElementType();

visitorContext.uasmBuilder.AddPush(arraySymbol);
Expand Down Expand Up @@ -638,17 +646,23 @@ public void ExecuteSet(SymbolDefinition value, bool explicitCast = false)
{
SymbolDefinition arraySymbol = accessValue.symbol;
string setIndexerUdonName;
System.Type arraySymbolType = arraySymbol.symbolCsType;

if (arraySymbol.symbolCsType == typeof(Vector2) ||
arraySymbol.symbolCsType == typeof(Vector3) ||
arraySymbol.symbolCsType == typeof(Vector4) ||
arraySymbol.symbolCsType == typeof(Matrix4x4))
if (arraySymbolType == typeof(Vector2) ||
arraySymbolType == typeof(Vector3) ||
arraySymbolType == typeof(Vector4) ||
arraySymbolType == typeof(Matrix4x4))
{
setIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbol.symbolCsType.GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "set_Item" && e.GetParameters().Length == 2));
}
else
{
setIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbol.symbolCsType.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(e => e.Name == "Set").First());
// udon-workaround: VRC scans UnityEngine.Object arrays in their respective methods, so those methods are useless since they get disproportionately expensive the larger the array is.
// Instead use the object[] indexer for these objects since it does not get scanned
if (arraySymbolType.GetElementType() == typeof(UnityEngine.Object) || arraySymbolType.GetElementType().IsSubclassOf(typeof(UnityEngine.Object)))
setIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(typeof(object[]).GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "Set"));
else
setIndexerUdonName = visitorContext.resolverContext.GetUdonMethodName(arraySymbolType.GetMethods(BindingFlags.Public | BindingFlags.Instance).First(e => e.Name == "Set"));
}

visitorContext.uasmBuilder.AddPush(arraySymbol);
Expand Down Expand Up @@ -886,7 +900,7 @@ public SymbolDefinition CastSymbolToType(SymbolDefinition sourceSymbol, System.T
return castOutput;
}

// Int to enum cast
// udon-workaround: Int to enum cast
if (UdonSharpUtils.IsIntegerType(sourceSymbol.symbolCsType) && targetType.IsEnum)
{
SymbolDefinition enumArraySymbol = GetEnumArrayForType(targetType);
Expand Down
16 changes: 16 additions & 0 deletions Assets/UdonSharp/Tests/TestScripts/FlowControl/ForLoopTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ public void ExecuteTests()
}

tester.TestAssertion("Foreach string loop", builtStr == helloStr);

GameObject[] gameObjects = new GameObject[2000];
int gameObjectLen = gameObjects.Length;

for (int i = 0; i < gameObjects.Length; ++i)
{
gameObjects[i] = gameObject;
}

System.DateTime startTime = System.DateTime.UtcNow;
for (int i = 0; i < gameObjects.Length; ++i)
{
gameObjects[i] = gameObject;
}

tester.TestAssertion("Array indexer performance", (System.DateTime.UtcNow - startTime).TotalSeconds < 1f);
}
}
}

0 comments on commit 1cf3175

Please sign in to comment.