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;
}
}
}
}