diff --git a/src/DebugUtils/UnityDebugService.meta b/src/DebugUtils/UnityDebugService.meta new file mode 100644 index 0000000..920d753 --- /dev/null +++ b/src/DebugUtils/UnityDebugService.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 69f2abf8b8744c840b2da7f8988b3f8d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/DebugUtils/UnityDebugService.cs b/src/DebugUtils/UnityDebugService/UnityDebugService.cs similarity index 74% rename from src/DebugUtils/UnityDebugService.cs rename to src/DebugUtils/UnityDebugService/UnityDebugService.cs index 4f6a7d7..8c3c9d0 100644 --- a/src/DebugUtils/UnityDebugService.cs +++ b/src/DebugUtils/UnityDebugService/UnityDebugService.cs @@ -1,5 +1,8 @@ -using System; +using DCFApixels.DragonECS.Unity.Internal; +using System; +using System.Reflection; using Unity.Profiling; +using UnityEditor; using UnityEngine; #region [InitializeOnLoad] @@ -12,14 +15,12 @@ namespace DCFApixels.DragonECS } #endif #endregion - namespace DCFApixels.DragonECS { // Методы юнитевского Debug и ProfilerMarker потоко безопасны public partial class UnityDebugService : DebugService { private ProfilerMarker[] _profilerMarkers = new ProfilerMarker[64]; - static UnityDebugService() { Activate(); @@ -29,6 +30,27 @@ namespace DCFApixels.DragonECS if (Instance.GetType() == typeof(UnityDebugService)) { return; } Set(); } + protected override void OnEnableBaseService(DebugService oldService) + { + EditorGUI.hyperLinkClicked -= EditorGUI_hyperLinkClicked; + EditorGUI.hyperLinkClicked += EditorGUI_hyperLinkClicked; + Application.logMessageReceived -= Application_logMessageReceived; + Application.logMessageReceived += Application_logMessageReceived; + } + protected override void OnDisableBaseService(DebugService nextService) + { + Application.logMessageReceived -= Application_logMessageReceived; + EditorGUI.hyperLinkClicked -= EditorGUI_hyperLinkClicked; + } + private void EditorGUI_hyperLinkClicked(EditorWindow editor, HyperLinkClickedEventArgs args) + { + throw new NotImplementedException(); + } + private void Application_logMessageReceived(string logString, string stackTrace, LogType type) + { + } + + protected override DebugService CreateThreadInstance() { diff --git a/src/DebugUtils/UnityDebugService.cs.meta b/src/DebugUtils/UnityDebugService/UnityDebugService.cs.meta similarity index 100% rename from src/DebugUtils/UnityDebugService.cs.meta rename to src/DebugUtils/UnityDebugService/UnityDebugService.cs.meta diff --git a/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs b/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs new file mode 100644 index 0000000..93edbee --- /dev/null +++ b/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs @@ -0,0 +1,119 @@ +#if UNITY_EDITOR +using System; +using System.Reflection; +using System.Threading; +using UnityEditor; +using UnityEngine; + +namespace DCFApixels.DragonECS.Unity.Internal +{ + internal class UnityDebugServiceStorage : ScriptableSingleton + { + private static readonly MethodInfo _getLogsCountMethod; + public static readonly bool IsSupportAutoLingks; + static UnityDebugServiceStorage() + { + var logEntriesType = typeof(EditorWindow).Assembly.GetType("UnityEditor.LogEntries"); + if (logEntriesType != null) + { + _getLogsCountMethod = logEntriesType.GetMethod("GetCount", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); + } + IsSupportAutoLingks = _getLogsCountMethod != null; + + EditorGUI.hyperLinkClicked -= EditorGUI_hyperLinkClicked; + Application.logMessageReceived -= Application_logMessageReceived; + _consoleLogCounter = -1; + if (IsSupportAutoLingks) + { + EditorGUI.hyperLinkClicked += EditorGUI_hyperLinkClicked; + Application.logMessageReceived += Application_logMessageReceived; + _consoleLogCounter = GetConsoleLogCount(); + } + } + public UnityDebugServiceStorage() { } + + private const int IntervalChecksTicksThreshold = 100; + private static int _consoleLogCounter; + private static int _intervalChecksTicks = 0; + private static StructList _recycledIndexes; + private static StructList _logEntries; + private static object _lock = new object(); + + private static void EditorGUI_hyperLinkClicked(EditorWindow window, HyperLinkClickedEventArgs args) + { + throw new NotImplementedException(); + } + + private static void Application_logMessageReceived(string logString, string stackTrace, LogType type) + { + if (_intervalChecksTicks >= IntervalChecksTicksThreshold || + _logEntries.Count >= _logEntries.Capacity - 1) + { + CheckConsoleClean(); + _intervalChecksTicks = 0; + } + _logEntries.Add(new LogEntry(logString, stackTrace)); + + Interlocked.Increment(ref _consoleLogCounter); + Interlocked.Increment(ref _intervalChecksTicks); + } + + + private static bool CheckConsoleClean() + { + int currentCount = GetConsoleLogCount(); + if (_consoleLogCounter > currentCount) + { + + error + + _consoleLogCounter = currentCount; + return true; + } + return false; + } + private static int GetConsoleLogCount() + { + return (int)_getLogsCountMethod.Invoke(null, null); + } + private static string CreateIndexedLink(int index) + { + return $" "; + } + + + + //multi thread access. + public static string GetHyperLink() + { + return instance.GetHyperLink_Internal(); + } + public string GetHyperLink_Internal() + { + string hyperLink; + if (_recycledIndexes.Count > 0) + { + hyperLink = _recycledIndexes.Dequeue(); + } + else + { + hyperLink = CreateIndexedLink(_logEntries.Count); + } + return hyperLink; + } + + + + private readonly struct LogEntry + { + public readonly string LogString; + public readonly string StackTrace; + public LogEntry(string logString, string stackTrace) + { + LogString = logString; + StackTrace = stackTrace; + } + } + } +} +#endif \ No newline at end of file diff --git a/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs.meta b/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs.meta new file mode 100644 index 0000000..63688a2 --- /dev/null +++ b/src/DebugUtils/UnityDebugService/UnityDebugServiceStorage.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8666cb6c75b076c41910acbe59d258af \ No newline at end of file diff --git a/src/Internal/ArrayUtility.cs b/src/Internal/DragonArrayUtility.cs similarity index 72% rename from src/Internal/ArrayUtility.cs rename to src/Internal/DragonArrayUtility.cs index b17c463..b3e1dbb 100644 --- a/src/Internal/ArrayUtility.cs +++ b/src/Internal/DragonArrayUtility.cs @@ -72,58 +72,31 @@ namespace DCFApixels.DragonECS.Unity.Internal } } - internal static class ArrayUtility + internal static class DragonArrayUtility { - private static int GetHighBitNumber(uint bits) - { - if (bits == 0) - { - return -1; - } - int bit = 0; - if ((bits & 0xFFFF0000) != 0) - { - bits >>= 16; - bit |= 16; - } - if ((bits & 0xFF00) != 0) - { - bits >>= 8; - bit |= 8; - } - if ((bits & 0xF0) != 0) - { - bits >>= 4; - bit |= 4; - } - if ((bits & 0xC) != 0) - { - bits >>= 2; - bit |= 2; - } - if ((bits & 0x2) != 0) - { - bit |= 1; - } - return bit; - } - public static int NormalizeSizeToPowerOfTwo(int minSize) + public static int NextPow2(int v) { unchecked { - return 1 << (GetHighBitNumber((uint)minSize - 1u) + 1); + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return ++v; } } - public static int NormalizeSizeToPowerOfTwo_ClampOverflow(int minSize) + public static int NextPow2_ClampOverflow(int v) { unchecked { - int hibit = (GetHighBitNumber((uint)minSize - 1u) + 1); - if (hibit >= 32) + const int NO_SIGN_HIBIT = 0x40000000; + if ((v & NO_SIGN_HIBIT) != 0) { return int.MaxValue; } - return 1 << hibit; + return NextPow2(v); } } public static void Fill(T[] array, T value, int startIndex = 0, int length = -1) diff --git a/src/Internal/ArrayUtility.cs.meta b/src/Internal/DragonArrayUtility.cs.meta similarity index 100% rename from src/Internal/ArrayUtility.cs.meta rename to src/Internal/DragonArrayUtility.cs.meta diff --git a/src/Internal/Editor/EcsGUI.cs b/src/Internal/Editor/EcsGUI.cs index 9cf01be..1900fb7 100644 --- a/src/Internal/Editor/EcsGUI.cs +++ b/src/Internal/Editor/EcsGUI.cs @@ -432,6 +432,7 @@ namespace DCFApixels.DragonECS.Unity.Editors } else if (current.type == EventType.MouseDown && current.clickCount >= 2) { + //UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(); //TODO AssetDatabase.OpenAsset(script); } } diff --git a/src/Internal/Utils/StructList.cs b/src/Internal/Utils/StructList.cs index 793319d..88b8100 100644 --- a/src/Internal/Utils/StructList.cs +++ b/src/Internal/Utils/StructList.cs @@ -3,13 +3,18 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading; +using UnityEngine; namespace DCFApixels.DragonECS.Unity.Internal { + [Serializable] [DebuggerDisplay("Count: {Count}")] internal struct StructList { + [SerializeField] internal T[] _items; + [SerializeField] internal int _count; public IEnumerable Enumerable @@ -39,7 +44,7 @@ namespace DCFApixels.DragonECS.Unity.Internal set { if (value <= _items.Length) { return; } - value = ArrayUtility.NormalizeSizeToPowerOfTwo(value); + value = DragonArrayUtility.NextPow2(value); Array.Resize(ref _items, value); } } @@ -66,7 +71,7 @@ namespace DCFApixels.DragonECS.Unity.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public StructList(int capacity) { - _items = new T[ArrayUtility.NormalizeSizeToPowerOfTwo(capacity)]; + _items = new T[DragonArrayUtility.NextPow2(capacity)]; _count = 0; } @@ -92,6 +97,24 @@ namespace DCFApixels.DragonECS.Unity.Internal _items[idnex2] = tmp; } [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref T FastDequeue() + { +#if DEBUG + if (_count <= 0) { Throw.ArgumentOutOfRange(); } +#endif + return ref _items[--_count]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Dequeue() + { +#if DEBUG + if (_count <= 0) { Throw.ArgumentOutOfRange(); } +#endif + T result = _items[--_count]; + _items[_count] = default; + return result; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FastRemoveAt(int index) { #if DEBUG @@ -178,4 +201,38 @@ namespace DCFApixels.DragonECS.Unity.Internal return _items; } } + + internal static class StructListExt + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Add_MultiAccess(this ref StructList self, T item) + { + var index = Interlocked.Increment(ref self._count); + index -= 1; + if (index >= self._items.Length) + { + lock (self._items) + { + if (index >= self._items.Length) + { + Array.Resize(ref self._items, self._items.Length << 1); + } + } + } + self._items[index] = item; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryDequeue_MultiAccess(this ref StructList self, T item) + { + var index = Interlocked.Increment(ref self._count); + +#if DEBUG + if (_count <= 0) { Throw.ArgumentOutOfRange(); } +#endif + T result = _items[--_count]; + _items[_count] = default; + return result; + } + } } \ No newline at end of file