using System; using System.Collections.Generic; using System.Text.RegularExpressions; using ModestTree; #if UNITY_EDITOR using UnityEngine.Profiling; using System.Threading; #endif namespace Zenject { [NoReflectionBaking] public class ProfileBlock : IDisposable { #if UNITY_EDITOR static int _blockCount; static ProfileBlock _instance = new ProfileBlock(); static Dictionary _nameCache = new Dictionary(); ProfileBlock() { } public static Thread UnityMainThread { get; set; } public static Regex ProfilePattern { get; set; } static int GetHashCode(object p1, object p2) { unchecked // Overflow is fine, just wrap { int hash = 17; hash = hash * 29 + p1.GetHashCode(); hash = hash * 29 + p2.GetHashCode(); return hash; } } static int GetHashCode(object p1, object p2, object p3) { unchecked // Overflow is fine, just wrap { int hash = 17; hash = hash * 29 + p1.GetHashCode(); hash = hash * 29 + p2.GetHashCode(); hash = hash * 29 + p3.GetHashCode(); return hash; } } public static ProfileBlock Start(string sampleNameFormat, object obj1, object obj2) { #if ZEN_TESTS_OUTSIDE_UNITY return null; #else if (UnityMainThread == null || !UnityMainThread.Equals(Thread.CurrentThread)) { return null; } if (!Profiler.enabled) { return null; } // We need to ensure that we do not have per-frame allocations in ProfileBlock // to avoid infecting the test too much, so use a cache of formatted strings given // the input values // This only works if the input values do not change per frame var hash = GetHashCode(sampleNameFormat, obj1, obj2); string formatString; if (!_nameCache.TryGetValue(hash, out formatString)) { formatString = string.Format(sampleNameFormat, obj1, obj2); _nameCache.Add(hash, formatString); } return StartInternal(formatString); #endif } public static ProfileBlock Start(string sampleNameFormat, object obj) { #if ZEN_TESTS_OUTSIDE_UNITY return null; #else if (UnityMainThread == null || !UnityMainThread.Equals(Thread.CurrentThread)) { return null; } if (!Profiler.enabled) { return null; } // We need to ensure that we do not have per-frame allocations in ProfileBlock // to avoid infecting the test too much, so use a cache of formatted strings given // the input values // This only works if the input values do not change per frame var hash = GetHashCode(sampleNameFormat, obj); string formatString; if (!_nameCache.TryGetValue(hash, out formatString)) { formatString = string.Format(sampleNameFormat, obj); _nameCache.Add(hash, formatString); } return StartInternal(formatString); #endif } public static ProfileBlock Start(string sampleName) { #if ZEN_TESTS_OUTSIDE_UNITY return null; #else if (UnityMainThread == null || !UnityMainThread.Equals(Thread.CurrentThread)) { return null; } if (!Profiler.enabled) { return null; } return StartInternal(sampleName); #endif } static ProfileBlock StartInternal(string sampleName) { Assert.That(Profiler.enabled); if (ProfilePattern == null || ProfilePattern.Match(sampleName).Success) { Profiler.BeginSample(sampleName); _blockCount++; return _instance; } return null; } public void Dispose() { _blockCount--; Assert.That(_blockCount >= 0); Profiler.EndSample(); } #else ProfileBlock(string sampleName, bool rootBlock) { } ProfileBlock(string sampleName) : this(sampleName, false) { } public static Regex ProfilePattern { get; set; } public static ProfileBlock Start() { return null; } public static ProfileBlock Start(string sampleNameFormat, object obj1, object obj2) { return null; } // Remove the call completely for builds public static ProfileBlock Start(string sampleNameFormat, object obj) { return null; } // Remove the call completely for builds public static ProfileBlock Start(string sampleName) { return null; } public void Dispose() { } #endif } }