using System;
using System.Collections.Generic;
using System.Linq;
// ReSharper disable DelegateSubtraction
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Networking.PlayerConnection;
#endif
using UnityEngine.Networking.PlayerConnection;
using UnityEngine.ResourceManagement.Util;
namespace UnityEngine.ResourceManagement.Diagnostics
{
///
/// Collects ResourceManager events and passed them on the registered event handlers. In editor play mode, events are passed directly to the ResourceManager profiler window.
/// In player builds, events are sent to the editor via the EditorConnection API.
///
public class DiagnosticEventCollectorSingleton : ComponentSingleton
{
static Guid s_editorConnectionGuid;
internal Dictionary m_CreatedEvents = new Dictionary();
internal List m_UnhandledEvents = new List();
internal DelegateList s_EventHandlers = DelegateList.CreateWithGlobalCache();
///
/// The guid used for the PlayerConnect messaging system.
///
public static Guid PlayerConnectionGuid
{
get
{
if (s_editorConnectionGuid == Guid.Empty)
s_editorConnectionGuid = new Guid(1, 2, 3, new byte[] { 20, 1, 32, 32, 4, 9, 6, 44 });
return s_editorConnectionGuid;
}
}
///
protected override string GetGameObjectName() => "EventCollector";
///
/// Register for diagnostic events. If there is no collector, this will fail and return false.
///
/// The handler method action.
/// Register or unregister.
/// If true, the event collector will be created if needed.
/// True if registered, false if not.
public static bool RegisterEventHandler(Action handler, bool register, bool create)
{
if (register && (create || Exists))
{
Instance.RegisterEventHandler(handler);
return true;
}
if (!register && Exists)
{
Instance.UnregisterEventHandler(handler);
}
return false;
}
internal void RegisterEventHandler(Action handler)
{
Debug.Assert(m_UnhandledEvents != null, "DiagnosticEventCollectorSingleton.RegisterEventHandler - s_unhandledEvents == null.");
if (handler == null)
throw new ArgumentNullException("handler");
s_EventHandlers.Add(handler);
//Ensure that events are handled in frame order
var combinedAndSortedList = m_UnhandledEvents.Concat(m_CreatedEvents.Values).OrderBy(evt => evt.Frame);
foreach (var evt in combinedAndSortedList)
handler(evt);
m_UnhandledEvents.Clear();
}
///
/// Unregister event hander
///
/// Method or delegate that will handle the events
public void UnregisterEventHandler(Action handler)
{
if (handler == null)
throw new ArgumentNullException("handler");
s_EventHandlers.Remove(handler);
}
///
/// Send a event to all registered handlers
///
/// The event to send
public void PostEvent(DiagnosticEvent diagnosticEvent)
{
if (diagnosticEvent.Stream == (int)ResourceManager.DiagnosticEventType.AsyncOperationCreate && !m_CreatedEvents.ContainsKey(diagnosticEvent.ObjectId))
m_CreatedEvents.Add(diagnosticEvent.ObjectId, diagnosticEvent);
else if (diagnosticEvent.Stream == (int)ResourceManager.DiagnosticEventType.AsyncOperationDestroy)
m_CreatedEvents.Remove(diagnosticEvent.ObjectId);
Debug.Assert(m_UnhandledEvents != null, "DiagnosticEventCollectorSingleton.PostEvent - s_unhandledEvents == null.");
if (s_EventHandlers.Count > 0)
s_EventHandlers.Invoke(diagnosticEvent);
else
m_UnhandledEvents.Add(diagnosticEvent);
}
void Awake()
{
#if !UNITY_EDITOR
RegisterEventHandler((DiagnosticEvent diagnosticEvent) => {PlayerConnection.instance.Send(DiagnosticEventCollectorSingleton.PlayerConnectionGuid, diagnosticEvent.Serialize()); });
#endif
}
float m_lastTickSent = 0;
int m_lastFrame = 0;
float fpsAvg = 30;
void Update()
{
if (s_EventHandlers.Count > 0)
{
var elapsed = Time.realtimeSinceStartup - m_lastTickSent;
if (elapsed > .25f)
{
var fps = (Time.frameCount - m_lastFrame) / elapsed;
m_lastFrame = Time.frameCount;
fpsAvg = (fpsAvg + fps) * .5f;
m_lastTickSent = Time.realtimeSinceStartup;
int heapKB = (int)(Profiling.Profiler.GetMonoUsedSizeLong() / 1024);
PostEvent(new DiagnosticEvent("FrameCount", "FPS", 2, 1, Time.frameCount, (int)fpsAvg, null));
PostEvent(new DiagnosticEvent("MemoryCount", "MonoHeap", 3, 2, Time.frameCount, heapKB, null));
}
}
}
}
///
/// Collects ResourceManager events and passed them on the registered event handlers. In editor play mode, events are passed directly to the ResourceManager profiler window.
/// In player builds, events are sent to the editor via the EditorConnection API.
///
public class DiagnosticEventCollector : MonoBehaviour
{
static DiagnosticEventCollector s_Collector;
///
/// The guid used for the PlayerConnect messaging system.
///
public static Guid PlayerConnectionGuid => DiagnosticEventCollectorSingleton.PlayerConnectionGuid;
///
/// Retrieves the global event collector. A new one is created if needed.
///
/// The event collector global instance.
public static DiagnosticEventCollector FindOrCreateGlobalInstance()
{
if (s_Collector == null)
{
var go = new GameObject("EventCollector", typeof(DiagnosticEventCollector));
s_Collector = go.GetComponent();
go.hideFlags = HideFlags.DontSave;// HideFlags.HideAndDontSave;
}
return s_Collector;
}
///
/// Register for diagnostic events. If there is no collector, this will fail and return false.
///
/// The handler method action.
/// Register or unregister.
/// If true, the event collector will be created if needed.
/// True if registered, false if not.
public static bool RegisterEventHandler(Action handler, bool register, bool create) => DiagnosticEventCollectorSingleton.RegisterEventHandler(handler, register, create);
///
/// Unregister event hander
///
/// Method or delegate that will handle the events
public void UnregisterEventHandler(Action handler) => DiagnosticEventCollectorSingleton.Instance.UnregisterEventHandler(handler);
///
/// Send a event to all registered handlers
///
/// The event to send
public void PostEvent(DiagnosticEvent diagnosticEvent) => DiagnosticEventCollectorSingleton.Instance.PostEvent(diagnosticEvent);
#if UNITY_EDITOR
public static class PlayStateNotifier
{
}
#endif
}
}