summaryrefslogtreecommitdiff
path: root/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation
diff options
context:
space:
mode:
Diffstat (limited to 'VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation')
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs1
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance.meta8
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs165
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs.meta12
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs37
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters.meta8
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs109
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs33
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs46
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs12
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs12
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs44
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners.meta8
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs97
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs44
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs29
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs48
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs191
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs29
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs31
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs266
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs114
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs64
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs31
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats.meta8
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs757
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs32
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs15
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs.meta11
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs86
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs.meta3
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs260
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs.meta12
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs682
-rw-r--r--VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs.meta12
56 files changed, 3396 insertions, 0 deletions
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs
new file mode 100644
index 00000000..39dd14a7
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs
@@ -0,0 +1 @@
+/* Migration File: Intentionally Left Blank */ \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs.meta
new file mode 100644
index 00000000..f6e03bb1
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/AvatarValidation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d497915ac8463e048aeb2c934a36c299
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance.meta
new file mode 100644
index 00000000..07fd6f1c
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 16b48f3cc7015584c9d4157931f1c673
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs
new file mode 100644
index 00000000..3269bc89
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs
@@ -0,0 +1,165 @@
+using System.Collections;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance
+{
+ public static class AvatarPerformance
+ {
+ #region Public Constants
+
+ public const int DEFAULT_DYNAMIC_BONE_MAX_SIMULATED_BONE_LIMIT = 32;
+ public const int DEFAULT_DYNAMIC_BONE_MAX_COLLIDER_CHECK_LIMIT = 8;
+
+ #if UNITY_ANDROID || UNITY_IOS
+ internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_DEFAULT = PerformanceRating.Medium;
+ internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MIN = PerformanceRating.Medium;
+ internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MAX = PerformanceRating.Poor;
+ #else
+ internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_DEFAULT = PerformanceRating.VeryPoor;
+ internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MIN = PerformanceRating.Medium;
+ internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MAX = PerformanceRating.VeryPoor;
+ #endif
+
+ #endregion
+
+ #region Public Delegates
+
+ public delegate bool IgnoreDelegate(Component component);
+
+ public delegate void FilterBlockCallback();
+
+ public static IgnoreDelegate ShouldIgnoreComponent { get; set; }
+
+ #endregion
+
+ #region Public Methods
+
+ public static void CalculatePerformanceStats(string avatarName, GameObject avatarObject, AvatarPerformanceStats perfStats)
+ {
+ perfStats.Reset();
+ perfStats.avatarName = avatarName;
+
+ PerformanceScannerSet performanceScannerSet = GetPerformanceScannerSet();
+ if(performanceScannerSet != null)
+ {
+ performanceScannerSet.RunPerformanceScan(avatarObject, perfStats, ShouldIgnoreComponentInternal);
+ }
+
+ // cache performance ratings
+ perfStats.CalculateAllPerformanceRatings();
+ }
+
+ public static IEnumerator CalculatePerformanceStatsEnumerator(string avatarName, GameObject avatarObject, AvatarPerformanceStats perfStats)
+ {
+ perfStats.Reset();
+ perfStats.avatarName = avatarName;
+
+ PerformanceScannerSet performanceScannerSet = GetPerformanceScannerSet();
+ if(performanceScannerSet != null)
+ {
+ yield return performanceScannerSet.RunPerformanceScanEnumerator(avatarObject, perfStats, ShouldIgnoreComponentInternal);
+ }
+
+ // cache performance ratings
+ perfStats.CalculateAllPerformanceRatings();
+ }
+
+ public static IEnumerator ApplyPerformanceFiltersEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, PerformanceRating minPerfRating, FilterBlockCallback onBlock)
+ {
+ // Performance Filtering is disabled.
+ if(minPerfRating == PerformanceRating.None)
+ {
+ yield break;
+ }
+
+ PerformanceFilterSet performanceFilterSet = GetPerformanceFilterSet();
+ if(performanceFilterSet == null)
+ {
+ yield break;
+ }
+
+ bool avatarBlocked = false;
+ yield return performanceFilterSet.ApplyPerformanceFilters(
+ avatarObject,
+ perfStats,
+ minPerfRating,
+ ShouldIgnoreComponentInternal,
+ () => { avatarBlocked = true; }
+ );
+
+ if(!avatarBlocked)
+ {
+ yield break;
+ }
+
+ VRC.Core.Logger.LogFormat(
+ "Avatar hidden due to low performance rating: [{0}] {1} - minimum setting: {2}",
+ perfStats.avatarName,
+ perfStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.Overall),
+ minPerfRating
+ );
+
+ onBlock();
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private static PerformanceScannerSet GetPerformanceScannerSet()
+ {
+ PerformanceScannerSet performanceScannerSet;
+ if(VRC.ValidationHelpers.IsStandalonePlatform())
+ {
+ performanceScannerSet = Resources.Load<PerformanceScannerSet>("Validation/Performance/ScannerSets/PerformanceScannerSet_Windows");
+ }
+ else
+ {
+ performanceScannerSet = Resources.Load<PerformanceScannerSet>("Validation/Performance/ScannerSets/PerformanceScannerSet_Quest");
+ }
+
+ return performanceScannerSet;
+ }
+
+ private static PerformanceFilterSet GetPerformanceFilterSet()
+ {
+ PerformanceFilterSet performanceFilterSet;
+ if(VRC.ValidationHelpers.IsStandalonePlatform())
+ {
+ performanceFilterSet = Resources.Load<PerformanceFilterSet>("Validation/Performance/FilterSets/PerformanceFilterSet_Windows");
+ }
+ else
+ {
+ performanceFilterSet = Resources.Load<PerformanceFilterSet>("Validation/Performance/FilterSets/PerformanceFilterSet_Quest");
+ }
+
+ return performanceFilterSet;
+ }
+
+ private static bool ShouldIgnoreComponentInternal(Component component)
+ {
+ if(Application.isEditor)
+ {
+ if(component == null)
+ {
+ return false;
+ }
+
+ if(component.CompareTag("EditorOnly"))
+ {
+ return true;
+ }
+ }
+
+ if(ShouldIgnoreComponent != null)
+ {
+ return ShouldIgnoreComponent(component);
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs.meta
new file mode 100644
index 00000000..8d06a338
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformance.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 15ecac6f7fdc1bc4fb723fee6f4635dd
+timeCreated: 1540944864
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs
new file mode 100644
index 00000000..847ac62c
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs
@@ -0,0 +1,37 @@
+namespace VRC.SDKBase.Validation.Performance
+{
+ public enum AvatarPerformanceCategory
+ {
+ None,
+
+ Overall,
+
+ DownloadSize,
+ PolyCount,
+ AABB,
+ SkinnedMeshCount,
+ MeshCount,
+ MaterialCount,
+ DynamicBoneComponentCount,
+ DynamicBoneSimulatedBoneCount,
+ DynamicBoneColliderCount,
+ DynamicBoneCollisionCheckCount,
+ AnimatorCount,
+ BoneCount,
+ LightCount,
+ ParticleSystemCount,
+ ParticleTotalCount,
+ ParticleMaxMeshPolyCount,
+ ParticleTrailsEnabled,
+ ParticleCollisionEnabled,
+ TrailRendererCount,
+ LineRendererCount,
+ ClothCount,
+ ClothMaxVertices,
+ PhysicsColliderCount,
+ PhysicsRigidbodyCount,
+ AudioSourceCount,
+
+ AvatarPerformanceCategoryCount
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs.meta
new file mode 100644
index 00000000..449a3394
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/AvatarPerformanceCategory.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f1ce994297384ff1bc330196df61b7ca
+timeCreated: 1561267912 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters.meta
new file mode 100644
index 00000000..b19c2f04
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bf5b2b0296bbecc4dba509652ba3eb24
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs
new file mode 100644
index 00000000..9896fcc9
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs
@@ -0,0 +1,109 @@
+using System.Collections;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Filters
+{
+ public abstract class AbstractPerformanceFilter : ScriptableObject
+ {
+ public abstract IEnumerator ApplyPerformanceFilter(
+ GameObject avatarObject,
+ AvatarPerformanceStats perfStats,
+ PerformanceRating ratingLimit,
+ AvatarPerformance.IgnoreDelegate shouldIgnoreComponent,
+ AvatarPerformance.FilterBlockCallback onBlock
+ );
+
+ protected static IEnumerator RemoveComponentsOfTypeEnumerator<T>(GameObject target) where T : Component
+ {
+ if(target == null)
+ {
+ yield break;
+ }
+
+ foreach(T targetComponent in target.GetComponentsInChildren<T>(true))
+ {
+ if(targetComponent == null || targetComponent.gameObject == null)
+ {
+ continue;
+ }
+
+ #if VERBOSE_COMPONENT_REMOVAL
+ Debug.LogWarningFormat("Removing {0} comp from {1}", targetComponent.GetType().Name, targetComponent.gameObject.name);
+ #endif
+
+ yield return RemoveComponent(targetComponent);
+ }
+ }
+
+ protected static IEnumerator RemoveComponent(Component targetComponent)
+ {
+ yield return RemoveDependencies(targetComponent);
+
+ Destroy(targetComponent);
+ yield return null;
+ }
+
+ protected static IEnumerator RemoveDependencies(Component targetComponent)
+ {
+ if(targetComponent == null)
+ {
+ yield break;
+ }
+
+ Component[] siblingComponents = targetComponent.GetComponents<Component>();
+ if(siblingComponents == null || siblingComponents.Length == 0)
+ {
+ yield break;
+ }
+
+ System.Type componentType = targetComponent.GetType();
+ foreach(Component siblingComponent in siblingComponents)
+ {
+ if(siblingComponent == null)
+ {
+ continue;
+ }
+
+ bool deleteMe = false;
+ object[] requireComponentAttributes = siblingComponent.GetType().GetCustomAttributes(typeof(RequireComponent), true);
+ if(requireComponentAttributes.Length == 0)
+ {
+ continue;
+ }
+
+ foreach(var requireComponentObject in requireComponentAttributes)
+ {
+ RequireComponent requireComponentAttribute = requireComponentObject as RequireComponent;
+ if(requireComponentAttribute == null)
+ {
+ continue;
+ }
+
+ if(
+ requireComponentAttribute.m_Type0 != componentType &&
+ requireComponentAttribute.m_Type1 != componentType &&
+ requireComponentAttribute.m_Type2 != componentType
+ )
+ {
+ continue;
+ }
+
+ deleteMe = true;
+ break;
+ }
+
+ if(!deleteMe)
+ {
+ continue;
+ }
+
+ #if VERBOSE_COMPONENT_REMOVAL
+ Debug.LogWarningFormat("Deleting component dependency {0} found on {1}", siblingComponent.GetType().Name, targetComponent.gameObject.name);
+ #endif
+
+ yield return RemoveComponent(siblingComponent);
+ }
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs.meta
new file mode 100644
index 00000000..6987057b
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Filters/AbstractPerformanceFilter.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: abda65e062e44213aa3e1f4c82b400a8
+timeCreated: 1563937347 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs
new file mode 100644
index 00000000..e5acd99f
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs
@@ -0,0 +1,33 @@
+using UnityEngine;
+
+namespace VRC.SDKBase.Validation.Performance
+{
+ public static class MeshUtils
+ {
+ private const uint INDICES_PER_TRIANGLE = 3U;
+
+ public static uint GetMeshTriangleCount(Mesh sourceMesh)
+ {
+ if(sourceMesh == null)
+ {
+ return 0;
+ }
+
+ // We can't use GetIndexCount if the mesh isn't readable so just return a huge number.
+ // The SDK Control Panel should show a warning in this case.
+ if(!sourceMesh.isReadable)
+ {
+ return uint.MaxValue;
+ }
+
+ uint count = 0;
+ for(int i = 0; i < sourceMesh.subMeshCount; i++)
+ {
+ uint indexCount = sourceMesh.GetIndexCount(i);
+ count += indexCount / INDICES_PER_TRIANGLE;
+ }
+
+ return count;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs.meta
new file mode 100644
index 00000000..4658d3db
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/MeshUtils.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: f28c978154794266b38d686139c6b872
+timeCreated: 1561249515 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs
new file mode 100644
index 00000000..b2637485
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs
@@ -0,0 +1,46 @@
+using System.Collections;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Filters;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New PerformanceFilterSet",
+ menuName = "VRC Scriptable Objects/Performance/PerformanceFilterSet"
+ )]
+ #endif
+ public class PerformanceFilterSet : ScriptableObject
+ {
+ public AbstractPerformanceFilter[] performanceFilters;
+
+ public IEnumerator ApplyPerformanceFilters(
+ GameObject avatarObject,
+ AvatarPerformanceStats perfStats,
+ PerformanceRating ratingLimit,
+ AvatarPerformance.IgnoreDelegate shouldIgnoreComponent,
+ AvatarPerformance.FilterBlockCallback onBlock
+ )
+ {
+ foreach(AbstractPerformanceFilter performanceFilter in performanceFilters)
+ {
+ if(performanceFilter == null)
+ {
+ continue;
+ }
+
+ bool avatarBlocked = false;
+ yield return performanceFilter.ApplyPerformanceFilter(avatarObject, perfStats, ratingLimit, shouldIgnoreComponent, () => { avatarBlocked = true; });
+
+ if(!avatarBlocked)
+ {
+ continue;
+ }
+
+ onBlock();
+ break;
+ }
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs.meta
new file mode 100644
index 00000000..dce1427b
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceFilterSet.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8cdca9d06d1b4732b9ccb82a38bb8d9c
+timeCreated: 1563939583 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs
new file mode 100644
index 00000000..d132fe4a
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs
@@ -0,0 +1,12 @@
+namespace VRC.SDKBase.Validation.Performance
+{
+ public enum PerformanceInfoDisplayLevel
+ {
+ None,
+
+ Verbose,
+ Info,
+ Warning,
+ Error
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs.meta
new file mode 100644
index 00000000..c1ea78c5
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceInfoDisplayLevel.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a5ed7498cb1a46c78eab031f5f32448c
+timeCreated: 1561267918 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs
new file mode 100644
index 00000000..901d8494
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs
@@ -0,0 +1,12 @@
+namespace VRC.SDKBase.Validation.Performance
+{
+ public enum PerformanceRating
+ {
+ None = 0,
+ Excellent = 1,
+ Good = 2,
+ Medium = 3,
+ Poor = 4,
+ VeryPoor = 5
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs.meta
new file mode 100644
index 00000000..0175bfdb
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceRating.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 5019a55ee9e2404c81bc61a39a010d8d
+timeCreated: 1561267904 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs
new file mode 100644
index 00000000..716c66f7
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs
@@ -0,0 +1,44 @@
+using System.Collections;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Scanners;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New PerformanceScannerSet",
+ menuName = "VRC Scriptable Objects/Performance/PerformanceScannerSet"
+ )]
+ #endif
+ public class PerformanceScannerSet : ScriptableObject
+ {
+ public AbstractPerformanceScanner[] performanceScanners;
+
+ public void RunPerformanceScan(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ foreach(AbstractPerformanceScanner performanceScanner in performanceScanners)
+ {
+ if(performanceScanner == null)
+ {
+ continue;
+ }
+
+ performanceScanner.RunPerformanceScan(avatarObject, perfStats, shouldIgnoreComponent);
+ }
+ }
+
+ public IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ foreach(AbstractPerformanceScanner performanceScanner in performanceScanners)
+ {
+ if(performanceScanner == null)
+ {
+ continue;
+ }
+
+ yield return performanceScanner.RunPerformanceScanEnumerator(avatarObject, perfStats, shouldIgnoreComponent);
+ }
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs.meta
new file mode 100644
index 00000000..7a3b582a
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/PerformanceScannerSet.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 4afb61f36d144fc381114cd7f78d13e7
+timeCreated: 1561259704 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners.meta
new file mode 100644
index 00000000..3029e53c
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: edddcc1b6de3f554e8a13a0b94d47b96
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs
new file mode 100644
index 00000000..9face97c
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Profiling;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ public abstract class AbstractPerformanceScanner : ScriptableObject
+ {
+ private const int MAXIMUM_COMPONENT_SCANS_PER_FRAME = 10;
+ private static int _componentScansThisFrame = 0;
+ private static int _componentScansFrameNumber = 0;
+
+ private readonly Stack<IEnumerator> _coroutines = new Stack<IEnumerator>();
+
+ private bool _limitComponentScansPerFrame = true;
+
+ public abstract IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent);
+
+ public void RunPerformanceScan(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ _limitComponentScansPerFrame = false;
+
+ try
+ {
+ _coroutines.Push(RunPerformanceScanEnumerator(avatarObject, perfStats, shouldIgnoreComponent));
+ while(_coroutines.Count > 0)
+ {
+ IEnumerator currentCoroutine = _coroutines.Peek();
+ if(currentCoroutine.MoveNext())
+ {
+ IEnumerator nestedCoroutine = currentCoroutine.Current as IEnumerator;
+ if(nestedCoroutine != null)
+ {
+ _coroutines.Push(nestedCoroutine);
+ }
+ }
+ else
+ {
+ _coroutines.Pop();
+ }
+ }
+
+ _coroutines.Clear();
+ }
+ finally
+ {
+ _limitComponentScansPerFrame = true;
+ }
+ }
+
+ protected IEnumerator ScanAvatarForComponentsOfType(Type componentType, GameObject avatarObject, List<Component> destinationBuffer)
+ {
+ yield return HandleComponentScansPerFrameLimit();
+
+ Profiler.BeginSample("Component Scan");
+ destinationBuffer.Clear();
+ destinationBuffer.AddRange(avatarObject.GetComponentsInChildren(componentType, true));
+ Profiler.EndSample();
+ }
+
+ protected IEnumerator ScanAvatarForComponentsOfType<T>(GameObject avatarObject, List<T> destinationBuffer)
+ {
+ yield return HandleComponentScansPerFrameLimit();
+
+ Profiler.BeginSample("Component Scan");
+ destinationBuffer.Clear();
+ avatarObject.GetComponentsInChildren(true, destinationBuffer);
+ Profiler.EndSample();
+ yield return null;
+ }
+
+ private IEnumerator HandleComponentScansPerFrameLimit()
+ {
+ if(!_limitComponentScansPerFrame)
+ {
+ yield break;
+ }
+
+ while(_componentScansThisFrame >= MAXIMUM_COMPONENT_SCANS_PER_FRAME)
+ {
+ if(Time.frameCount > _componentScansFrameNumber)
+ {
+ _componentScansFrameNumber = Time.frameCount;
+ _componentScansThisFrame = 0;
+ break;
+ }
+
+ yield return null;
+ }
+
+ _componentScansThisFrame++;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs.meta
new file mode 100644
index 00000000..357ef0f0
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AbstractPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0bd0691a021844f49444a04a959d6328
+timeCreated: 1561279121 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs
new file mode 100644
index 00000000..6aa73010
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs
@@ -0,0 +1,44 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New AnimatorPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/AnimatorPerformanceScanner"
+ )]
+ #endif
+ public sealed class AnimatorPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ int animatorCount = 0;
+
+ // Animators
+ List<Animator> animatorBuffer = new List<Animator>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, animatorBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ animatorBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ // ReSharper disable once UselessBinaryOperation
+ animatorCount += animatorBuffer.Count;
+
+ // Animations
+ List<UnityEngine.Animation> animationBuffer = new List<UnityEngine.Animation>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, animationBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ animationBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ animatorCount += animationBuffer.Count;
+
+ perfStats.animatorCount = animatorCount;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs.meta
new file mode 100644
index 00000000..edec0a0f
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AnimatorPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 08c8e931d0544866a0f626855d9c1841
+timeCreated: 1561251363 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs
new file mode 100644
index 00000000..a8a5944d
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs
@@ -0,0 +1,29 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New AudioPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/AudioPerformanceScanner"
+ )]
+ #endif
+ public sealed class AudioPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Audio Sources
+ List<AudioSource> audioSourceBuffer = new List<AudioSource>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, audioSourceBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ audioSourceBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ perfStats.audioSourceCount = audioSourceBuffer.Count;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs.meta
new file mode 100644
index 00000000..6cbd2a09
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/AudioPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: b3a8bba736414d1aaa9e766da27b56b5
+timeCreated: 1561255618 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs
new file mode 100644
index 00000000..d8332184
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs
@@ -0,0 +1,48 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Profiling;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New ClothPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/ClothPerformanceScanner"
+ )]
+ #endif
+ public sealed class ClothPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Cloth
+ List<Cloth> clothBuffer = new List<Cloth>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, clothBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ clothBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ int totalClothVertices = 0;
+ foreach(Cloth cloth in clothBuffer)
+ {
+ if(cloth == null)
+ {
+ continue;
+ }
+
+ Vector3[] clothVertices = cloth.vertices;
+ if(clothVertices == null)
+ {
+ continue;
+ }
+
+ totalClothVertices += clothVertices.Length;
+ }
+
+ perfStats.clothCount = clothBuffer.Count;
+ perfStats.clothMaxVertices = totalClothVertices;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs.meta
new file mode 100644
index 00000000..b04d8aef
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ClothPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 0cec88b5a46f459195f10a2f11fddb2f
+timeCreated: 1561255843 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs
new file mode 100644
index 00000000..860df33a
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
+using UnityEngine.Profiling;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New DynamicBonePerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/DynamicBonePerformanceScanner"
+ )]
+ #endif
+ public sealed class DynamicBonePerformanceScanner : AbstractPerformanceScanner
+ {
+ private Type _dynamicBoneType;
+ private FieldInfo _dynamicBoneRootFieldInfo;
+ private FieldInfo _dynamicBoneExclusionsFieldInfo;
+ private FieldInfo _dynamicBoneCollidersFieldInfo;
+ private FieldInfo _dynamicBoneEndLengthFieldInfo;
+ private FieldInfo _dynamicBoneEndOffsetFieldInfo;
+
+ private void Awake()
+ {
+ FindDynamicBoneTypes();
+ }
+
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ if(_dynamicBoneType == null)
+ {
+ yield break;
+ }
+
+ // Dynamic Bone as Component
+ List<Component> dynamicBoneComponentBuffer = new List<Component>();
+ List<object> dynamicBoneColliderObjectBuffer = new List<object>();
+ yield return ScanAvatarForComponentsOfType(_dynamicBoneType, avatarObject, dynamicBoneComponentBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ dynamicBoneComponentBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ int totalSimulatedBoneCount = 0;
+ int totalCollisionChecks = 0;
+
+ Profiler.BeginSample("Analyze Dynamic Bones");
+ foreach(Component dynamicBone in dynamicBoneComponentBuffer)
+ {
+ Profiler.BeginSample("Single Dynamic Bone Component");
+ int simulatedBones = 0;
+
+ // Add extra bones to the end of each chain if end bones are being used.
+ float endLength = (float)_dynamicBoneEndLengthFieldInfo.GetValue(dynamicBone);
+ Vector3 endOffset = (Vector3)_dynamicBoneEndOffsetFieldInfo.GetValue(dynamicBone);
+ bool hasEndBones = endLength > 0 || endOffset != Vector3.zero;
+
+ Transform root = (Transform)_dynamicBoneRootFieldInfo.GetValue(dynamicBone);
+ if(root != null)
+ {
+ List<Transform> exclusions = (List<Transform>)_dynamicBoneExclusionsFieldInfo.GetValue(dynamicBone);
+
+ // Calculate number of simulated bones for the hierarchy
+ simulatedBones = CountTransformsRecursively(root, exclusions, hasEndBones);
+ totalSimulatedBoneCount += simulatedBones;
+ }
+
+ int colliderListEntryCount = 0;
+ IList colliderList = (IList)_dynamicBoneCollidersFieldInfo.GetValue(dynamicBone);
+ if(colliderList != null)
+ {
+ foreach(object collider in colliderList)
+ {
+ colliderListEntryCount += 1;
+ if(collider != null && !dynamicBoneColliderObjectBuffer.Contains(collider))
+ {
+ dynamicBoneColliderObjectBuffer.Add(collider);
+ }
+ }
+ }
+
+ // The root bone is skipped in collision checks.
+ totalCollisionChecks += (simulatedBones - 1) * colliderListEntryCount;
+ Profiler.EndSample();
+ }
+
+ Profiler.EndSample();
+
+ yield return null;
+
+ perfStats.dynamicBoneComponentCount = dynamicBoneComponentBuffer.Count;
+ perfStats.dynamicBoneSimulatedBoneCount = totalSimulatedBoneCount;
+ perfStats.dynamicBoneColliderCount = dynamicBoneColliderObjectBuffer.Count;
+ perfStats.dynamicBoneCollisionCheckCount = totalCollisionChecks;
+ }
+
+ private void FindDynamicBoneTypes()
+ {
+ if(_dynamicBoneType != null)
+ {
+ return;
+ }
+
+ Type dyBoneType = ValidationUtils.GetTypeFromName("DynamicBone");
+ if(dyBoneType == null)
+ {
+ return;
+ }
+
+ Type dyBoneColliderType = ValidationUtils.GetTypeFromName("DynamicBoneColliderBase") ?? ValidationUtils.GetTypeFromName("DynamicBoneCollider");
+ if(dyBoneColliderType == null)
+ {
+ return;
+ }
+
+ FieldInfo rootFieldInfo = dyBoneType.GetField("m_Root", BindingFlags.Public | BindingFlags.Instance);
+ if(rootFieldInfo == null || rootFieldInfo.FieldType != typeof(Transform))
+ {
+ return;
+ }
+
+ FieldInfo exclusionsFieldInfo = dyBoneType.GetField("m_Exclusions", BindingFlags.Public | BindingFlags.Instance);
+ if(exclusionsFieldInfo == null || exclusionsFieldInfo.FieldType != typeof(List<Transform>))
+ {
+ return;
+ }
+
+ FieldInfo collidersFieldInfo = dyBoneType.GetField("m_Colliders", BindingFlags.Public | BindingFlags.Instance);
+ if(collidersFieldInfo == null || collidersFieldInfo.FieldType.GetGenericTypeDefinition() != typeof(List<>) ||
+ collidersFieldInfo.FieldType.GetGenericArguments().Single() != dyBoneColliderType)
+ {
+ return;
+ }
+
+ FieldInfo endLengthFieldInfo = dyBoneType.GetField("m_EndLength", BindingFlags.Public | BindingFlags.Instance);
+ if(endLengthFieldInfo == null || endLengthFieldInfo.FieldType != typeof(float))
+ {
+ return;
+ }
+
+ FieldInfo endOffsetFieldInfo = dyBoneType.GetField("m_EndOffset", BindingFlags.Public | BindingFlags.Instance);
+ if(endOffsetFieldInfo == null || endOffsetFieldInfo.FieldType != typeof(Vector3))
+ {
+ return;
+ }
+
+ _dynamicBoneType = dyBoneType;
+ _dynamicBoneRootFieldInfo = rootFieldInfo;
+ _dynamicBoneExclusionsFieldInfo = exclusionsFieldInfo;
+ _dynamicBoneCollidersFieldInfo = collidersFieldInfo;
+ _dynamicBoneEndLengthFieldInfo = endLengthFieldInfo;
+ _dynamicBoneEndOffsetFieldInfo = endOffsetFieldInfo;
+ }
+
+ // Like DynamicBone itself exclusions only apply to children of the current bone.
+ // This means the root bone itself never excluded.
+ private static int CountTransformsRecursively(Transform transform, List<Transform> exclusions, bool addEndBones)
+ {
+ if(transform == null)
+ {
+ return 0;
+ }
+
+ int count = 1;
+ int childCount = transform.childCount;
+ if(childCount > 0)
+ {
+ foreach(Transform child in transform)
+ {
+ if(exclusions == null || !exclusions.Contains(child))
+ {
+ count += CountTransformsRecursively(child, exclusions, addEndBones);
+ }
+ }
+ }
+ else
+ {
+ if(addEndBones)
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs.meta
new file mode 100644
index 00000000..60f0db5a
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/DynamicBonePerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: a226df494ef04404a9a47c714822ab9f
+timeCreated: 1561251560 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs
new file mode 100644
index 00000000..8d86c6b5
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs
@@ -0,0 +1,29 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New LightPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/LightPerformanceScanner"
+ )]
+ #endif
+ public sealed class LightPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Lights
+ List<Light> lightBuffer = new List<Light>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, lightBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ lightBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ perfStats.lightCount = lightBuffer.Count;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs.meta
new file mode 100644
index 00000000..9c346056
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LightPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 405778fdc32c44c1bb9fdd0476fb0007
+timeCreated: 1561256390 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs
new file mode 100644
index 00000000..1d1faa85
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs
@@ -0,0 +1,31 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New LineRendererPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/LineRendererPerformanceScanner"
+ )]
+ #endif
+ public sealed class LineRendererPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Line Renderers
+ List<LineRenderer> lineRendererBuffer = new List<LineRenderer>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, lineRendererBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ lineRendererBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ int numLineRenderers = lineRendererBuffer.Count;
+ perfStats.lineRendererCount = numLineRenderers;
+ perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + numLineRenderers;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs.meta
new file mode 100644
index 00000000..79875a56
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/LineRendererPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ec87392b85844f7bb526a48ec866a8f0
+timeCreated: 1561253047 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs
new file mode 100644
index 00000000..9b5b1201
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs
@@ -0,0 +1,266 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Profiling;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New MeshPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/MeshPerformanceScanner"
+ )]
+ #endif
+ public sealed class MeshPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Renderers
+ List<Renderer> rendererBuffer = new List<Renderer>(16);
+ yield return ScanAvatarForComponentsOfType(avatarObject, rendererBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ rendererBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ yield return AnalyzeGeometry(avatarObject, rendererBuffer, perfStats);
+ AnalyzeMeshRenderers(rendererBuffer, perfStats);
+ AnalyzeSkinnedMeshRenderers(rendererBuffer, perfStats);
+
+
+ yield return null;
+ }
+
+ private static uint CalculateRendererPolyCount(Renderer renderer)
+ {
+ Mesh sharedMesh = null;
+ SkinnedMeshRenderer skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
+ if(skinnedMeshRenderer != null)
+ {
+ sharedMesh = skinnedMeshRenderer.sharedMesh;
+ }
+
+ if(sharedMesh == null)
+ {
+ MeshRenderer meshRenderer = renderer as MeshRenderer;
+ if(meshRenderer != null)
+ {
+ MeshFilter meshFilter = meshRenderer.GetComponent<MeshFilter>();
+ if(meshFilter != null)
+ {
+ sharedMesh = meshFilter.sharedMesh;
+ }
+ }
+ }
+
+ if(sharedMesh == null)
+ {
+ return 0;
+ }
+
+ return MeshUtils.GetMeshTriangleCount(sharedMesh);
+ }
+
+ private static bool RendererHasMesh(Renderer renderer)
+ {
+ MeshRenderer meshRenderer = renderer as MeshRenderer;
+ if(meshRenderer != null)
+ {
+ MeshFilter meshFilter = meshRenderer.GetComponent<MeshFilter>();
+ if(meshFilter == null)
+ {
+ return false;
+ }
+
+ return meshFilter.sharedMesh != null;
+ }
+
+ SkinnedMeshRenderer skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
+ if(skinnedMeshRenderer != null)
+ {
+ return skinnedMeshRenderer.sharedMesh != null;
+ }
+
+ return false;
+ }
+
+ private IEnumerator AnalyzeGeometry(GameObject avatarObject, IEnumerable<Renderer> renderers, AvatarPerformanceStats perfStats)
+ {
+ List<Renderer> lodGroupRendererIgnoreBuffer = new List<Renderer>(16);
+ List<LODGroup> lodBuffer = new List<LODGroup>(16);
+
+ ulong polyCount = 0;
+ Bounds bounds = new Bounds(avatarObject.transform.position, Vector3.zero);
+
+ yield return ScanAvatarForComponentsOfType(avatarObject, lodBuffer);
+ try
+ {
+ foreach(LODGroup lodGroup in lodBuffer)
+ {
+ LOD[] lodLevels = lodGroup.GetLODs();
+
+ ulong highestLodPolyCount = 0;
+ foreach(LOD lod in lodLevels)
+ {
+ uint thisLodPolyCount = 0;
+ foreach(Renderer renderer in lod.renderers)
+ {
+ lodGroupRendererIgnoreBuffer.Add(renderer);
+ checked
+ {
+ thisLodPolyCount += CalculateRendererPolyCount(renderer);
+ }
+ }
+
+ if(thisLodPolyCount > highestLodPolyCount)
+ {
+ highestLodPolyCount = thisLodPolyCount;
+ }
+ }
+
+ checked
+ {
+ polyCount += highestLodPolyCount;
+ }
+ }
+ }
+ catch(OverflowException e)
+ {
+ VRC.Core.Logger.Log("Overflow exception while analyzing geometry, assuming max value:" + e.ToString(), VRC.Core.DebugLevel.All);
+ polyCount = uint.MaxValue;
+ }
+
+ Profiler.BeginSample("Calculate Total Polygon Count and Bounds");
+ foreach(Renderer renderer in renderers)
+ {
+ Profiler.BeginSample("Single Renderer");
+ if(renderer is MeshRenderer || renderer is SkinnedMeshRenderer)
+ {
+ if(!RendererHasMesh(renderer))
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ bounds.Encapsulate(renderer.bounds);
+ }
+
+ if(lodGroupRendererIgnoreBuffer.Contains(renderer))
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ polyCount += CalculateRendererPolyCount(renderer);
+ Profiler.EndSample();
+ }
+
+ Profiler.EndSample();
+
+ bounds.center -= avatarObject.transform.position;
+
+ lodGroupRendererIgnoreBuffer.Clear();
+ lodBuffer.Clear();
+
+ perfStats.polyCount = polyCount > int.MaxValue ? int.MaxValue : (int)polyCount;
+ perfStats.aabb = bounds;
+ }
+
+ private static void AnalyzeSkinnedMeshRenderers(IEnumerable<Renderer> renderers, AvatarPerformanceStats perfStats)
+ {
+ Profiler.BeginSample("AnalyzeSkinnedMeshRenderers");
+ int count = 0;
+ int materialSlots = 0;
+ int skinnedBoneCount = 0;
+ HashSet<Transform> transformIgnoreBuffer = new HashSet<Transform>();
+
+ foreach(Renderer renderer in renderers)
+ {
+ Profiler.BeginSample("Analyze SkinnedMeshRenderer");
+ SkinnedMeshRenderer skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
+ if(skinnedMeshRenderer == null)
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ count++;
+
+ Mesh sharedMesh = skinnedMeshRenderer.sharedMesh;
+ if(sharedMesh != null)
+ {
+ materialSlots += sharedMesh.subMeshCount;
+ }
+
+ // bone count
+ Profiler.BeginSample("Count Bones");
+ Transform[] bones = skinnedMeshRenderer.bones;
+ foreach(Transform bone in bones)
+ {
+ Profiler.BeginSample("Count Bone");
+ if(bone == null || transformIgnoreBuffer.Contains(bone))
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ transformIgnoreBuffer.Add(bone);
+ skinnedBoneCount++;
+ Profiler.EndSample();
+ }
+
+ Profiler.EndSample();
+
+ Profiler.EndSample();
+ }
+
+ transformIgnoreBuffer.Clear();
+ Profiler.EndSample();
+
+ perfStats.skinnedMeshCount = count;
+ perfStats.boneCount = skinnedBoneCount;
+ perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + materialSlots;
+ }
+
+ private static void AnalyzeMeshRenderers(IEnumerable<Renderer> renderers, AvatarPerformanceStats perfStats)
+ {
+ Profiler.BeginSample("AnalyzeMeshRenderers");
+ int count = 0;
+ int materialSlots = 0;
+ foreach(Renderer renderer in renderers)
+ {
+ Profiler.BeginSample("Analyze MeshRenderer");
+ MeshRenderer meshRenderer = renderer as MeshRenderer;
+ if(meshRenderer == null)
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ count++;
+
+ Profiler.BeginSample("Get MeshFilter");
+ MeshFilter meshFilter = meshRenderer.GetComponent<MeshFilter>();
+ Profiler.EndSample();
+ if(meshFilter == null)
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ Mesh sharedMesh = meshFilter.sharedMesh;
+ if(sharedMesh != null)
+ {
+ materialSlots += sharedMesh.subMeshCount;
+ }
+ }
+
+ Profiler.EndSample();
+
+ perfStats.meshCount = count;
+ perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + materialSlots;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs.meta
new file mode 100644
index 00000000..ba7c1507
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/MeshPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 38bca10261df4ddfa10cff3b3bbb9428
+timeCreated: 1561249198 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs
new file mode 100644
index 00000000..3d7acea8
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs
@@ -0,0 +1,114 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Profiling;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New ParticlePerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/ParticlePerformanceScanner"
+ )]
+ #endif
+ public sealed class ParticlePerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Particle Systems
+ List<ParticleSystem> particleSystemBuffer = new List<ParticleSystem>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, particleSystemBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ particleSystemBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ AnalyzeParticleSystemRenderers(particleSystemBuffer, perfStats);
+
+ yield return null;
+ }
+
+ private static void AnalyzeParticleSystemRenderers(IEnumerable<ParticleSystem> particleSystems, AvatarPerformanceStats perfStats)
+ {
+ int particleSystemCount = 0;
+ ulong particleTotalCount = 0;
+ ulong particleTotalMaxMeshPolyCount = 0;
+ bool particleTrailsEnabled = false;
+ bool particleCollisionEnabled = false;
+ int materialSlots = 0;
+
+ Profiler.BeginSample("AnalyzeParticleSystemRenderers");
+ foreach(ParticleSystem particleSystem in particleSystems)
+ {
+ Profiler.BeginSample("Single Particle System");
+ int particleCount = particleSystem.main.maxParticles;
+ if(particleCount <= 0)
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ particleSystemCount++;
+ particleTotalCount += (uint)particleCount;
+
+ ParticleSystemRenderer particleSystemRenderer = particleSystem.GetComponent<ParticleSystemRenderer>();
+ if(particleSystemRenderer == null)
+ {
+ Profiler.EndSample();
+ continue;
+ }
+
+ materialSlots++;
+
+ // mesh particles
+ if(particleSystemRenderer.renderMode == ParticleSystemRenderMode.Mesh && particleSystemRenderer.meshCount > 0)
+ {
+ uint highestPolyCount = 0;
+
+ Mesh[] meshes = new Mesh[particleSystemRenderer.meshCount];
+ int particleRendererMeshCount = particleSystemRenderer.GetMeshes(meshes);
+ for(int meshIndex = 0; meshIndex < particleRendererMeshCount; meshIndex++)
+ {
+ Mesh mesh = meshes[meshIndex];
+ if(mesh == null)
+ {
+ continue;
+ }
+
+ uint polyCount = MeshUtils.GetMeshTriangleCount(mesh);
+ if(polyCount > highestPolyCount)
+ {
+ highestPolyCount = polyCount;
+ }
+ }
+
+ ulong maxMeshParticlePolyCount = (uint)particleCount * highestPolyCount;
+ particleTotalMaxMeshPolyCount += maxMeshParticlePolyCount;
+ }
+
+ if(particleSystem.trails.enabled)
+ {
+ particleTrailsEnabled = true;
+ materialSlots++;
+ }
+
+ if(particleSystem.collision.enabled)
+ {
+ particleCollisionEnabled = true;
+ }
+
+ Profiler.EndSample();
+ }
+
+ Profiler.EndSample();
+
+ perfStats.particleSystemCount = particleSystemCount;
+ perfStats.particleTotalCount = particleTotalCount > int.MaxValue ? int.MaxValue : (int)particleTotalCount;
+ perfStats.particleMaxMeshPolyCount = particleTotalMaxMeshPolyCount > int.MaxValue ? int.MaxValue : (int)particleTotalMaxMeshPolyCount;
+ perfStats.particleTrailsEnabled = particleTrailsEnabled;
+ perfStats.particleCollisionEnabled = particleCollisionEnabled;
+ perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + materialSlots;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs.meta
new file mode 100644
index 00000000..54556038
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/ParticlePerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 10723e354ff14f98a49ab797b3f005e6
+timeCreated: 1561250267 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs
new file mode 100644
index 00000000..c6b643da
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs
@@ -0,0 +1,64 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Profiling;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New PhysicsPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/PhysicsPerformanceScanner"
+ )]
+ #endif
+ public sealed class PhysicsPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Colliders
+ List<Collider> colliderBuffer = new List<Collider>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, colliderBuffer);
+ colliderBuffer.RemoveAll(
+ o =>
+ {
+ if(shouldIgnoreComponent != null && shouldIgnoreComponent(o))
+ {
+ return true;
+ }
+
+ if(o.GetComponent<VRC.SDKBase.VRCStation>() != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ );
+
+ perfStats.physicsColliderCount = colliderBuffer.Count;
+
+ // Rigidbodies
+ List<Rigidbody> rigidbodyBuffer = new List<Rigidbody>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, rigidbodyBuffer);
+ rigidbodyBuffer.RemoveAll(
+ o =>
+ {
+ if(shouldIgnoreComponent != null && shouldIgnoreComponent(o))
+ {
+ return true;
+ }
+
+ if(o.GetComponent<VRC.SDKBase.VRCStation>() != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ );
+
+ perfStats.physicsRigidbodyCount = rigidbodyBuffer.Count;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs.meta
new file mode 100644
index 00000000..ed550cf1
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/PhysicsPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 6a94ecdeecd04f85824cc3244be5712a
+timeCreated: 1561256481 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs
new file mode 100644
index 00000000..c75e4cae
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs
@@ -0,0 +1,31 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using VRC.SDKBase.Validation.Performance.Stats;
+
+namespace VRC.SDKBase.Validation.Performance.Scanners
+{
+ #if VRC_CLIENT
+ [CreateAssetMenu(
+ fileName = "New TrailRendererPerformanceScanner",
+ menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/TrailRendererPerformanceScanner"
+ )]
+ #endif
+ public sealed class TrailRendererPerformanceScanner : AbstractPerformanceScanner
+ {
+ public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
+ {
+ // Trail Renderers
+ List<TrailRenderer> trailRendererBuffer = new List<TrailRenderer>();
+ yield return ScanAvatarForComponentsOfType(avatarObject, trailRendererBuffer);
+ if(shouldIgnoreComponent != null)
+ {
+ trailRendererBuffer.RemoveAll(c => shouldIgnoreComponent(c));
+ }
+
+ int numTrailRenderers = trailRendererBuffer.Count;
+ perfStats.trailRendererCount = numTrailRenderers;
+ perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + numTrailRenderers;
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs.meta
new file mode 100644
index 00000000..028dd032
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Scanners/TrailRendererPerformanceScanner.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 2efd714b564547b4be1ebd1f2700668b
+timeCreated: 1561251810 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats.meta
new file mode 100644
index 00000000..39e091dc
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6970bf241e7266748992480464b59687
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs
new file mode 100644
index 00000000..44fd4041
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs
@@ -0,0 +1,757 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using UnityEngine;
+
+namespace VRC.SDKBase.Validation.Performance.Stats
+{
+ public class AvatarPerformanceStats
+ {
+ private delegate int ComparePerformanceStatsDelegate(AvatarPerformanceStats stats, AvatarPerformanceStatsLevel statsLevel);
+
+ #region Public Fields
+
+ public string avatarName;
+
+ public int? polyCount;
+ public Bounds? aabb;
+ public int? skinnedMeshCount;
+ public int? meshCount;
+ public int? materialCount;
+ public int? animatorCount;
+ public int? boneCount;
+ public int? lightCount;
+ public int? particleSystemCount;
+ public int? particleTotalCount;
+ public int? particleMaxMeshPolyCount;
+ public bool? particleTrailsEnabled;
+ public bool? particleCollisionEnabled;
+ public int? trailRendererCount;
+ public int? lineRendererCount;
+ public int? dynamicBoneComponentCount;
+ public int? dynamicBoneSimulatedBoneCount;
+ public int? dynamicBoneColliderCount;
+ public int? dynamicBoneCollisionCheckCount; // number of collider simulated bones excluding the root multiplied by the number of colliders
+ public int? clothCount;
+ public int? clothMaxVertices;
+ public int? physicsColliderCount;
+ public int? physicsRigidbodyCount;
+ public int? audioSourceCount;
+ public float? downloadSize;
+
+ #endregion
+
+ #region Private Fields
+
+ private readonly PerformanceRating[] _performanceRatingCache;
+
+ private static readonly ImmutableArray<AvatarPerformanceCategory> _performanceCategories = Enum.GetValues(typeof(AvatarPerformanceCategory))
+ .Cast<AvatarPerformanceCategory>()
+ .ToImmutableArray();
+
+ private static readonly Dictionary<AvatarPerformanceCategory, string> _performanceCategoryDisplayNames = new Dictionary<AvatarPerformanceCategory, string>
+ {
+ {AvatarPerformanceCategory.PolyCount, "Polygons"},
+ {AvatarPerformanceCategory.AABB, "Bounds"},
+ {AvatarPerformanceCategory.SkinnedMeshCount, "Skinned Meshes"},
+ {AvatarPerformanceCategory.MeshCount, "Meshes"},
+ {AvatarPerformanceCategory.MaterialCount, "Material Slots"},
+ {AvatarPerformanceCategory.AnimatorCount, "Animators"},
+ {AvatarPerformanceCategory.BoneCount, "Bones"},
+ {AvatarPerformanceCategory.LightCount, "Lights"},
+ {AvatarPerformanceCategory.ParticleSystemCount, "Particle Systems"},
+ {AvatarPerformanceCategory.ParticleTotalCount, "Total Max Particles"},
+ {AvatarPerformanceCategory.ParticleMaxMeshPolyCount, "Mesh Particle Max Polygons"},
+ {AvatarPerformanceCategory.ParticleTrailsEnabled, "Particle Trails Enabled"},
+ {AvatarPerformanceCategory.ParticleCollisionEnabled, "Particle Collision Enabled"},
+ {AvatarPerformanceCategory.TrailRendererCount, "Trail Renderers"},
+ {AvatarPerformanceCategory.LineRendererCount, "Line Renderers"},
+ {AvatarPerformanceCategory.DynamicBoneComponentCount, "Dynamic Bone Components"},
+ {AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount, "Dynamic Bone Transforms"},
+ {AvatarPerformanceCategory.DynamicBoneColliderCount, "Dynamic Bone Colliders"},
+ {AvatarPerformanceCategory.DynamicBoneCollisionCheckCount, "Dynamic Bone Collision Check Count"},
+ {AvatarPerformanceCategory.ClothCount, "Cloths"},
+ {AvatarPerformanceCategory.ClothMaxVertices, "Total Cloth Vertices"},
+ {AvatarPerformanceCategory.PhysicsColliderCount, "Physics Colliders"},
+ {AvatarPerformanceCategory.PhysicsRigidbodyCount, "Physics Rigidbodies"},
+ {AvatarPerformanceCategory.AudioSourceCount, "Audio Sources"},
+ {AvatarPerformanceCategory.DownloadSize, "Download Size"},
+ };
+
+ private static readonly Dictionary<PerformanceRating, string> _performanceRatingDisplayNames = new Dictionary<PerformanceRating, string>
+ {
+ {PerformanceRating.None, "None"},
+ {PerformanceRating.Excellent, "Excellent"},
+ {PerformanceRating.Good, "Good"},
+ {PerformanceRating.Medium, "Medium"},
+ {PerformanceRating.Poor, "Poor"},
+ {PerformanceRating.VeryPoor, "VeryPoor"}
+ };
+
+ #endregion
+
+ #region Initialization
+
+ private static AvatarPerformanceStatsLevelSet _performanceStatsLevelSet = null;
+
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
+ public static void Initialize()
+ {
+ if(_performanceStatsLevelSet != null)
+ {
+ return;
+ }
+
+ _performanceStatsLevelSet = Resources.Load<AvatarPerformanceStatsLevelSet>(GetPlatformPerformanceStatLevels());
+ }
+
+ private static string GetPlatformPerformanceStatLevels()
+ {
+ #if UNITY_ANDROID
+ return "Validation/Performance/StatsLevels/Quest/AvatarPerformanceStatLevels_Quest";
+ #else
+ return "Validation/Performance/StatsLevels/Windows/AvatarPerformanceStatLevels_Windows";
+ #endif
+ }
+
+ #endregion
+
+ #region Constructors
+
+ public AvatarPerformanceStats()
+ {
+ _performanceRatingCache = new PerformanceRating[(int)AvatarPerformanceCategory.AvatarPerformanceCategoryCount];
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public void Reset()
+ {
+ avatarName = null;
+ polyCount = null;
+ aabb = null;
+ skinnedMeshCount = null;
+ meshCount = null;
+ materialCount = null;
+ animatorCount = null;
+ boneCount = null;
+ lightCount = null;
+ particleSystemCount = null;
+ particleTotalCount = null;
+ particleMaxMeshPolyCount = null;
+ particleTrailsEnabled = null;
+ particleCollisionEnabled = null;
+ trailRendererCount = null;
+ lineRendererCount = null;
+ dynamicBoneComponentCount = null;
+ dynamicBoneSimulatedBoneCount = null;
+ dynamicBoneColliderCount = null;
+ dynamicBoneCollisionCheckCount = null;
+ clothCount = null;
+ clothMaxVertices = null;
+ physicsColliderCount = null;
+ physicsRigidbodyCount = null;
+ audioSourceCount = null;
+ downloadSize = null;
+
+ for(int i = 0; i < (int)AvatarPerformanceCategory.AvatarPerformanceCategoryCount; i++)
+ {
+ _performanceRatingCache[i] = PerformanceRating.None;
+ }
+ }
+
+ public Snapshot GetSnapshot()
+ {
+ return new Snapshot(this);
+ }
+
+ public PerformanceRating GetPerformanceRatingForCategory(AvatarPerformanceCategory perfCategory)
+ {
+ if(_performanceRatingCache[(int)perfCategory] == PerformanceRating.None)
+ {
+ _performanceRatingCache[(int)perfCategory] = CalculatePerformanceRatingForCategory(perfCategory);
+ }
+
+ return _performanceRatingCache[(int)perfCategory];
+ }
+
+ public void CalculateAllPerformanceRatings()
+ {
+ for(int i = 0; i < _performanceRatingCache.Length; i++)
+ {
+ _performanceRatingCache[i] = PerformanceRating.None;
+ }
+
+ foreach(AvatarPerformanceCategory perfCategory in _performanceCategories)
+ {
+ if(perfCategory == AvatarPerformanceCategory.None ||
+ perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount)
+ {
+ continue;
+ }
+
+ if(_performanceRatingCache[(int)perfCategory] == PerformanceRating.None)
+ {
+ _performanceRatingCache[(int)perfCategory] = CalculatePerformanceRatingForCategory(perfCategory);
+ }
+ }
+ }
+
+ public static string GetPerformanceCategoryDisplayName(AvatarPerformanceCategory category)
+ {
+ return _performanceCategoryDisplayNames[category];
+ }
+
+ public static string GetPerformanceRatingDisplayName(PerformanceRating rating)
+ {
+ return _performanceRatingDisplayNames[rating];
+ }
+
+ public static AvatarPerformanceStatsLevel GetStatLevelForRating(PerformanceRating rating)
+ {
+ switch(rating)
+ {
+ case PerformanceRating.None:
+ return _performanceStatsLevelSet.excellent;
+
+ case PerformanceRating.Excellent:
+ return _performanceStatsLevelSet.excellent;
+
+ case PerformanceRating.Good:
+ return _performanceStatsLevelSet.good;
+
+ case PerformanceRating.Medium:
+ return _performanceStatsLevelSet.medium;
+
+ case PerformanceRating.Poor:
+ return _performanceStatsLevelSet.poor;
+
+ case PerformanceRating.VeryPoor:
+ return _performanceStatsLevelSet.poor;
+
+ default:
+ return _performanceStatsLevelSet.excellent;
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private PerformanceRating CalculatePerformanceRatingForCategory(AvatarPerformanceCategory perfCategory)
+ {
+ switch(perfCategory)
+ {
+ case AvatarPerformanceCategory.Overall:
+ {
+ PerformanceRating maxRating = PerformanceRating.None;
+
+ foreach(AvatarPerformanceCategory category in _performanceCategories)
+ {
+ if(category == AvatarPerformanceCategory.None ||
+ category == AvatarPerformanceCategory.Overall ||
+ category == AvatarPerformanceCategory.AvatarPerformanceCategoryCount)
+ {
+ continue;
+ }
+
+ PerformanceRating rating = GetPerformanceRatingForCategory(category);
+ if(rating > maxRating)
+ {
+ maxRating = rating;
+ }
+ }
+
+ return maxRating;
+ }
+ case AvatarPerformanceCategory.PolyCount:
+ {
+ if(!polyCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.polyCount.GetValueOrDefault() - y.polyCount);
+ }
+ case AvatarPerformanceCategory.AABB:
+ {
+ if(!aabb.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating(
+ (x, y) =>
+ ApproxLessOrEqual(y.aabb.extents.x, 0.0f) || // -1 extents means "no AABB limit"
+ (
+ ApproxLessOrEqual(x.aabb.GetValueOrDefault().extents.x, y.aabb.extents.x) &&
+ ApproxLessOrEqual(x.aabb.GetValueOrDefault().extents.y, y.aabb.extents.y) &&
+ ApproxLessOrEqual(x.aabb.GetValueOrDefault().extents.z, y.aabb.extents.z))
+ ? -1
+ : 1
+ );
+ }
+ case AvatarPerformanceCategory.SkinnedMeshCount:
+ {
+ if(!skinnedMeshCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.skinnedMeshCount.GetValueOrDefault() - y.skinnedMeshCount);
+ }
+ case AvatarPerformanceCategory.MeshCount:
+ {
+ if(!meshCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.meshCount.GetValueOrDefault() - y.meshCount);
+ }
+ case AvatarPerformanceCategory.MaterialCount:
+ {
+ if(!materialCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.materialCount.GetValueOrDefault() - y.materialCount);
+ }
+ case AvatarPerformanceCategory.AnimatorCount:
+ {
+ if(!animatorCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.animatorCount.GetValueOrDefault() - y.animatorCount);
+ }
+ case AvatarPerformanceCategory.BoneCount:
+ {
+ if(!boneCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.boneCount.GetValueOrDefault() - y.boneCount);
+ }
+ case AvatarPerformanceCategory.LightCount:
+ {
+ if(!lightCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.lightCount.GetValueOrDefault() - y.lightCount);
+ }
+ case AvatarPerformanceCategory.ParticleSystemCount:
+ {
+ if(!particleSystemCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.particleSystemCount.GetValueOrDefault() - y.particleSystemCount);
+ }
+ case AvatarPerformanceCategory.ParticleTotalCount:
+ {
+ if(!particleTotalCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.particleTotalCount.GetValueOrDefault() - y.particleTotalCount);
+ }
+ case AvatarPerformanceCategory.ParticleMaxMeshPolyCount:
+ {
+ if(!particleMaxMeshPolyCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.particleMaxMeshPolyCount.GetValueOrDefault() - y.particleMaxMeshPolyCount);
+ }
+ case AvatarPerformanceCategory.ParticleTrailsEnabled:
+ {
+ if(!particleTrailsEnabled.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating(
+ (x, y) =>
+ {
+ if(x.particleTrailsEnabled == y.particleTrailsEnabled)
+ {
+ return 0;
+ }
+
+ return x.particleTrailsEnabled.GetValueOrDefault() ? 1 : -1;
+ });
+ }
+ case AvatarPerformanceCategory.ParticleCollisionEnabled:
+ {
+ if(!particleCollisionEnabled.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating(
+ (x, y) =>
+ {
+ if(x.particleCollisionEnabled == y.particleCollisionEnabled)
+ {
+ return 0;
+ }
+
+ return x.particleCollisionEnabled.GetValueOrDefault() ? 1 : -1;
+ });
+ }
+ case AvatarPerformanceCategory.TrailRendererCount:
+ {
+ if(!trailRendererCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.trailRendererCount.GetValueOrDefault() - y.trailRendererCount);
+ }
+ case AvatarPerformanceCategory.LineRendererCount:
+ {
+ if(!lineRendererCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.lineRendererCount.GetValueOrDefault() - y.lineRendererCount);
+ }
+ case AvatarPerformanceCategory.DynamicBoneComponentCount:
+ {
+ if(!dynamicBoneComponentCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.dynamicBoneComponentCount.GetValueOrDefault() - y.dynamicBoneComponentCount);
+ }
+ case AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount:
+ {
+ if(!dynamicBoneSimulatedBoneCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.dynamicBoneSimulatedBoneCount.GetValueOrDefault() - y.dynamicBoneSimulatedBoneCount);
+ }
+ case AvatarPerformanceCategory.DynamicBoneColliderCount:
+ {
+ if(!dynamicBoneColliderCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.dynamicBoneColliderCount.GetValueOrDefault() - y.dynamicBoneColliderCount);
+ }
+ case AvatarPerformanceCategory.DynamicBoneCollisionCheckCount:
+ {
+ if(!dynamicBoneCollisionCheckCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.dynamicBoneCollisionCheckCount.GetValueOrDefault() - y.dynamicBoneCollisionCheckCount);
+ }
+ case AvatarPerformanceCategory.ClothCount:
+ {
+ if(!clothCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.clothCount.GetValueOrDefault() - y.clothCount);
+ }
+ case AvatarPerformanceCategory.ClothMaxVertices:
+ {
+ if(!clothMaxVertices.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.clothMaxVertices.GetValueOrDefault() - y.clothMaxVertices);
+ }
+ case AvatarPerformanceCategory.PhysicsColliderCount:
+ {
+ if(!physicsColliderCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.physicsColliderCount.GetValueOrDefault() - y.physicsColliderCount);
+ }
+ case AvatarPerformanceCategory.PhysicsRigidbodyCount:
+ {
+ if(!physicsRigidbodyCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.physicsRigidbodyCount.GetValueOrDefault() - y.physicsRigidbodyCount);
+ }
+ case AvatarPerformanceCategory.AudioSourceCount:
+ {
+ if(!audioSourceCount.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return CalculatePerformanceRating((x, y) => x.audioSourceCount.GetValueOrDefault() - y.audioSourceCount);
+ }
+ case AvatarPerformanceCategory.DownloadSize:
+ {
+ if(!downloadSize.HasValue)
+ {
+ return PerformanceRating.None;
+ }
+
+ return PerformanceRating.Excellent;
+ }
+ default:
+ {
+ return PerformanceRating.None;
+ }
+ }
+ }
+
+ private PerformanceRating CalculatePerformanceRating(ComparePerformanceStatsDelegate compareFn)
+ {
+ if(compareFn(this, _performanceStatsLevelSet.excellent) <= 0)
+ {
+ return PerformanceRating.Excellent;
+ }
+
+ if(compareFn(this, _performanceStatsLevelSet.good) <= 0)
+ {
+ return PerformanceRating.Good;
+ }
+
+ if(compareFn(this, _performanceStatsLevelSet.medium) <= 0)
+ {
+ return PerformanceRating.Medium;
+ }
+
+ if(compareFn(this, _performanceStatsLevelSet.poor) <= 0)
+ {
+ return PerformanceRating.Poor;
+ }
+
+ return PerformanceRating.VeryPoor;
+ }
+
+ private static bool ApproxLessOrEqual(float x1, float x2)
+ {
+ float r = x1 - x2;
+ return r < 0.0f || Mathf.Approximately(r, 0.0f);
+ }
+
+ #endregion
+
+ #region Overrides
+
+ public override string ToString()
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder();
+ sb.AppendFormat("Avatar Name: {0}\n", avatarName);
+ sb.AppendFormat("Overall Performance: {0}\n", GetPerformanceRatingForCategory(AvatarPerformanceCategory.Overall));
+ sb.AppendFormat("Poly Count: {0}\n", polyCount);
+ sb.AppendFormat("Bounds: {0}\n", aabb.ToString());
+ sb.AppendFormat("Skinned Mesh Count: {0}\n", skinnedMeshCount);
+ sb.AppendFormat("Mesh Count: {0}\n", meshCount);
+ sb.AppendFormat("Material Count: {0}\n", materialCount);
+ sb.AppendFormat("Animator Count: {0}\n", animatorCount);
+ sb.AppendFormat("Bone Count: {0}\n", boneCount);
+ sb.AppendFormat("Light Count: {0}\n", lightCount);
+ sb.AppendFormat("Particle System Count: {0}\n", particleSystemCount);
+ sb.AppendFormat("Particle Total Count: {0}\n", particleTotalCount);
+ sb.AppendFormat("Particle Max Mesh Poly Count: {0}\n", particleMaxMeshPolyCount);
+ sb.AppendFormat("Particle Trails Enabled: {0}\n", particleTrailsEnabled);
+ sb.AppendFormat("Particle Collision Enabled: {0}\n", particleCollisionEnabled);
+ sb.AppendFormat("Trail Renderer Count: {0}\n", trailRendererCount);
+ sb.AppendFormat("Line Renderer Count: {0}\n", lineRendererCount);
+ sb.AppendFormat("Dynamic Bone Component Count: {0}\n", dynamicBoneComponentCount);
+ sb.AppendFormat("Dynamic Bone Simulated Bone Count: {0}\n", dynamicBoneSimulatedBoneCount);
+ sb.AppendFormat("Dynamic Bone Collider Count: {0}\n", dynamicBoneColliderCount);
+ sb.AppendFormat("Dynamic Bone Collision Check Count: {0}\n", dynamicBoneCollisionCheckCount);
+ sb.AppendFormat("Cloth Count: {0}\n", clothCount);
+ sb.AppendFormat("Cloth Max Vertices: {0}\n", clothMaxVertices);
+ sb.AppendFormat("Physics Collider Count: {0}\n", physicsColliderCount);
+ sb.AppendFormat("Physics Rigidbody Count: {0}\n", physicsRigidbodyCount);
+ if(downloadSize > 0)
+ {
+ sb.AppendFormat("Download Size: {0} MB\n", downloadSize);
+ }
+
+ return sb.ToString();
+ }
+
+ // Mirror the AvatarPerformanceStats class even if some aren't used right now.
+ [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
+ [SuppressMessage("ReSharper", "NotAccessedField.Global")]
+ public readonly struct Snapshot
+ {
+ public readonly string avatarName;
+
+ // Stats
+ public readonly int? polyCount;
+ public readonly Bounds? aabb;
+ public readonly int? skinnedMeshCount;
+ public readonly int? meshCount;
+ public readonly int? materialCount;
+ public readonly int? animatorCount;
+ public readonly int? boneCount;
+ public readonly int? lightCount;
+ public readonly int? particleSystemCount;
+ public readonly int? particleTotalCount;
+ public readonly int? particleMaxMeshPolyCount;
+ public readonly bool? particleTrailsEnabled;
+ public readonly bool? particleCollisionEnabled;
+ public readonly int? trailRendererCount;
+ public readonly int? lineRendererCount;
+ public readonly int? dynamicBoneComponentCount;
+ public readonly int? dynamicBoneSimulatedBoneCount;
+ public readonly int? dynamicBoneColliderCount;
+ public readonly int? dynamicBoneCollisionCheckCount; // number of collider simulated bones excluding the root multiplied by the number of colliders
+ public readonly int? clothCount;
+ public readonly int? clothMaxVertices;
+ public readonly int? physicsColliderCount;
+ public readonly int? physicsRigidbodyCount;
+ public readonly int? audioSourceCount;
+ public readonly float? downloadSize;
+
+ // Ratings
+ public readonly PerformanceRating overallRating;
+ public readonly PerformanceRating polyCountRating;
+ public readonly PerformanceRating aabbRating;
+ public readonly PerformanceRating skinnedMeshCountRating;
+ public readonly PerformanceRating meshCountRating;
+ public readonly PerformanceRating materialCountRating;
+ public readonly PerformanceRating animatorCountRating;
+ public readonly PerformanceRating boneCountRating;
+ public readonly PerformanceRating lightCountRating;
+ public readonly PerformanceRating particleSystemCountRating;
+ public readonly PerformanceRating particleTotalCountRating;
+ public readonly PerformanceRating particleMaxMeshPolyCountRating;
+ public readonly PerformanceRating particleTrailsEnabledRating;
+ public readonly PerformanceRating particleCollisionEnabledRating;
+ public readonly PerformanceRating trailRendererCountRating;
+ public readonly PerformanceRating lineRendererCountRating;
+ public readonly PerformanceRating dynamicBoneComponentCountRating;
+ public readonly PerformanceRating dynamicBoneSimulatedBoneCountRating;
+ public readonly PerformanceRating dynamicBoneColliderCountRating;
+ public readonly PerformanceRating dynamicBoneCollisionCheckCountRating;
+ public readonly PerformanceRating clothCountRating;
+ public readonly PerformanceRating clothMaxVerticesRating;
+ public readonly PerformanceRating physicsColliderCountRating;
+ public readonly PerformanceRating physicsRigidbodyCountRating;
+ public readonly PerformanceRating audioSourceCountRating;
+ public readonly PerformanceRating downloadSizeRating;
+
+ public Snapshot(AvatarPerformanceStats avatarPerformanceStats)
+ {
+ avatarName = avatarPerformanceStats.avatarName;
+ polyCount = avatarPerformanceStats.polyCount;
+ aabb = avatarPerformanceStats.aabb;
+ skinnedMeshCount = avatarPerformanceStats.skinnedMeshCount;
+ meshCount = avatarPerformanceStats.meshCount;
+ materialCount = avatarPerformanceStats.materialCount;
+ animatorCount = avatarPerformanceStats.animatorCount;
+ boneCount = avatarPerformanceStats.boneCount;
+ lightCount = avatarPerformanceStats.lightCount;
+ particleSystemCount = avatarPerformanceStats.particleSystemCount;
+ particleTotalCount = avatarPerformanceStats.particleTotalCount;
+ particleMaxMeshPolyCount = avatarPerformanceStats.particleMaxMeshPolyCount;
+ particleTrailsEnabled = avatarPerformanceStats.particleTrailsEnabled;
+ particleCollisionEnabled = avatarPerformanceStats.particleCollisionEnabled;
+ trailRendererCount = avatarPerformanceStats.trailRendererCount;
+ lineRendererCount = avatarPerformanceStats.lineRendererCount;
+ dynamicBoneComponentCount = avatarPerformanceStats.dynamicBoneComponentCount;
+ dynamicBoneSimulatedBoneCount = avatarPerformanceStats.dynamicBoneSimulatedBoneCount;
+ dynamicBoneColliderCount = avatarPerformanceStats.dynamicBoneColliderCount;
+ dynamicBoneCollisionCheckCount = avatarPerformanceStats.dynamicBoneCollisionCheckCount;
+ clothCount = avatarPerformanceStats.clothCount;
+ clothMaxVertices = avatarPerformanceStats.clothMaxVertices;
+ physicsColliderCount = avatarPerformanceStats.physicsColliderCount;
+ physicsRigidbodyCount = avatarPerformanceStats.physicsRigidbodyCount;
+ audioSourceCount = avatarPerformanceStats.audioSourceCount;
+ downloadSize = avatarPerformanceStats.downloadSize;
+
+ overallRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.Overall);
+ polyCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.PolyCount);
+ aabbRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.AABB);
+ skinnedMeshCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.SkinnedMeshCount);
+ meshCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.MeshCount);
+ materialCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.MaterialCount);
+ animatorCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.AnimatorCount);
+ boneCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.BoneCount);
+ lightCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.LightCount);
+ particleSystemCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ParticleSystemCount);
+ particleTotalCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ParticleTotalCount);
+ particleMaxMeshPolyCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ParticleMaxMeshPolyCount);
+ particleTrailsEnabledRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ParticleTrailsEnabled);
+ particleCollisionEnabledRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ParticleCollisionEnabled);
+ trailRendererCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.TrailRendererCount);
+ lineRendererCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.LineRendererCount);
+ dynamicBoneComponentCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.DynamicBoneComponentCount);
+ dynamicBoneSimulatedBoneCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount);
+ dynamicBoneColliderCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.DynamicBoneColliderCount);
+ dynamicBoneCollisionCheckCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.DynamicBoneCollisionCheckCount);
+ clothCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ClothCount);
+ clothMaxVerticesRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.ClothMaxVertices);
+ physicsColliderCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.PhysicsColliderCount);
+ physicsRigidbodyCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.PhysicsRigidbodyCount);
+ audioSourceCountRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.AudioSourceCount);
+ downloadSizeRating = avatarPerformanceStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.DownloadSize);
+ }
+
+ public override string ToString()
+ {
+ System.Text.StringBuilder sb = new System.Text.StringBuilder(1024);
+ sb.AppendFormat("Avatar Name: {0}\n", avatarName);
+ sb.AppendFormat("Overall Performance: {0}\n", overallRating);
+ sb.AppendFormat("Poly Count: {0}\n", polyCount);
+ sb.AppendFormat("Bounds: {0}\n", aabb.ToString());
+ sb.AppendFormat("Skinned Mesh Count: {0}\n", skinnedMeshCount);
+ sb.AppendFormat("Mesh Count: {0}\n", meshCount);
+ sb.AppendFormat("Material Count: {0}\n", materialCount);
+ sb.AppendFormat("Animator Count: {0}\n", animatorCount);
+ sb.AppendFormat("Bone Count: {0}\n", boneCount);
+ sb.AppendFormat("Light Count: {0}\n", lightCount);
+ sb.AppendFormat("Particle System Count: {0}\n", particleSystemCount);
+ sb.AppendFormat("Particle Total Count: {0}\n", particleTotalCount);
+ sb.AppendFormat("Particle Max Mesh Poly Count: {0}\n", particleMaxMeshPolyCount);
+ sb.AppendFormat("Particle Trails Enabled: {0}\n", particleTrailsEnabled);
+ sb.AppendFormat("Particle Collision Enabled: {0}\n", particleCollisionEnabled);
+ sb.AppendFormat("Trail Renderer Count: {0}\n", trailRendererCount);
+ sb.AppendFormat("Line Renderer Count: {0}\n", lineRendererCount);
+ sb.AppendFormat("Dynamic Bone Component Count: {0}\n", dynamicBoneComponentCount);
+ sb.AppendFormat("Dynamic Bone Simulated Bone Count: {0}\n", dynamicBoneSimulatedBoneCount);
+ sb.AppendFormat("Dynamic Bone Collider Count: {0}\n", dynamicBoneColliderCount);
+ sb.AppendFormat("Dynamic Bone Collision Check Count: {0}\n", dynamicBoneCollisionCheckCount);
+ sb.AppendFormat("Cloth Count: {0}\n", clothCount);
+ sb.AppendFormat("Cloth Max Vertices: {0}\n", clothMaxVertices);
+ sb.AppendFormat("Physics Collider Count: {0}\n", physicsColliderCount);
+ sb.AppendFormat("Physics Rigidbody Count: {0}\n", physicsRigidbodyCount);
+ sb.AppendFormat("Download Size: {0} MB\n", downloadSize);
+
+ return sb.ToString();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs.meta
new file mode 100644
index 00000000..894716a6
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStats.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1bf4fb79a49d4b109c4dce6b38e5548e
+timeCreated: 1561267926 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs
new file mode 100644
index 00000000..c31b43a2
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs
@@ -0,0 +1,32 @@
+using UnityEngine;
+
+namespace VRC.SDKBase.Validation.Performance.Stats
+{
+ public class AvatarPerformanceStatsLevel : ScriptableObject
+ {
+ public int polyCount;
+ public Bounds aabb;
+ public int skinnedMeshCount;
+ public int meshCount;
+ public int materialCount;
+ public int animatorCount;
+ public int boneCount;
+ public int lightCount;
+ public int particleSystemCount;
+ public int particleTotalCount;
+ public int particleMaxMeshPolyCount;
+ public bool particleTrailsEnabled;
+ public bool particleCollisionEnabled;
+ public int trailRendererCount;
+ public int lineRendererCount;
+ public int dynamicBoneComponentCount;
+ public int dynamicBoneSimulatedBoneCount;
+ public int dynamicBoneColliderCount;
+ public int dynamicBoneCollisionCheckCount;
+ public int clothCount;
+ public int clothMaxVertices;
+ public int physicsColliderCount;
+ public int physicsRigidbodyCount;
+ public int audioSourceCount;
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs.meta
new file mode 100644
index 00000000..966fbdc0
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevel.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f742c36dce5730f4d96e37d82c330584
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs
new file mode 100644
index 00000000..6490496b
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs
@@ -0,0 +1,15 @@
+using UnityEngine;
+using UnityEngine.Serialization;
+
+namespace VRC.SDKBase.Validation.Performance.Stats
+{
+ public class AvatarPerformanceStatsLevelSet : ScriptableObject
+ {
+ [FormerlySerializedAs("veryGood")]
+ public AvatarPerformanceStatsLevel excellent;
+ public AvatarPerformanceStatsLevel good;
+ public AvatarPerformanceStatsLevel medium;
+ [FormerlySerializedAs("bad")]
+ public AvatarPerformanceStatsLevel poor;
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs.meta
new file mode 100644
index 00000000..603c0a80
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/Performance/Stats/AvatarPerformanceStatsLevelSet.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 468554b1bfc447f41a20a2f5bae65d16
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs
new file mode 100644
index 00000000..0a69a1af
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs
@@ -0,0 +1,86 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace VRC.SDKBase.Validation
+{
+ public static class ShaderValidation
+ {
+ public static IEnumerable<Shader> FindIllegalShaders(GameObject target, string[] whitelist)
+ {
+ List<Shader> illegalShaders = new List<Shader>();
+ IEnumerator seeker = FindIllegalShadersEnumerator(target, whitelist, (c) => illegalShaders.Add(c));
+ while(seeker.MoveNext())
+ {
+ }
+
+ return illegalShaders;
+ }
+
+ private static IEnumerator FindIllegalShadersEnumerator(GameObject target, string[] whitelist, System.Action<Shader> onFound, bool useWatch = false)
+ {
+ System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
+ if(useWatch)
+ {
+ watch.Start();
+ }
+
+ List<Material> materialCache = new List<Material>();
+ Queue<GameObject> children = new Queue<GameObject>();
+ children.Enqueue(target.gameObject);
+ while(children.Count > 0)
+ {
+ GameObject child = children.Dequeue();
+ if(child == null)
+ {
+ continue;
+ }
+
+ for(int idx = 0; idx < child.transform.childCount; ++idx)
+ {
+ children.Enqueue(child.transform.GetChild(idx).gameObject);
+ }
+
+ foreach(Renderer childRenderers in child.transform.GetComponents<Renderer>())
+ {
+ if(childRenderers == null)
+ {
+ continue;
+ }
+
+ foreach(Material sharedMaterial in childRenderers.sharedMaterials)
+ {
+ if(materialCache.Any(cacheMtl => sharedMaterial == cacheMtl)) // did we already look at this one?
+ {
+ continue;
+ }
+
+ // Skip empty material slots, or materials without shaders.
+ // Both will end up using the magenta error shader.
+ if(sharedMaterial == null || sharedMaterial.shader == null)
+ {
+ continue;
+ }
+
+ if(whitelist.All(okayShaderName => sharedMaterial.shader.name != okayShaderName))
+ {
+ onFound(sharedMaterial.shader);
+ yield return null;
+ }
+
+ materialCache.Add(sharedMaterial);
+ }
+
+ if(!useWatch || watch.ElapsedMilliseconds <= 1)
+ {
+ continue;
+ }
+
+ yield return null;
+ watch.Reset();
+ }
+ }
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs.meta
new file mode 100644
index 00000000..1b9e8680
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ShaderValidation.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: bef0a8d1d2c547119a62b7d7a5c512ea
+timeCreated: 1563940360 \ No newline at end of file
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs
new file mode 100644
index 00000000..e6dfef95
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using UnityEngine;
+using System.Reflection;
+
+namespace VRC.SDKBase.Validation
+{
+ public static class ValidationUtils
+ {
+ public static void RemoveIllegalComponents(GameObject target, HashSet<Type> whitelist, bool retry = true, bool onlySceneObjects = false, bool logStripping = true)
+ {
+ List<Component> foundComponents = FindIllegalComponents(target, whitelist);
+ foreach(Component component in foundComponents)
+ {
+ if(component == null)
+ {
+ continue;
+ }
+
+ if(onlySceneObjects && component.GetInstanceID() < 0)
+ {
+ continue;
+ }
+
+ if(logStripping)
+ {
+ Core.Logger.LogWarning($"Removing {component.GetType().Name} comp from {component.gameObject.name}");
+ }
+
+ RemoveComponent(component);
+ }
+ }
+
+ public static List<Component> FindIllegalComponents(GameObject target, HashSet<Type> whitelist)
+ {
+ List<Component> foundComponents = new List<Component>();
+ Component[] allComponents = target.GetComponentsInChildren<Component>(true);
+ foreach(Component component in allComponents)
+ {
+ if(component == null)
+ {
+ continue;
+ }
+
+ Type componentType = component.GetType();
+ if(whitelist.Contains(componentType))
+ {
+ continue;
+ }
+
+ foundComponents.Add(component);
+ }
+
+ return foundComponents;
+ }
+
+ private static readonly Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();
+ private static readonly Dictionary<string, HashSet<Type>> _whitelistCache = new Dictionary<string, HashSet<Type>>();
+ public static HashSet<Type> WhitelistedTypes(string whitelistName, IEnumerable<string> componentTypeWhitelist)
+ {
+ if (_whitelistCache.ContainsKey(whitelistName))
+ {
+ return _whitelistCache[whitelistName];
+ }
+
+ Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ HashSet<Type> whitelist = new HashSet<Type>();
+ foreach(string whitelistedTypeName in componentTypeWhitelist)
+ {
+ Type whitelistedType = GetTypeFromName(whitelistedTypeName, assemblies);
+ if(whitelistedType == null)
+ {
+ continue;
+ }
+
+ if(whitelist.Contains(whitelistedType))
+ {
+ continue;
+ }
+
+ whitelist.Add(whitelistedType);
+ }
+
+ AddDerivedClasses(whitelist);
+
+ _whitelistCache[whitelistName] = whitelist;
+
+ return _whitelistCache[whitelistName];
+ }
+
+ public static HashSet<Type> WhitelistedTypes(string whitelistName, IEnumerable<Type> componentTypeWhitelist)
+ {
+ if (_whitelistCache.ContainsKey(whitelistName))
+ {
+ return _whitelistCache[whitelistName];
+ }
+
+ HashSet<Type> whitelist = new HashSet<Type>();
+ whitelist.UnionWith(componentTypeWhitelist);
+
+ AddDerivedClasses(whitelist);
+
+ _whitelistCache[whitelistName] = whitelist;
+
+ return _whitelistCache[whitelistName];
+ }
+
+ private static void AddDerivedClasses(HashSet<Type> whitelist)
+ {
+ Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ foreach(Assembly assembly in assemblies)
+ {
+ foreach(Type type in assembly.GetTypes())
+ {
+ if(whitelist.Contains(type))
+ {
+ continue;
+ }
+
+ if(!typeof(Component).IsAssignableFrom(type))
+ {
+ continue;
+ }
+
+ Type currentType = type;
+ while(currentType != typeof(object) && currentType != null)
+ {
+ if(whitelist.Contains(currentType))
+ {
+ whitelist.Add(type);
+ break;
+ }
+
+ currentType = currentType.BaseType;
+ }
+ }
+ }
+ }
+
+ public static Type GetTypeFromName(string name, Assembly[] assemblies = null)
+ {
+ if (_typeCache.ContainsKey(name))
+ {
+
+ return _typeCache[name];
+ }
+
+ if (assemblies == null)
+ {
+ assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ }
+
+ foreach (Assembly assembly in assemblies)
+ {
+ Type found = assembly.GetType(name);
+ if(found == null)
+ {
+ continue;
+ }
+
+ _typeCache[name] = found;
+ return found;
+ }
+
+ //This is really verbose for some SDK scenes, eg.
+ //If they don't have FinalIK installed
+#if VRC_CLIENT && UNITY_EDITOR
+ Debug.LogWarningFormat("Could not find type {0}", name);
+#endif
+
+ _typeCache[name] = null;
+ return null;
+ }
+
+ private static readonly Dictionary<Type, ImmutableArray<RequireComponent>> _requireComponentsCache = new Dictionary<Type, ImmutableArray<RequireComponent>>();
+ private static void RemoveDependencies(Component rootComponent)
+ {
+ if (rootComponent == null)
+ {
+ return;
+ }
+
+ Component[] components = rootComponent.GetComponents<Component>();
+ if (components == null || components.Length == 0)
+ {
+ return;
+ }
+
+ Type compType = rootComponent.GetType();
+ foreach (var siblingComponent in components)
+ {
+ if (siblingComponent == null)
+ {
+ continue;
+ }
+
+ Type siblingComponentType = siblingComponent.GetType();
+ if(!_requireComponentsCache.TryGetValue(siblingComponentType, out ImmutableArray<RequireComponent> requiredComponentAttributes))
+ {
+ requiredComponentAttributes = siblingComponentType.GetCustomAttributes(typeof(RequireComponent), true).Cast<RequireComponent>().ToImmutableArray();
+ _requireComponentsCache.Add(siblingComponentType, requiredComponentAttributes);
+ }
+
+ bool deleteMe = false;
+ foreach (RequireComponent requireComponent in requiredComponentAttributes)
+ {
+ if (requireComponent == null)
+ {
+ continue;
+ }
+
+ if(requireComponent.m_Type0 != compType && requireComponent.m_Type1 != compType && requireComponent.m_Type2 != compType)
+ {
+ continue;
+ }
+
+ deleteMe = true;
+ break;
+ }
+
+ if (deleteMe && siblingComponent != rootComponent)
+ {
+ RemoveComponent(siblingComponent);
+ }
+ }
+ }
+
+ public static void RemoveComponent(Component comp)
+ {
+ if (comp == null)
+ {
+ return;
+ }
+
+ RemoveDependencies(comp);
+
+#if VRC_CLIENT
+ UnityEngine.Object.DestroyImmediate(comp, true);
+#else
+ UnityEngine.Object.DestroyImmediate(comp, false);
+#endif
+ }
+
+ public static void RemoveComponentsOfType<T>(GameObject target) where T : Component
+ {
+ if (target == null)
+ return;
+
+ foreach (T comp in target.GetComponentsInChildren<T>(true))
+ {
+ if (comp == null || comp.gameObject == null)
+ continue;
+
+ RemoveComponent(comp);
+ }
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs.meta
new file mode 100644
index 00000000..adc2f728
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/ValidationUtils.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 8a90ec11b51863c4cb2d8a8cee31c2fb
+timeCreated: 1504829091
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs
new file mode 100644
index 00000000..3dab42c2
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs
@@ -0,0 +1,682 @@
+using System;
+using System.Collections.Generic;
+using JetBrains.Annotations;
+#if TextMeshPro
+using TMPro;
+#endif
+using UnityEngine;
+using UnityEngine.Playables;
+using UnityEngine.Timeline;
+using UnityEngine.UI;
+
+namespace VRC.SDKBase.Validation
+{
+ public static class WorldValidation
+ {
+ private static readonly Lazy<int> _debugLevel = new Lazy<int>(InitializeLogging);
+ private static int DebugLevel => _debugLevel.Value;
+
+ private static int InitializeLogging()
+ {
+ int hashCode = typeof(WorldValidation).GetHashCode();
+ VRC.Core.Logger.DescribeDebugLevel(hashCode, "WorldValidation", VRC.Core.Logger.Color.red);
+ VRC.Core.Logger.AddDebugLevel(hashCode);
+ return hashCode;
+ }
+
+ static string[] ComponentTypeWhiteList = null;
+
+ public enum WhiteListConfiguration
+ {
+ None,
+ VRCSDK2,
+ VRCSDK3,
+ Unchanged
+ }
+
+ static WhiteListConfiguration ComponentTypeWhiteListConfiguration = WhiteListConfiguration.None;
+
+ static readonly string[] ComponentTypeWhiteListCommon = new string[]
+ {
+ #if UNITY_STANDALONE
+ "UnityEngine.Rendering.PostProcessing.PostProcessDebug",
+ "UnityEngine.Rendering.PostProcessing.PostProcessLayer",
+ "UnityEngine.Rendering.PostProcessing.PostProcessVolume",
+ #endif
+ "VRC.Core.PipelineManager",
+ "UiInputField",
+ "VRCProjectSettings",
+ "DynamicBone",
+ "DynamicBoneCollider",
+ "TMPro.TMP_Dropdown",
+ "TMPro.TMP_InputField",
+ "TMPro.TMP_ScrollbarEventHandler",
+ "TMPro.TMP_SelectionCaret",
+ "TMPro.TMP_SpriteAnimator",
+ "TMPro.TMP_SubMesh",
+ "TMPro.TMP_SubMeshUI",
+ "TMPro.TMP_Text",
+ "TMPro.TextMeshPro",
+ "TMPro.TextMeshProUGUI",
+ "TMPro.TextContainer",
+ "TMPro.TMP_Dropdown+DropdownItem",
+ "UnityEngine.EventSystems.EventSystem",
+ "UnityEngine.EventSystems.EventTrigger",
+ "UnityEngine.EventSystems.UIBehaviour",
+ "UnityEngine.EventSystems.BaseInput",
+ "UnityEngine.EventSystems.BaseInputModule",
+ "UnityEngine.EventSystems.PointerInputModule",
+ "UnityEngine.EventSystems.StandaloneInputModule",
+ "UnityEngine.EventSystems.TouchInputModule",
+ "UnityEngine.EventSystems.BaseRaycaster",
+ "UnityEngine.EventSystems.PhysicsRaycaster",
+ "UnityEngine.UI.Button",
+ "UnityEngine.UI.Dropdown",
+ "UnityEngine.UI.Dropdown+DropdownItem",
+ "UnityEngine.UI.Graphic",
+ "UnityEngine.UI.GraphicRaycaster",
+ "UnityEngine.UI.Image",
+ "UnityEngine.UI.InputField",
+ "UnityEngine.UI.Mask",
+ "UnityEngine.UI.MaskableGraphic",
+ "UnityEngine.UI.RawImage",
+ "UnityEngine.UI.RectMask2D",
+ "UnityEngine.UI.Scrollbar",
+ "UnityEngine.UI.ScrollRect",
+ "UnityEngine.UI.Selectable",
+ "UnityEngine.UI.Slider",
+ "UnityEngine.UI.Text",
+ "UnityEngine.UI.Toggle",
+ "UnityEngine.UI.ToggleGroup",
+ "UnityEngine.UI.AspectRatioFitter",
+ "UnityEngine.UI.CanvasScaler",
+ "UnityEngine.UI.ContentSizeFitter",
+ "UnityEngine.UI.GridLayoutGroup",
+ "UnityEngine.UI.HorizontalLayoutGroup",
+ "UnityEngine.UI.HorizontalOrVerticalLayoutGroup",
+ "UnityEngine.UI.LayoutElement",
+ "UnityEngine.UI.LayoutGroup",
+ "UnityEngine.UI.VerticalLayoutGroup",
+ "UnityEngine.UI.BaseMeshEffect",
+ "UnityEngine.UI.Outline",
+ "UnityEngine.UI.PositionAsUV1",
+ "UnityEngine.UI.Shadow",
+ "OVRLipSync",
+ "OVRLipSyncContext",
+ "OVRLipSyncContextBase",
+ "OVRLipSyncContextCanned",
+ "OVRLipSyncContextMorphTarget",
+ "OVRLipSyncContextTextureFlip",
+ "ONSPReflectionZone",
+ "OculusSpatializerUnity",
+ "ONSPAmbisonicsNative",
+ "ONSPAudioSource",
+ "RootMotion.FinalIK.BipedIK",
+ "RootMotion.FinalIK.FingerRig",
+ "RootMotion.FinalIK.Grounder",
+ "RootMotion.FinalIK.GrounderBipedIK",
+ "RootMotion.FinalIK.GrounderFBBIK",
+ "RootMotion.FinalIK.GrounderIK",
+ "RootMotion.FinalIK.GrounderQuadruped",
+ "RootMotion.FinalIK.GrounderVRIK",
+ "RootMotion.FinalIK.AimIK",
+ "RootMotion.FinalIK.CCDIK",
+ "RootMotion.FinalIK.FABRIK",
+ "RootMotion.FinalIK.FABRIKRoot",
+ "RootMotion.FinalIK.FullBodyBipedIK",
+ "RootMotion.FinalIK.IK",
+ "RootMotion.FinalIK.IKExecutionOrder",
+ "RootMotion.FinalIK.LegIK",
+ "RootMotion.FinalIK.LimbIK",
+ "RootMotion.FinalIK.LookAtIK",
+ "RootMotion.FinalIK.TrigonometricIK",
+ "RootMotion.FinalIK.VRIK",
+ "RootMotion.FinalIK.FBBIKArmBending",
+ "RootMotion.FinalIK.FBBIKHeadEffector",
+ "RootMotion.FinalIK.TwistRelaxer",
+ "RootMotion.FinalIK.InteractionObject",
+ "RootMotion.FinalIK.InteractionSystem",
+ "RootMotion.FinalIK.InteractionTarget",
+ "RootMotion.FinalIK.InteractionTrigger",
+ "RootMotion.FinalIK.GenericPoser",
+ "RootMotion.FinalIK.HandPoser",
+ "RootMotion.FinalIK.Poser",
+ "RootMotion.FinalIK.RagdollUtility",
+ "RootMotion.FinalIK.RotationLimit",
+ "RootMotion.FinalIK.RotationLimitAngle",
+ "RootMotion.FinalIK.RotationLimitHinge",
+ "RootMotion.FinalIK.RotationLimitPolygonal",
+ "RootMotion.FinalIK.RotationLimitSpline",
+ "RootMotion.FinalIK.AimPoser",
+ "RootMotion.FinalIK.Amplifier",
+ "RootMotion.FinalIK.BodyTilt",
+ "RootMotion.FinalIK.HitReaction",
+ "RootMotion.FinalIK.HitReactionVRIK",
+ "RootMotion.FinalIK.Inertia",
+ "RootMotion.FinalIK.OffsetModifier",
+ "RootMotion.FinalIK.OffsetModifierVRIK",
+ "RootMotion.FinalIK.OffsetPose",
+ "RootMotion.FinalIK.Recoil",
+ "RootMotion.FinalIK.ShoulderRotator",
+ "RootMotion.Dynamics.AnimationBlocker",
+ "RootMotion.Dynamics.BehaviourBase",
+ "RootMotion.Dynamics.BehaviourFall",
+ "RootMotion.Dynamics.BehaviourPuppet",
+ "RootMotion.Dynamics.JointBreakBroadcaster",
+ "RootMotion.Dynamics.MuscleCollisionBroadcaster",
+ "RootMotion.Dynamics.PressureSensor",
+ "RootMotion.Dynamics.Prop",
+ "RootMotion.Dynamics.PropRoot",
+ "RootMotion.Dynamics.PuppetMaster",
+ "RootMotion.Dynamics.PuppetMasterSettings",
+ // TODO: remove these if they are only needed in editor
+ "RootMotion.Dynamics.BipedRagdollCreator",
+ "RootMotion.Dynamics.RagdollCreator",
+ "RootMotion.Dynamics.RagdollEditor",
+ //
+ "RootMotion.SolverManager",
+ "RootMotion.TriggerEventBroadcaster",
+ "UnityEngine.WindZone",
+ "UnityEngine.Tilemaps.Tilemap",
+ "UnityEngine.Tilemaps.TilemapRenderer",
+ "UnityEngine.Terrain",
+ "UnityEngine.Tree",
+ "UnityEngine.SpriteMask",
+ "UnityEngine.Grid",
+ "UnityEngine.GridLayout",
+ "UnityEngine.AudioSource",
+ "UnityEngine.AudioReverbZone",
+ "UnityEngine.AudioLowPassFilter",
+ "UnityEngine.AudioHighPassFilter",
+ "UnityEngine.AudioDistortionFilter",
+ "UnityEngine.AudioEchoFilter",
+ "UnityEngine.AudioChorusFilter",
+ "UnityEngine.AudioReverbFilter",
+ "UnityEngine.Playables.PlayableDirector",
+ "UnityEngine.TerrainCollider",
+ "UnityEngine.Canvas",
+ "UnityEngine.CanvasGroup",
+ "UnityEngine.CanvasRenderer",
+ "UnityEngine.TextMesh",
+ "UnityEngine.Animator",
+ "UnityEngine.AI.NavMeshAgent",
+ "UnityEngine.AI.NavMeshObstacle",
+ "UnityEngine.AI.OffMeshLink",
+ "UnityEngine.Cloth",
+ "UnityEngine.WheelCollider",
+ "UnityEngine.Rigidbody",
+ "UnityEngine.Joint",
+ "UnityEngine.HingeJoint",
+ "UnityEngine.SpringJoint",
+ "UnityEngine.FixedJoint",
+ "UnityEngine.CharacterJoint",
+ "UnityEngine.ConfigurableJoint",
+ "UnityEngine.ConstantForce",
+ "UnityEngine.Collider",
+ "UnityEngine.BoxCollider",
+ "UnityEngine.SphereCollider",
+ "UnityEngine.MeshCollider",
+ "UnityEngine.CapsuleCollider",
+ "UnityEngine.CharacterController",
+ "UnityEngine.ParticleSystem",
+ "UnityEngine.ParticleSystemRenderer",
+ "UnityEngine.BillboardRenderer",
+ "UnityEngine.Camera",
+ "UnityEngine.FlareLayer",
+ "UnityEngine.SkinnedMeshRenderer",
+ "UnityEngine.Renderer",
+ "UnityEngine.TrailRenderer",
+ "UnityEngine.LineRenderer",
+ "UnityEngine.GUIElement",
+ "UnityEngine.GUILayer",
+ "UnityEngine.Light",
+ "UnityEngine.LightProbeGroup",
+ "UnityEngine.LightProbeProxyVolume",
+ "UnityEngine.LODGroup",
+ "UnityEngine.ReflectionProbe",
+ "UnityEngine.SpriteRenderer",
+ "UnityEngine.Transform",
+ "UnityEngine.RectTransform",
+ "UnityEngine.Rendering.SortingGroup",
+ "UnityEngine.Projector",
+ "UnityEngine.OcclusionPortal",
+ "UnityEngine.OcclusionArea",
+ "UnityEngine.LensFlare",
+ "UnityEngine.Skybox",
+ "UnityEngine.MeshFilter",
+ "UnityEngine.Halo",
+ "UnityEngine.MeshRenderer",
+ "UnityEngine.Collider2D",
+ "UnityEngine.Rigidbody2D",
+ "UnityEngine.CompositeCollider2D",
+ "UnityEngine.ConstantForce2D",
+ "UnityEngine.AreaEffector2D",
+ "UnityEngine.CapsuleCollider2D",
+ "UnityEngine.DistanceJoint2D",
+ "UnityEngine.EdgeCollider2D",
+ "UnityEngine.Effector2D",
+ "UnityEngine.BoxCollider2D",
+ "UnityEngine.CircleCollider2D",
+ "UnityEngine.FixedJoint2D",
+ "UnityEngine.HingeJoint2D",
+ "UnityEngine.FrictionJoint2D",
+ "UnityEngine.PlatformEffector2D",
+ "UnityEngine.PointEffector2D",
+ "UnityEngine.PolygonCollider2D",
+ "UnityEngine.SliderJoint2D",
+ "UnityEngine.SurfaceEffector2D",
+ "UnityEngine.RelativeJoint2D",
+ "UnityEngine.TargetJoint2D",
+ "UnityEngine.WheelJoint2D",
+ "UnityEngine.Joint2D",
+ "UnityEngine.ParticleSystemForceField"
+ };
+
+ static readonly string[] ComponentTypeWhiteListSdk2 = new string[]
+ {
+ #if UNITY_STANDALONE
+ "VRCSDK2.VRC_CustomRendererBehaviour",
+ "VRCSDK2.VRC_MidiNoteIn",
+ "VRCSDK2.scripts.Scenes.VRC_Panorama",
+ "VRCSDK2.VRC_Water",
+ "UnityStandardAssets.Water.WaterBasic",
+ "UnityStandardAssets.Water.Displace",
+ "UnityStandardAssets.Water.GerstnerDisplace",
+ "UnityStandardAssets.Water.PlanarReflection",
+ "UnityStandardAssets.Water.SpecularLighting",
+ "UnityStandardAssets.Water.Water",
+ "UnityStandardAssets.Water.WaterBase",
+ "UnityStandardAssets.Water.WaterTile",
+ #endif
+ "VRCSDK2.VRCTriggerRelay",
+ "VRCSDK2.VRC_AudioBank",
+ "VRCSDK2.VRC_DataStorage",
+ "VRCSDK2.VRC_EventHandler",
+ "VRCSDK2.VRC_IKFollower",
+ "VRCSDK2.VRC_Label",
+ "VRCSDK2.VRC_KeyEvents",
+ "VRCSDK2.VRC_PhysicsRoot",
+ "VRCSDK2.VRC_CombatSystem",
+ "VRCSDK2.VRC_DestructibleStandard",
+ "VRC_VisualDamage",
+ "VRCSDK2.VRC_OscButtonIn",
+ "VRCSDK2.VRC_GunStats",
+ "VRCSDK2.VRC_JukeBox",
+ "VRCSDK2.VRC_AddDamage",
+ "VRCSDK2.VRC_AddHealth",
+ "VRCSDK2.VRC_AvatarCalibrator",
+ "VRCSDK2.VRC_AvatarPedestal",
+ "VRCSDK2.VRC_NPCSpawn",
+ "VRCSDK2.VRC_ObjectSpawn",
+ "VRCSDK2.VRC_ObjectSync",
+ "VRCSDK2.VRC_Pickup",
+ "VRCSDK2.VRC_PortalMarker",
+ "VRCSDK2.VRC_SlideShow",
+ "VRCSDK2.VRC_SpatialAudioSource",
+ "VRCSDK2.VRC_StationInput",
+ "VRCSDK2.VRC_SyncAnimation",
+ "VRCSDK2.VRC_SyncVideoPlayer",
+ "VRCSDK2.VRC_SyncVideoStream",
+ "VRCSDK2.VRC_VideoScreen",
+ "VRCSDK2.VRC_VideoSpeaker",
+ "VRCSDK2.VRC_PlayerAudioOverride",
+ "VRCSDK2.VRC_MirrorReflection",
+ "VRCSDK2.VRC_PlayerMods",
+ "VRCSDK2.VRC_SceneDescriptor",
+ "VRCSDK2.VRC_SceneResetPosition",
+ "VRCSDK2.VRC_SceneSmoothShift",
+ "VRCSDK2.VRC_SpecialLayer",
+ "VRCSDK2.VRC_Station",
+ "VRCSDK2.VRC_StereoObject",
+ "VRCSDK2.VRC_TimedEvents",
+ "VRCSDK2.VRC_Trigger",
+ "VRCSDK2.VRC_TriggerColliderEventTrigger",
+ "VRCSDK2.VRC_UseEvents",
+ "VRCSDK2.VRC_UiShape",
+ "UnityEngine.Animation",
+ #if !UNITY_2019_4_OR_NEWER
+ "UnityEngine.GUIText",
+ "UnityEngine.GUITexture",
+ #endif
+ "UnityEngine.Video.VideoPlayer",
+ "PhysSound.PhysSoundBase",
+ "PhysSound.PhysSoundObject",
+ "PhysSound.PhysSoundTempAudio",
+ "PhysSound.PhysSoundTempAudioPool",
+ "PhysSound.PhysSoundTerrain",
+ "RealisticEyeMovements.EyeAndHeadAnimator",
+ "RealisticEyeMovements.LookTargetController",
+ "UnityStandardAssets.Cameras.AbstractTargetFollower",
+ "UnityStandardAssets.Cameras.AutoCam",
+ "UnityStandardAssets.Cameras.FreeLookCam",
+ "UnityStandardAssets.Cameras.HandHeldCam",
+ "UnityStandardAssets.Cameras.LookatTarget",
+ "UnityStandardAssets.Cameras.PivotBasedCameraRig",
+ "UnityStandardAssets.Cameras.ProtectCameraFromWallClip",
+ "UnityStandardAssets.Cameras.TargetFieldOfView",
+ "UnityStandardAssets.Characters.FirstPerson.FirstPersonController",
+ "UnityStandardAssets.Characters.FirstPerson.HeadBob",
+ "UnityStandardAssets.Characters.FirstPerson.RigidbodyFirstPersonController",
+ "UnityStandardAssets.Vehicles.Ball.Ball",
+ "UnityStandardAssets.Vehicles.Ball.BallUserControl",
+ "UnityStandardAssets.Characters.ThirdPerson.AICharacterControl",
+ "UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter",
+ "UnityStandardAssets.Characters.ThirdPerson.ThirdPersonUserControl",
+ "UnityStandardAssets.CrossPlatformInput.AxisTouchButton",
+ "UnityStandardAssets.CrossPlatformInput.ButtonHandler",
+ "UnityStandardAssets.CrossPlatformInput.InputAxisScrollbar",
+ "UnityStandardAssets.CrossPlatformInput.Joystick",
+ "UnityStandardAssets.CrossPlatformInput.MobileControlRig",
+ "UnityStandardAssets.CrossPlatformInput.TiltInput",
+ "UnityStandardAssets.CrossPlatformInput.TouchPad",
+ "UnityStandardAssets.Effects.AfterburnerPhysicsForce",
+ "UnityStandardAssets.Effects.ExplosionFireAndDebris",
+ "UnityStandardAssets.Effects.ExplosionPhysicsForce",
+ "UnityStandardAssets.Effects.Explosive",
+ "UnityStandardAssets.Effects.ExtinguishableParticleSystem",
+ "UnityStandardAssets.Effects.FireLight",
+ "UnityStandardAssets.Effects.Hose",
+ "UnityStandardAssets.Effects.ParticleSystemMultiplier",
+ "UnityStandardAssets.Effects.SmokeParticles",
+ "UnityStandardAssets.Effects.WaterHoseParticles",
+ "UnityStandardAssets.Utility.ActivateTrigger",
+ "UnityStandardAssets.Utility.AutoMoveAndRotate",
+ "UnityStandardAssets.Utility.DragRigidbody",
+ "UnityStandardAssets.Utility.DynamicShadowSettings",
+ "UnityStandardAssets.Utility.FollowTarget",
+ "UnityStandardAssets.Utility.FPSCounter",
+ "UnityStandardAssets.Utility.ObjectResetter",
+ "UnityStandardAssets.Utility.ParticleSystemDestroyer",
+ #if !UNITY_2019_4_OR_NEWER
+ "UnityStandardAssets.Utility.SimpleActivatorMenu",
+ #endif
+ "UnityStandardAssets.Utility.SimpleMouseRotator",
+ "UnityStandardAssets.Utility.SmoothFollow",
+ "UnityStandardAssets.Utility.TimedObjectActivator",
+ "UnityStandardAssets.Utility.TimedObjectDestructor",
+ "UnityStandardAssets.Utility.WaypointCircuit",
+ "UnityStandardAssets.Utility.WaypointProgressTracker",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplaneAiControl",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplaneAudio",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplaneController",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplaneControlSurfaceAnimator",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplanePropellerAnimator",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplaneUserControl2Axis",
+ "UnityStandardAssets.Vehicles.Aeroplane.AeroplaneUserControl4Axis",
+ "UnityStandardAssets.Vehicles.Aeroplane.JetParticleEffect",
+ "UnityStandardAssets.Vehicles.Aeroplane.LandingGear",
+ "UnityStandardAssets.Vehicles.Car.BrakeLight",
+ "UnityStandardAssets.Vehicles.Car.CarAIControl",
+ "UnityStandardAssets.Vehicles.Car.CarAudio",
+ "UnityStandardAssets.Vehicles.Car.CarController",
+ "UnityStandardAssets.Vehicles.Car.CarSelfRighting",
+ "UnityStandardAssets.Vehicles.Car.CarUserControl",
+ "UnityStandardAssets.Vehicles.Car.Mudguard",
+ "UnityStandardAssets.Vehicles.Car.SkidTrail",
+ "UnityStandardAssets.Vehicles.Car.Suspension",
+ "UnityStandardAssets.Vehicles.Car.WheelEffects",
+ "RenderHeads.Media.AVProVideo.ApplyToMaterial",
+ "RenderHeads.Media.AVProVideo.ApplyToMesh",
+ "RenderHeads.Media.AVProVideo.AudioOutput",
+ "RenderHeads.Media.AVProVideo.CubemapCube",
+ "RenderHeads.Media.AVProVideo.DebugOverlay",
+ "RenderHeads.Media.AVProVideo.DisplayBackground",
+ "RenderHeads.Media.AVProVideo.DisplayIMGUI",
+ "RenderHeads.Media.AVProVideo.DisplayUGUI",
+ "RenderHeads.Media.AVProVideo.MediaPlayer",
+ "RenderHeads.Media.AVProVideo.StreamParser",
+ "RenderHeads.Media.AVProVideo.SubtitlesUGUI",
+ "RenderHeads.Media.AVProVideo.UpdateStereoMaterial",
+ "AlphaButtonClickMask",
+ "EventSystemChecker",
+ "VirtualMarketplaceItem",
+ "SDK2UrlLauncher"
+ };
+
+ static readonly string[] ComponentTypeWhiteListSdk3 = new string[]
+ {
+ "VRC.SDK3.VRCDestructibleStandard",
+ "VRC.SDK3.Components.VRCVisualDamage",
+ "VRC.SDK3.Components.VRCAvatarPedestal",
+ "VRC.SDK3.Components.VRCPickup",
+ "VRC.SDK3.Components.VRCPortalMarker",
+ "VRC.SDK3.Components.VRCSpatialAudioSource",
+ "VRC.SDK3.Components.VRCMirrorReflection",
+ "VRC.SDK3.Components.VRCSceneDescriptor",
+ "VRC.SDK3.Components.VRCStation",
+ "VRC.SDK3.Components.VRCUiShape",
+ "VRC.SDK3.Components.VRCObjectSync",
+ "VRC.SDK3.Components.VRCObjectPool",
+ "VRC.SDK3.Video.Components.VRCUnityVideoPlayer",
+ "VRC.SDK3.Video.Components.AVPro.VRCAVProVideoPlayer",
+ "VRC.SDK3.Video.Components.AVPro.VRCAVProVideoScreen",
+ "VRC.SDK3.Video.Components.AVPro.VRCAVProVideoSpeaker",
+ "VRC.SDK3.Midi.VRCMidiListener",
+ "VRC.Udon.UdonBehaviour",
+ "VRC.Udon.AbstractUdonBehaviourEventProxy",
+ "UnityEngine.Animations.AimConstraint",
+ "UnityEngine.Animations.LookAtConstraint",
+ "UnityEngine.Animations.ParentConstraint",
+ "UnityEngine.Animations.PositionConstraint",
+ "UnityEngine.Animations.RotationConstraint",
+ "UnityEngine.Animations.ScaleConstraint",
+ "UnityEngine.ParticleSystemForceField",
+ "Cinemachine.Cinemachine3rdPersonAim",
+ "Cinemachine.CinemachineBlendListCamera",
+ "Cinemachine.CinemachineBrain",
+ "Cinemachine.CinemachineCameraOffset",
+ "Cinemachine.CinemachineClearShot",
+ "Cinemachine.CinemachineCollider",
+ "Cinemachine.CinemachineConfiner",
+ "Cinemachine.CinemachineDollyCart",
+ "Cinemachine.CinemachineExternalCamera",
+ "Cinemachine.CinemachineFollowZoom",
+ "Cinemachine.CinemachineFreeLook",
+ "Cinemachine.CinemachineMixingCamera",
+ "Cinemachine.CinemachinePath",
+ "Cinemachine.CinemachinePipeline",
+ "Cinemachine.CinemachinePixelPerfect",
+ "Cinemachine.CinemachineRecomposer",
+ "Cinemachine.CinemachineSmoothPath",
+ "Cinemachine.CinemachineStateDrivenCamera",
+ "Cinemachine.CinemachineStoryboard",
+ "Cinemachine.CinemachineTargetGroup",
+ "Cinemachine.CinemachineVirtualCamera",
+ "Cinemachine.Cinemachine3rdPersonFollow",
+ "Cinemachine.CinemachineBasicMultiChannelPerlin",
+ "Cinemachine.CinemachineComposer",
+ "Cinemachine.CinemachineFramingTransposer",
+ "Cinemachine.CinemachineGroupComposer",
+ "Cinemachine.CinemachineHardLockToTarget",
+ "Cinemachine.CinemachineHardLookAt",
+ "Cinemachine.CinemachineOrbitalTransposer",
+ "Cinemachine.CinemachinePOV",
+ "Cinemachine.CinemachineSameAsFollowTarget",
+ "Cinemachine.CinemachineTrackedDolly",
+ "Cinemachine.CinemachineTransposer",
+ "Cinemachine.CinemachineCore"
+ };
+
+ public static readonly string[] ShaderWhiteList = new string[]
+ {
+ "VRChat/Mobile/Standard Lite",
+ "VRChat/Mobile/Diffuse",
+ "VRChat/Mobile/Bumped Diffuse",
+ "VRChat/Mobile/Bumped Mapped Specular",
+ "VRChat/Mobile/Toon Lit",
+ "VRChat/Mobile/MatCap Lit",
+ "VRChat/Mobile/Lightmapped",
+ "VRChat/Mobile/Skybox",
+ "VRChat/Mobile/Particles/Additive",
+ "VRChat/Mobile/Particles/Multiply",
+ "FX/MirrorReflection",
+ "UI/Default",
+ };
+
+ private static readonly HashSet<int> scannedObjects = new HashSet<int>();
+
+ private static void ConfigureWhiteList(WhiteListConfiguration config)
+ {
+ if(ComponentTypeWhiteListConfiguration == config ||
+ config == WhiteListConfiguration.Unchanged)
+ {
+ return;
+ }
+
+ List<string> concatenation = new List<string>();
+ concatenation.AddRange(ComponentTypeWhiteListCommon);
+
+ switch(config)
+ {
+ case WhiteListConfiguration.VRCSDK2:
+ concatenation.AddRange(ComponentTypeWhiteListSdk2);
+ break;
+ case WhiteListConfiguration.VRCSDK3:
+ concatenation.AddRange(ComponentTypeWhiteListSdk3);
+ break;
+ }
+
+ ComponentTypeWhiteListConfiguration = config;
+ ComponentTypeWhiteList = concatenation.ToArray();
+ }
+
+ [PublicAPI]
+ public static void RemoveIllegalComponents(List<GameObject> targets, WhiteListConfiguration config, bool retry = true, HashSet<Type> tagWhitelistedTypes = null)
+ {
+ ConfigureWhiteList(config);
+
+ HashSet<Type> whitelist = ValidationUtils.WhitelistedTypes($"world{config}", ComponentTypeWhiteList);
+
+ // combine whitelist types from world tags with cached whitelist
+ if (tagWhitelistedTypes != null)
+ {
+ tagWhitelistedTypes.UnionWith(whitelist);
+ }
+
+ foreach(GameObject target in targets)
+ {
+ ValidationUtils.RemoveIllegalComponents(target, (tagWhitelistedTypes == null) ? whitelist : tagWhitelistedTypes, retry, true, true);
+ SecurityScan(target);
+ AddScanned(target);
+ }
+ }
+
+ private static void AddScanned(GameObject obj)
+ {
+ if(obj == null)
+ return;
+
+ if(!scannedObjects.Contains(obj.GetInstanceID()))
+ scannedObjects.Add(obj.GetInstanceID());
+
+ for(int idx = 0; idx < obj.transform.childCount; ++idx)
+ AddScanned(obj.transform.GetChild(idx)?.gameObject);
+ }
+
+ private static bool WasScanned(GameObject obj)
+ {
+ return scannedObjects.Contains(obj.GetInstanceID());
+ }
+
+ [PublicAPI]
+ public static void ScanGameObject(GameObject target, WhiteListConfiguration config)
+ {
+ if(WasScanned(target))
+ {
+ return;
+ }
+
+ ConfigureWhiteList(config);
+ HashSet<Type> whitelist = ValidationUtils.WhitelistedTypes("world" + config, ComponentTypeWhiteList);
+ ValidationUtils.RemoveIllegalComponents(target, whitelist);
+ SecurityScan(target);
+ AddScanned(target);
+
+ // Must be called after AddScanned to avoid infinite recursion.
+ ScanDropdownTemplates(target, config);
+ }
+
+ [PublicAPI]
+ public static void ClearScannedGameObjectCache()
+ {
+ scannedObjects.Clear();
+ }
+
+ [PublicAPI]
+ public static IEnumerable<Shader> FindIllegalShaders(GameObject target)
+ {
+ return ShaderValidation.FindIllegalShaders(target, ShaderWhiteList);
+ }
+
+ private static void SecurityScan(GameObject target)
+ {
+ PlayableDirector[] playableDirectors = target.GetComponentsInChildren<PlayableDirector>(true);
+ foreach(PlayableDirector playableDirector in playableDirectors)
+ {
+ StripPlayableDirectorWithPrefabs(playableDirector);
+ }
+ }
+
+ private static void ScanDropdownTemplates(GameObject target, WhiteListConfiguration config)
+ {
+ Dropdown[] dropdowns = target.GetComponentsInChildren<Dropdown>(true);
+ foreach(Dropdown dropdown in dropdowns)
+ {
+ if(dropdown == null)
+ {
+ continue;
+ }
+
+ RectTransform dropdownTemplate = dropdown.template;
+ if(dropdownTemplate == null)
+ {
+ continue;
+ }
+
+ ScanGameObject(dropdownTemplate.transform.root.gameObject, config);
+ }
+
+ #if TextMeshPro
+ TMP_Dropdown[] tmpDropdowns = target.GetComponentsInChildren<TMP_Dropdown>(true);
+ foreach(TMP_Dropdown textMeshProDropdown in tmpDropdowns)
+ {
+ if(textMeshProDropdown == null)
+ {
+ continue;
+ }
+
+ RectTransform dropdownTemplate = textMeshProDropdown.template;
+ if(dropdownTemplate == null)
+ {
+ continue;
+ }
+
+ ScanGameObject(dropdownTemplate.transform.root.gameObject, config);
+ }
+ #endif
+ }
+
+ private static void StripPlayableDirectorWithPrefabs(PlayableDirector playableDirector)
+ {
+ if(!(playableDirector.playableAsset is UnityEngine.Timeline.TimelineAsset timelineAsset))
+ return;
+
+ IEnumerable<TrackAsset> tracks = timelineAsset.GetOutputTracks();
+ foreach(TrackAsset track in tracks)
+ {
+ if(!(track is ControlTrack))
+ continue;
+
+ IEnumerable<TimelineClip> clips = track.GetClips();
+ foreach(TimelineClip clip in clips)
+ {
+ if(clip.asset is ControlPlayableAsset controlPlayableAsset && controlPlayableAsset.prefabGameObject != null)
+ {
+ UnityEngine.Object.Destroy(playableDirector);
+ VRC.Core.Logger.LogWarning("PlayableDirector containing prefab removed", DebugLevel, playableDirector.gameObject);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs.meta b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs.meta
new file mode 100644
index 00000000..f84c2b09
--- /dev/null
+++ b/VRCSDK3AvatarsLegacy/Assets/VRCSDK/Dependencies/VRChat/Scripts/Validation/WorldValidation.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9b03724cd556cb047b2da80492ea28a5
+timeCreated: 1504829091
+licenseType: Pro
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: