using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Ubisoft { /// /// Utility class responsible for allowing some code, typically Unity API, called from a worker thread to be executed in the main thread /// public class HtMainThreadUtility : MonoBehaviour, HtSingletonHolder.IConsumer { internal class OrderManager { /// Holds actions received from another Thread. Will be copied into ActionsToExecute and then executed from there. private List ActionRequests { get; set; } /// Holds Actions copied from ActionRequests to be executed. private List ActionsToExecute { get; set; } /// Used to know if whe have new Action function to execute. This prevents the use of the lock keyword every frame. private volatile bool m_areAnyRequestsPending; internal OrderManager() { ActionRequests = new List(); ActionsToExecute = new List(); m_areAnyRequestsPending = false; } internal void Order(Action action) { if (action != null) { lock (ActionRequests) { ActionRequests.Add(action); m_areAnyRequestsPending = true; } } } internal void Update() { if (m_areAnyRequestsPending) { //Clear the old actions from the ActionsToExecute queue ActionsToExecute.Clear(); lock (ActionRequests) { //Copy ActionRequests into the ActionsToExecute variable ActionsToExecute.AddRange(ActionRequests); //Now clear the ActionRequests since we've done copying it ActionRequests.Clear(); m_areAnyRequestsPending = false; } // Loop and execute the functions from the ActionsToExecute for (int i = 0; i < ActionsToExecute.Count; i++) { ActionsToExecute[i].Invoke(); } } } } private static HtMainThreadUtility s_instance; /// /// Return the singleton instance. Make sure to call this property in the main thread at least once before /// attempting to enqueue any actions from a worker thread. /// public static HtMainThreadUtility Instance { get { if (s_instance == null) { s_instance = HtSingletonHolder.AddMainComponent(); s_instance.Init(); } return s_instance; } } /// /// Manager responsible for calling functions on Update() /// private OrderManager UpdateOrderManager { get; set; } /// /// Manager responsible for calling functions on LateUpdate() /// private OrderManager LateUpdateOrderManager { get; set; } /// /// Manager responsible for calling functions on FixedUpdate() /// private OrderManager FixedUpdateOrderManager { get; set; } private void Init() { UpdateOrderManager = new OrderManager(); LateUpdateOrderManager = new OrderManager(); FixedUpdateOrderManager = new OrderManager(); } /// /// Execute a coroutine in Update() in the main thread. /// /// Coroutine to execute in Update() in the main thread. public void ExecuteCoroutine(IEnumerator action) { ExecuteInUpdate(() => StartCoroutine(action)); } /// /// Execute an action in Update() in the main thread. /// /// Action to execute in Update() the main thread. public void ExecuteInUpdate(Action action) { UpdateOrderManager.Order(action); } /// /// Execute an action in LateUpdate() in the main thread. /// /// Action to execute in LateUpdate() the main thread. public void ExecuteInLateUpdate(Action action) { LateUpdateOrderManager.Order(action); } /// /// Execute an action in FixedUpdate() in the main thread. /// /// Action to execute in FixedUpdate() the main thread. public void ExecuteInFixedUpdate(Action action) { FixedUpdateOrderManager.Order(action); } public void Update() { UpdateOrderManager.Update(); } public void LateUpdate() { LateUpdateOrderManager.Update(); } public void FixedUpdate() { FixedUpdateOrderManager.Update(); } public void OnDestroy() { s_instance = null; if (s_instance != null) { HtSingletonHolder.RemoveMainComponent(false); s_instance = null; } } public void DestroySingleton(Component singleton) { if (singleton == this) { Debug.LogInformation("Destroy " + GetType()); s_instance = null; } } } }