ZoeAppClass/Library/PackageCache/com.unity.addressables@1.1.10/Runtime/ResourceManager/AsyncOperations/AsyncOperationBase.cs

458 lines
16 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.Util;
// ReSharper disable DelegateSubtraction
namespace UnityEngine.ResourceManagement.AsyncOperations
{
internal interface ICachable
{
int Hash { get; set; }
}
internal interface IAsyncOperation
{
object GetResultAsObject();
Type ResultType { get; }
int Version { get; }
string DebugName { get; }
void DecrementReferenceCount();
void IncrementReferenceCount();
int ReferenceCount { get; }
float PercentComplete { get; }
AsyncOperationStatus Status { get; }
Exception OperationException { get; }
bool IsDone { get; }
Action<IAsyncOperation> OnDestroy { set; }
void GetDependencies(List<AsyncOperationHandle> deps);
event Action<AsyncOperationHandle> CompletedTypeless;
event Action<AsyncOperationHandle> Destroyed;
void InvokeCompletionEvent();
System.Threading.Tasks.Task<object> Task { get; }
void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks);
AsyncOperationHandle Handle { get; }
}
/// <summary>
/// base class for implemented AsyncOperations, implements the needed interfaces and consolidates redundant code
/// </summary>
public abstract class AsyncOperationBase<TObject> : IAsyncOperation
{
/// <summary>
/// This will be called by the resource manager after all dependent operation complete. This method should not be called manually.
/// A custom operation should override this method and begin work when it is called.
/// </summary>
protected abstract void Execute();
/// <summary>
/// This will be called by the resource manager when the reference count of the operation reaches zero. This method should not be called manually.
/// A custom operation should override this method and release any held resources
/// </summary>
protected virtual void Destroy() { }
/// <summary>
/// A custom operation should override this method to return the progress of the operation.
/// </summary>
/// <returns>Progress of the operation. Value should be between 0.0f and 1.0f</returns>
protected virtual float Progress { get { return 0; } }
/// <summary>
/// A custom operation should override this method to provide a debug friendly name for the operation.
/// </summary>
protected virtual string DebugName { get { return this.ToString(); } }
/// <summary>
/// A custom operation should override this method to provide a list of AsyncOperationHandles that it depends on.
/// </summary>
/// <param name="dependencies">The list that should be populated with dependent AsyncOperationHandles.</param>
protected virtual void GetDependencies(List<AsyncOperationHandle> dependencies) { }
internal TObject Result { get { return m_Result; } }
int m_referenceCount = 1;
AsyncOperationStatus m_Status;
Exception m_Error;
ResourceManager m_RM;
TObject m_Result;
private int m_Version;
internal int Version { get { return m_Version; } }
DelegateList<AsyncOperationHandle> m_CompletedAction;
DelegateList<AsyncOperationHandle> m_DestroyedAction;
DelegateList<AsyncOperationHandle<TObject>> m_CompletedActionT;
Action<IAsyncOperation> m_OnDestroyAction;
internal Action<IAsyncOperation> OnDestroy { set { m_OnDestroyAction = value; } }
internal int ReferenceCount { get { return m_referenceCount; } }
Action<AsyncOperationHandle> m_dependencyCompleteAction;
protected AsyncOperationBase()
{
m_UpdateCallback = UpdateCallback;
m_dependencyCompleteAction = o => InvokeExecute();
}
internal static string ShortenPath(string p, bool keepExtension)
{
var slashIndex = p.LastIndexOf('/');
if (slashIndex > 0)
p = p.Substring(slashIndex + 1);
if (!keepExtension)
{
slashIndex = p.LastIndexOf('.');
if (slashIndex > 0)
p = p.Substring(0, slashIndex);
}
return p;
}
internal void IncrementReferenceCount()
{
if (m_referenceCount == 0)
throw new Exception(string.Format("Cannot increment reference count on operation {0} because it has already been destroyed", this));
m_referenceCount++;
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationReferenceCount, m_referenceCount);
}
internal void DecrementReferenceCount()
{
if (m_referenceCount <= 0)
throw new Exception(string.Format("Cannot decrement reference count for operation {0} because it is already 0", this));
m_referenceCount--;
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationReferenceCount, m_referenceCount);
if (m_referenceCount == 0)
{
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationDestroy);
Destroy();
if (m_DestroyedAction != null)
{
m_DestroyedAction.Invoke(new AsyncOperationHandle<TObject>(this));
m_DestroyedAction.Clear();
}
if (m_OnDestroyAction != null)
{
m_OnDestroyAction(this);
m_OnDestroyAction = null;
}
m_Result = default(TObject);
m_referenceCount = 1;
m_Status = AsyncOperationStatus.None;
m_Error = null;
m_Version++;
m_RM = null;
}
}
System.Threading.EventWaitHandle m_waitHandle;
internal System.Threading.WaitHandle WaitHandle
{
get
{
if (m_waitHandle == null)
m_waitHandle = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.ManualReset);
m_waitHandle.Reset();
return m_waitHandle;
}
}
internal System.Threading.Tasks.Task<TObject> Task
{
get
{
if (Status == AsyncOperationStatus.Failed)
{
return System.Threading.Tasks.Task.FromResult(default(TObject));
}
if (Status == AsyncOperationStatus.Succeeded)
{
return System.Threading.Tasks.Task.FromResult(Result);
}
var handle = WaitHandle;
return System.Threading.Tasks.Task.Factory.StartNew(o =>
{
var asyncOperation = o as AsyncOperationBase<TObject>;
if (asyncOperation == null)
return default(TObject);
handle.WaitOne();
return asyncOperation.Result;
}, this);
}
}
System.Threading.Tasks.Task<object> IAsyncOperation.Task
{
get
{
return Task as System.Threading.Tasks.Task<object>;
}
}
/// <inheritdoc />
public override string ToString()
{
var instId = "";
var or = m_Result as Object;
if (or != null)
instId = "(" + or.GetInstanceID() + ")";
return string.Format("{0}, result='{1}', status='{2}'", base.ToString(), (or + instId), m_Status);
}
bool m_InDeferredCallbackQueue;
void RegisterForDeferredCallbackEvent(bool incrementReferenceCount = true)
{
if (IsDone && !m_InDeferredCallbackQueue)
{
m_InDeferredCallbackQueue = true;
m_RM.RegisterForDeferredCallback(this, incrementReferenceCount);
}
}
internal event Action<AsyncOperationHandle<TObject>> Completed
{
add
{
if (m_CompletedActionT == null)
m_CompletedActionT = DelegateList<AsyncOperationHandle<TObject>>.CreateWithGlobalCache();
m_CompletedActionT.Add(value);
RegisterForDeferredCallbackEvent();
}
remove
{
m_CompletedActionT.Remove(value);
}
}
internal event Action<AsyncOperationHandle> Destroyed
{
add
{
if (m_DestroyedAction == null)
m_DestroyedAction = DelegateList<AsyncOperationHandle>.CreateWithGlobalCache();
m_DestroyedAction.Add(value);
}
remove
{
m_DestroyedAction.Remove(value);
}
}
internal event Action<AsyncOperationHandle> CompletedTypeless
{
add
{
if (m_CompletedAction == null)
m_CompletedAction = DelegateList<AsyncOperationHandle>.CreateWithGlobalCache();
m_CompletedAction.Add(value);
RegisterForDeferredCallbackEvent();
}
remove
{
m_CompletedAction.Remove(value);
}
}
/// <inheritdoc />
internal AsyncOperationStatus Status { get { return m_Status; } }
/// <inheritdoc />
internal Exception OperationException
{
get { return m_Error; }
private set
{
m_Error = value;
if (m_Error != null && ResourceManager.ExceptionHandler != null)
ResourceManager.ExceptionHandler(new AsyncOperationHandle(this), value);
}
}
internal bool MoveNext() { return !IsDone; }
internal void Reset() { }
internal object Current { get { return null; } } // should throw exception?
internal bool IsDone { get { return Status == AsyncOperationStatus.Failed || Status == AsyncOperationStatus.Succeeded; } }
internal float PercentComplete
{
get
{
if (m_Status == AsyncOperationStatus.None)
{
try
{
return Progress;
}
catch
{
return 0.0f;
}
}
return 1.0f;
}
}
internal void InvokeCompletionEvent()
{
if (m_CompletedAction != null)
{
m_CompletedAction.Invoke(new AsyncOperationHandle(this));
m_CompletedAction.Clear();
}
if (m_CompletedActionT != null)
{
m_CompletedActionT.Invoke(new AsyncOperationHandle<TObject>(this));
m_CompletedActionT.Clear();
}
if (m_waitHandle != null)
m_waitHandle.Set();
m_InDeferredCallbackQueue = false;
}
internal AsyncOperationHandle<TObject> Handle { get { return new AsyncOperationHandle<TObject>(this); } }
DelegateList<float> m_UpdateCallbacks;
Action<float> m_UpdateCallback;
//bool m_IsExecuting;
private void UpdateCallback(float unscaledDeltaTime)
{
IUpdateReceiver updateOp = this as IUpdateReceiver;
updateOp.Update(unscaledDeltaTime);
}
/// <summary>
/// Complete the operation and invoke events.
/// </summary>
/// <param name="result">The result object for the operation.</param>
/// <param name="success">True if successful.</param>
/// <param name="errorMsg">The error message if the operation has failed.</param>
public void Complete(TObject result, bool success, string errorMsg)
{
IUpdateReceiver upOp = this as IUpdateReceiver;
if (m_UpdateCallbacks != null && upOp != null)
m_UpdateCallbacks.Remove(m_UpdateCallback);
m_Result = result;
m_Status = success ? AsyncOperationStatus.Succeeded : AsyncOperationStatus.Failed;
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationPercentComplete, 1);
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationComplete);
if (m_Status == AsyncOperationStatus.Failed)
{
if (string.IsNullOrEmpty(errorMsg))
errorMsg = "Unknown error in AsyncOperation";
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationFail, 0, errorMsg);
OperationException = new Exception(errorMsg);
ICachable cachedOperation = this as ICachable;
if (cachedOperation != null)
m_RM.RemoveOperationFromCache(cachedOperation.Hash);
RegisterForDeferredCallbackEvent(false);
}
else
{
InvokeCompletionEvent();
DecrementReferenceCount();
}
}
internal void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks)
{
m_RM = rm;
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationCreate);
m_RM.PostDiagnosticEvent(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationPercentComplete, 0);
IncrementReferenceCount(); // keep a reference until the operation completes
m_UpdateCallbacks = updateCallbacks;
if (dependency.IsValid() && !dependency.IsDone)
dependency.Completed += m_dependencyCompleteAction;
else
InvokeExecute();
}
private void InvokeExecute()
{
// m_IsExecuting = true;
Execute();
IUpdateReceiver upOp = this as IUpdateReceiver;
if (upOp != null)
m_UpdateCallbacks.Add(m_UpdateCallback);
// m_IsExecuting = false;
}
event Action<AsyncOperationHandle> IAsyncOperation.CompletedTypeless
{
add { CompletedTypeless += value; }
remove { CompletedTypeless -= value; }
}
event Action<AsyncOperationHandle> IAsyncOperation.Destroyed
{
add
{
Destroyed += value;
}
remove
{
Destroyed -= value;
}
}
int IAsyncOperation.Version { get { return Version; } }
int IAsyncOperation.ReferenceCount { get { return ReferenceCount; } }
float IAsyncOperation.PercentComplete { get { return PercentComplete; } }
AsyncOperationStatus IAsyncOperation.Status { get { return Status; } }
Exception IAsyncOperation.OperationException { get { return OperationException; } }
bool IAsyncOperation.IsDone { get { return IsDone; } }
AsyncOperationHandle IAsyncOperation.Handle { get { return Handle; } }
Action<IAsyncOperation> IAsyncOperation.OnDestroy { set { OnDestroy = value; } }
string IAsyncOperation.DebugName { get { return DebugName; } }
object IAsyncOperation.GetResultAsObject()
{
return m_Result;
}
Type IAsyncOperation.ResultType { get { return typeof(TObject); } }
void IAsyncOperation.GetDependencies(List<AsyncOperationHandle> deps) { GetDependencies(deps); }
void IAsyncOperation.DecrementReferenceCount() { DecrementReferenceCount(); }
void IAsyncOperation.IncrementReferenceCount() { IncrementReferenceCount(); }
void IAsyncOperation.InvokeCompletionEvent() { InvokeCompletionEvent(); }
void IAsyncOperation.Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks)
{
Start(rm, dependency, updateCallbacks);
}
}
}