Add VRChat and GitHub packages to Unity project
This commit is contained in:
1 parent
0334a52b21
commit
85538aaccc
3297 files changed
+847877
No files matched your search
+167
@@ -0,0 +1,167 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15ecac6f7fdc1bc4fb723fee6f4635dd
|
||||
timeCreated: 1540944864
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
namespace VRCSDK2.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
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1ce994297384ff1bc330196df61b7ca
|
||||
timeCreated: 1561267912
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b99e0ada644703542a2c3f854643609f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: abda65e062e44213aa3e1f4c82b400a8
|
||||
timeCreated: 1563937347
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f28c978154794266b38d686139c6b872
|
||||
timeCreated: 1561249515
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using VRCSDK2.Validation.Performance.Filters;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cdca9d06d1b4732b9ccb82a38bb8d9c
|
||||
timeCreated: 1563939583
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace VRCSDK2.Validation.Performance
|
||||
{
|
||||
public enum PerformanceInfoDisplayLevel
|
||||
{
|
||||
None,
|
||||
|
||||
Verbose,
|
||||
Info,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5ed7498cb1a46c78eab031f5f32448c
|
||||
timeCreated: 1561267918
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace VRCSDK2.Validation.Performance
|
||||
{
|
||||
public enum PerformanceRating
|
||||
{
|
||||
None = 0,
|
||||
Excellent = 1,
|
||||
Good = 2,
|
||||
Medium = 3,
|
||||
Poor = 4,
|
||||
VeryPoor = 5
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5019a55ee9e2404c81bc61a39a010d8d
|
||||
timeCreated: 1561267904
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using VRCSDK2.Validation.Performance.Scanners;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4afb61f36d144fc381114cd7f78d13e7
|
||||
timeCreated: 1561259704
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67bd9cc73f0a3e64e99dac8128cdf4b2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bd0691a021844f49444a04a959d6328
|
||||
timeCreated: 1561279121
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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<Animation> animationBuffer = new List<Animation>();
|
||||
yield return ScanAvatarForComponentsOfType(avatarObject, animationBuffer);
|
||||
if(shouldIgnoreComponent != null)
|
||||
{
|
||||
animationBuffer.RemoveAll(c => shouldIgnoreComponent(c));
|
||||
}
|
||||
|
||||
animatorCount += animationBuffer.Count;
|
||||
|
||||
perfStats.animatorCount = animatorCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08c8e931d0544866a0f626855d9c1841
|
||||
timeCreated: 1561251363
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3a8bba736414d1aaa9e766da27b56b5
|
||||
timeCreated: 1561255618
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0cec88b5a46f459195f10a2f11fddb2f
|
||||
timeCreated: 1561255843
|
||||
+191
@@ -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 VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a226df494ef04404a9a47c714822ab9f
|
||||
timeCreated: 1561251560
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 405778fdc32c44c1bb9fdd0476fb0007
|
||||
timeCreated: 1561256390
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec87392b85844f7bb526a48ec866a8f0
|
||||
timeCreated: 1561253047
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38bca10261df4ddfa10cff3b3bbb9428
|
||||
timeCreated: 1561249198
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10723e354ff14f98a49ab797b3f005e6
|
||||
timeCreated: 1561250267
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a94ecdeecd04f85824cc3244be5712a
|
||||
timeCreated: 1561256481
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using VRCSDK2.Validation.Performance.Stats;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2efd714b564547b4be1ebd1f2700668b
|
||||
timeCreated: 1561251810
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68702a268e708b043a0dca66980158fa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+589
@@ -0,0 +1,589 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRCSDK2.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 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 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 Enum.GetValues(typeof(AvatarPerformanceCategory)))
|
||||
{
|
||||
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 Enum.GetValues(typeof(AvatarPerformanceCategory)))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bf4fb79a49d4b109c4dce6b38e5548e
|
||||
timeCreated: 1561267926
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRCSDK2.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;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f742c36dce5730f4d96e37d82c330584
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Serialization;
|
||||
|
||||
namespace VRCSDK2.Validation.Performance.Stats
|
||||
{
|
||||
public class AvatarPerformanceStatsLevelSet : ScriptableObject
|
||||
{
|
||||
[FormerlySerializedAs("veryGood")]
|
||||
public AvatarPerformanceStatsLevel excellent;
|
||||
public AvatarPerformanceStatsLevel good;
|
||||
public AvatarPerformanceStatsLevel medium;
|
||||
[FormerlySerializedAs("bad")]
|
||||
public AvatarPerformanceStatsLevel poor;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 468554b1bfc447f41a20a2f5bae65d16
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user