initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,616 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
// ReSharper disable DelegateSubtraction
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
internal interface ICachable
|
||||
{
|
||||
IOperationCacheKey Key { 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; }
|
||||
DownloadStatus GetDownloadStatus(HashSet<object> visited);
|
||||
AsyncOperationStatus Status { get; }
|
||||
|
||||
Exception OperationException { get; }
|
||||
bool IsDone { get; }
|
||||
Action<IAsyncOperation> OnDestroy { set; }
|
||||
void GetDependencies(List<AsyncOperationHandle> deps);
|
||||
bool IsRunning { get; }
|
||||
|
||||
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; }
|
||||
|
||||
void WaitForCompletion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// base class for implemented AsyncOperations, implements the needed interfaces and consolidates redundant code
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of the operation.</typeparam>
|
||||
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>
|
||||
public virtual void GetDependencies(List<AsyncOperationHandle> dependencies)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor to Result of the operation.
|
||||
/// </summary>
|
||||
public TObject Result { get; set; }
|
||||
|
||||
int m_referenceCount = 1;
|
||||
internal AsyncOperationStatus m_Status;
|
||||
internal Exception m_Error;
|
||||
internal ResourceManager m_RM;
|
||||
internal int m_Version;
|
||||
|
||||
internal int Version
|
||||
{
|
||||
get { return m_Version; }
|
||||
}
|
||||
|
||||
DelegateList<AsyncOperationHandle> m_DestroyedAction;
|
||||
DelegateList<AsyncOperationHandle<TObject>> m_CompletedActionT;
|
||||
|
||||
internal bool CompletedEventHasListeners => m_CompletedActionT != null && m_CompletedActionT.Count > 0;
|
||||
internal bool DestroyedEventHasListeners => m_DestroyedAction != null && m_DestroyedAction.Count > 0;
|
||||
|
||||
Action<IAsyncOperation> m_OnDestroyAction;
|
||||
|
||||
internal Action<IAsyncOperation> OnDestroy
|
||||
{
|
||||
set { m_OnDestroyAction = value; }
|
||||
}
|
||||
|
||||
Action<AsyncOperationHandle> m_dependencyCompleteAction;
|
||||
|
||||
/// <summary>
|
||||
/// True, If the operation has been executed, else false
|
||||
/// </summary>
|
||||
protected internal bool HasExecuted = false;
|
||||
|
||||
internal event Action Executed;
|
||||
|
||||
/// <summary>
|
||||
/// The number of references that are using this operation.
|
||||
/// When the ReferenceCount reaches 0, this operation is Destroyed.
|
||||
/// </summary>
|
||||
protected internal int ReferenceCount
|
||||
{
|
||||
get { return m_referenceCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the current op has begun but hasn't yet reached completion. False otherwise.
|
||||
/// </summary>
|
||||
public bool IsRunning { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Basic constructor for AsyncOperationBase.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously complete the async operation.
|
||||
/// </summary>
|
||||
public void WaitForCompletion()
|
||||
{
|
||||
if (PlatformUtilities.PlatformUsesMultiThreading(Application.platform))
|
||||
while (!InvokeWaitForCompletion())
|
||||
{
|
||||
}
|
||||
else
|
||||
throw new Exception($"{Application.platform} does not support synchronous Addressable loading. Please do not use WaitForCompletion on the {Application.platform} platform.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used for the implementation of WaitForCompletion in an IAsyncOperation.
|
||||
/// </summary>
|
||||
/// <returns>True if the operation has completed, otherwise false.</returns>
|
||||
protected virtual bool InvokeWaitForCompletion()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increments the reference count for this operation.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">Thrown if the operation has already been destroyed after reaching 0 reference count.</exception>
|
||||
protected 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++;
|
||||
if (m_RM != null && m_RM.postProfilerEvents)
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationReferenceCount,
|
||||
m_referenceCount));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reduces the reference count for this operation by 1. If the reference count is reduced to 0, the operation is destroyed.
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">Thrown if the operation has already been destroyed after reaching 0 reference count.</exception>
|
||||
protected 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--;
|
||||
|
||||
if (m_RM != null && m_RM.postProfilerEvents)
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationReferenceCount,
|
||||
m_referenceCount));
|
||||
|
||||
if (m_referenceCount == 0)
|
||||
{
|
||||
if (m_RM != null && m_RM.postProfilerEvents)
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationDestroy));
|
||||
|
||||
if (m_DestroyedAction != null)
|
||||
{
|
||||
m_DestroyedAction.Invoke(new AsyncOperationHandle<TObject>(this));
|
||||
m_DestroyedAction.Clear();
|
||||
}
|
||||
|
||||
Destroy();
|
||||
Result = default(TObject);
|
||||
m_referenceCount = 1;
|
||||
m_Status = AsyncOperationStatus.None;
|
||||
m_taskCompletionSource = null;
|
||||
m_taskCompletionSourceTypeless = null;
|
||||
m_Error = null;
|
||||
m_Version++;
|
||||
m_RM = null;
|
||||
|
||||
if (m_OnDestroyAction != null)
|
||||
{
|
||||
m_OnDestroyAction(this);
|
||||
m_OnDestroyAction = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TaskCompletionSource<TObject> m_taskCompletionSource;
|
||||
|
||||
internal Task<TObject> Task
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_taskCompletionSource == null)
|
||||
{
|
||||
m_taskCompletionSource = new TaskCompletionSource<TObject>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
if (IsDone && !CompletedEventHasListeners)
|
||||
m_taskCompletionSource.SetResult(Result);
|
||||
}
|
||||
|
||||
return m_taskCompletionSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
TaskCompletionSource<object> m_taskCompletionSourceTypeless;
|
||||
|
||||
Task<object> IAsyncOperation.Task
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_taskCompletionSourceTypeless == null)
|
||||
{
|
||||
m_taskCompletionSourceTypeless = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
if (IsDone && !CompletedEventHasListeners)
|
||||
m_taskCompletionSourceTypeless.SetResult(Result);
|
||||
}
|
||||
|
||||
return m_taskCompletionSourceTypeless.Task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts the information about the operation to a formatted string.
|
||||
/// </summary>
|
||||
/// <returns>Returns the information about the operation.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var instId = "";
|
||||
var or = 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 { Completed += s => value(s); }
|
||||
remove { Completed -= s => value(s); }
|
||||
}
|
||||
|
||||
/// <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_CompletedActionT != null)
|
||||
{
|
||||
m_CompletedActionT.Invoke(new AsyncOperationHandle<TObject>(this));
|
||||
m_CompletedActionT.Clear();
|
||||
}
|
||||
|
||||
if (m_taskCompletionSource != null)
|
||||
m_taskCompletionSource.TrySetResult(Result);
|
||||
|
||||
if (m_taskCompletionSourceTypeless != null)
|
||||
m_taskCompletionSourceTypeless.TrySetResult(Result);
|
||||
|
||||
m_InDeferredCallbackQueue = false;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle<TObject> Handle
|
||||
{
|
||||
get { return new AsyncOperationHandle<TObject>(this); }
|
||||
}
|
||||
|
||||
DelegateList<float> m_UpdateCallbacks;
|
||||
Action<float> m_UpdateCallback;
|
||||
|
||||
private void UpdateCallback(float unscaledDeltaTime)
|
||||
{
|
||||
IUpdateReceiver updateOp = this as IUpdateReceiver;
|
||||
updateOp.Update(unscaledDeltaTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete the operation and invoke events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An operation is considered to have failed silently if success is true and if errorMsg isn't null or empty.
|
||||
/// The exception handler will be called in cases of silent failures.
|
||||
/// Any failed operations will call Release on any dependencies that succeeded.
|
||||
/// </remarks>
|
||||
/// <param name="result">The result object for the operation.</param>
|
||||
/// <param name="success">True if successful or if the operation failed silently.</param>
|
||||
/// <param name="errorMsg">The error message if the operation has failed.</param>
|
||||
public void Complete(TObject result, bool success, string errorMsg)
|
||||
{
|
||||
Complete(result, success, errorMsg, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete the operation and invoke events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An operation is considered to have failed silently if success is true and if errorMsg isn't null or empty.
|
||||
/// The exception handler will be called in cases of silent failures.
|
||||
/// </remarks>
|
||||
/// <param name="result">The result object for the operation.</param>
|
||||
/// <param name="success">True if successful or if the operation failed silently.</param>
|
||||
/// <param name="errorMsg">The error message if the operation has failed.</param>
|
||||
/// <param name="releaseDependenciesOnFailure">When true, failed operations will release any dependencies that succeeded.</param>
|
||||
public void Complete(TObject result, bool success, string errorMsg, bool releaseDependenciesOnFailure)
|
||||
{
|
||||
Complete(result, success, !string.IsNullOrEmpty(errorMsg) ? new OperationException(errorMsg) : null, releaseDependenciesOnFailure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete the operation and invoke events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// An operation is considered to have failed silently if success is true and if exception isn't null.
|
||||
/// The exception handler will be called in cases of silent failures.
|
||||
/// </remarks>
|
||||
/// <param name="result">The result object for the operation.</param>
|
||||
/// <param name="success">True if successful or if the operation failed silently.</param>
|
||||
/// <param name="exception">The exception if the operation has failed.</param>
|
||||
/// <param name="releaseDependenciesOnFailure">When true, failed operations will release any dependencies that succeeded.</param>
|
||||
public void Complete(TObject result, bool success, Exception exception, bool releaseDependenciesOnFailure = true)
|
||||
{
|
||||
if (IsDone)
|
||||
return;
|
||||
|
||||
IUpdateReceiver upOp = this as IUpdateReceiver;
|
||||
if (m_UpdateCallbacks != null && upOp != null)
|
||||
m_UpdateCallbacks.Remove(m_UpdateCallback);
|
||||
|
||||
Result = result;
|
||||
m_Status = success ? AsyncOperationStatus.Succeeded : AsyncOperationStatus.Failed;
|
||||
|
||||
if (m_RM != null && m_RM.postProfilerEvents)
|
||||
{
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationPercentComplete, 1));
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationComplete));
|
||||
}
|
||||
|
||||
if (m_Status == AsyncOperationStatus.Failed || exception != null)
|
||||
{
|
||||
if (exception == null || string.IsNullOrEmpty(exception.Message))
|
||||
OperationException = new OperationException($"Unknown error in AsyncOperation : {DebugName}");
|
||||
else
|
||||
OperationException = exception;
|
||||
}
|
||||
|
||||
if (m_Status == AsyncOperationStatus.Failed)
|
||||
{
|
||||
if (releaseDependenciesOnFailure)
|
||||
ReleaseDependencies();
|
||||
|
||||
if (m_RM != null && m_RM.postProfilerEvents)
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationFail, 0,
|
||||
exception?.ToString()));
|
||||
|
||||
ICachable cachedOperation = this as ICachable;
|
||||
if (cachedOperation?.Key != null)
|
||||
m_RM?.RemoveOperationFromCache(cachedOperation.Key);
|
||||
|
||||
RegisterForDeferredCallbackEvent(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
InvokeCompletionEvent();
|
||||
DecrementReferenceCount();
|
||||
}
|
||||
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
internal void Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks)
|
||||
{
|
||||
m_RM = rm;
|
||||
IsRunning = true;
|
||||
HasExecuted = false;
|
||||
if (m_RM != null && m_RM.postProfilerEvents)
|
||||
{
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(new AsyncOperationHandle(this), ResourceManager.DiagnosticEventType.AsyncOperationCreate));
|
||||
m_RM.PostDiagnosticEvent(new ResourceManager.DiagnosticEventContext(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();
|
||||
}
|
||||
|
||||
internal void InvokeExecute()
|
||||
{
|
||||
Execute();
|
||||
HasExecuted = true;
|
||||
IUpdateReceiver upOp = this as IUpdateReceiver;
|
||||
if (upOp != null && !IsDone)
|
||||
m_UpdateCallbacks.Add(m_UpdateCallback);
|
||||
Executed?.Invoke();
|
||||
}
|
||||
|
||||
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 => Version;
|
||||
|
||||
int IAsyncOperation.ReferenceCount => ReferenceCount;
|
||||
|
||||
float IAsyncOperation.PercentComplete => PercentComplete;
|
||||
|
||||
AsyncOperationStatus IAsyncOperation.Status => Status;
|
||||
|
||||
Exception IAsyncOperation.OperationException => OperationException;
|
||||
|
||||
bool IAsyncOperation.IsDone => IsDone;
|
||||
|
||||
AsyncOperationHandle IAsyncOperation.Handle => Handle;
|
||||
|
||||
Action<IAsyncOperation> IAsyncOperation.OnDestroy
|
||||
{
|
||||
set { OnDestroy = value; }
|
||||
}
|
||||
|
||||
string IAsyncOperation.DebugName => DebugName;
|
||||
|
||||
/// <inheritdoc/>
|
||||
object IAsyncOperation.GetResultAsObject() => Result;
|
||||
|
||||
Type IAsyncOperation.ResultType
|
||||
{
|
||||
get { return typeof(TObject); }
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IAsyncOperation.GetDependencies(List<AsyncOperationHandle> deps) => GetDependencies(deps);
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IAsyncOperation.DecrementReferenceCount() => DecrementReferenceCount();
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IAsyncOperation.IncrementReferenceCount() => IncrementReferenceCount();
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IAsyncOperation.InvokeCompletionEvent() => InvokeCompletionEvent();
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IAsyncOperation.Start(ResourceManager rm, AsyncOperationHandle dependency, DelegateList<float> updateCallbacks) => Start(rm, dependency, updateCallbacks);
|
||||
|
||||
internal virtual void ReleaseDependencies()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
DownloadStatus IAsyncOperation.GetDownloadStatus(HashSet<object> visited) => GetDownloadStatus(visited);
|
||||
|
||||
internal virtual DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
visited.Add(this);
|
||||
return new DownloadStatus() {IsDone = IsDone};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f9fa130dc1a674f38a215c4f52e2b420
|
||||
timeCreated: 1503083765
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,609 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Handle for internal operations. This allows for reference counting and checking for valid references.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The object type of the underlying operation.</typeparam>
|
||||
public struct AsyncOperationHandle<TObject> : IEnumerator, IEquatable<AsyncOperationHandle<TObject>>
|
||||
{
|
||||
internal AsyncOperationBase<TObject> m_InternalOp;
|
||||
int m_Version;
|
||||
string m_LocationName;
|
||||
|
||||
internal string LocationName
|
||||
{
|
||||
get { return m_LocationName; }
|
||||
set { m_LocationName = value; }
|
||||
}
|
||||
|
||||
bool m_UnloadSceneOpExcludeReleaseCallback;
|
||||
|
||||
internal bool UnloadSceneOpExcludeReleaseCallback
|
||||
{
|
||||
get { return m_UnloadSceneOpExcludeReleaseCallback; }
|
||||
set { m_UnloadSceneOpExcludeReleaseCallback = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conversion from typed to non typed handles. This does not increment the reference count.
|
||||
/// To convert from non-typed back, use AsyncOperationHandle.Convert<T>()
|
||||
/// </summary>
|
||||
/// <param name="obj">The typed handle to convert.</param>
|
||||
/// <returns>Returns the converted operation handle.</returns>
|
||||
static public implicit operator AsyncOperationHandle(AsyncOperationHandle<TObject> obj)
|
||||
{
|
||||
return new AsyncOperationHandle(obj.m_InternalOp, obj.m_Version, obj.m_LocationName);
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(AsyncOperationBase<TObject> op)
|
||||
{
|
||||
m_InternalOp = op;
|
||||
m_Version = op?.Version ?? 0;
|
||||
m_LocationName = null;
|
||||
m_UnloadSceneOpExcludeReleaseCallback = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the current download status for this operation and its dependencies.
|
||||
/// </summary>
|
||||
/// <returns>The download status.</returns>
|
||||
public DownloadStatus GetDownloadStatus()
|
||||
{
|
||||
return InternalGetDownloadStatus(new HashSet<object>());
|
||||
}
|
||||
|
||||
internal DownloadStatus InternalGetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
if (visited == null)
|
||||
visited = new HashSet<object>();
|
||||
return visited.Add(InternalOp) ? InternalOp.GetDownloadStatus(visited) : new DownloadStatus() {IsDone = IsDone};
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op)
|
||||
{
|
||||
m_InternalOp = (AsyncOperationBase<TObject>)op;
|
||||
m_Version = op?.Version ?? 0;
|
||||
m_LocationName = null;
|
||||
m_UnloadSceneOpExcludeReleaseCallback = false;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op, int version)
|
||||
{
|
||||
m_InternalOp = (AsyncOperationBase<TObject>)op;
|
||||
m_Version = version;
|
||||
m_LocationName = null;
|
||||
m_UnloadSceneOpExcludeReleaseCallback = false;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op, string locationName)
|
||||
{
|
||||
m_InternalOp = (AsyncOperationBase<TObject>)op;
|
||||
m_Version = op?.Version ?? 0;
|
||||
m_LocationName = locationName;
|
||||
m_UnloadSceneOpExcludeReleaseCallback = false;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName)
|
||||
{
|
||||
m_InternalOp = (AsyncOperationBase<TObject>)op;
|
||||
m_Version = version;
|
||||
m_LocationName = locationName;
|
||||
m_UnloadSceneOpExcludeReleaseCallback = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released.
|
||||
/// </summary>
|
||||
/// <returns>A new handle to the operation. This handle must also be released.</returns>
|
||||
internal AsyncOperationHandle<TObject> Acquire()
|
||||
{
|
||||
InternalOp.IncrementReferenceCount();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
|
||||
/// </summary>
|
||||
public event Action<AsyncOperationHandle<TObject>> Completed
|
||||
{
|
||||
add { InternalOp.Completed += value; }
|
||||
remove { InternalOp.Completed -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completion event for non-typed callback handlers. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
|
||||
/// </summary>
|
||||
public event Action<AsyncOperationHandle> CompletedTypeless
|
||||
{
|
||||
add { InternalOp.CompletedTypeless += value; }
|
||||
remove { InternalOp.CompletedTypeless -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Debug name of the operation.
|
||||
/// </summary>
|
||||
public string DebugName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid())
|
||||
return "InvalidHandle";
|
||||
return ((IAsyncOperation)InternalOp).DebugName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get dependency operations.
|
||||
/// </summary>
|
||||
/// <param name="deps">The list of AsyncOperationHandles that are dependencies of a given AsyncOperationHandle</param>
|
||||
public void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
InternalOp.GetDependencies(deps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for handling the destruction of the operation.
|
||||
/// </summary>
|
||||
public event Action<AsyncOperationHandle> Destroyed
|
||||
{
|
||||
add { InternalOp.Destroyed += value; }
|
||||
remove { InternalOp.Destroyed -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide equality for this struct.
|
||||
/// </summary>
|
||||
/// <param name="other">The operation to compare to.</param>
|
||||
/// <returns>True if the the operation handles reference the same AsyncOperation and the version is the same.</returns>
|
||||
public bool Equals(AsyncOperationHandle<TObject> other)
|
||||
{
|
||||
return m_Version == other.m_Version && m_InternalOp == other.m_InternalOp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hash code of this struct.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_InternalOp == null ? 0 : m_InternalOp.GetHashCode() * 17 + m_Version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously complete the async operation.
|
||||
/// </summary>
|
||||
/// <returns>The result of the operation or null.</returns>
|
||||
public TObject WaitForCompletion()
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
AsyncOperationHandle.IsWaitingForCompletion = true;
|
||||
try
|
||||
{
|
||||
if (IsValid() && !InternalOp.IsDone)
|
||||
InternalOp.WaitForCompletion();
|
||||
if (IsValid())
|
||||
return Result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
AsyncOperationHandle.IsWaitingForCompletion = false;
|
||||
m_InternalOp?.m_RM?.Update(Time.unscaledDeltaTime);
|
||||
}
|
||||
#else
|
||||
if (IsValid() && !InternalOp.IsDone)
|
||||
InternalOp.WaitForCompletion();
|
||||
|
||||
m_InternalOp?.m_RM?.Update(Time.unscaledDeltaTime);
|
||||
if (IsValid())
|
||||
return Result;
|
||||
#endif
|
||||
return default(TObject);
|
||||
}
|
||||
|
||||
internal AsyncOperationBase<TObject> InternalOp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_InternalOp == null || m_InternalOp.Version != m_Version)
|
||||
throw new Exception("Attempting to use an invalid operation handle");
|
||||
return m_InternalOp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the operation is complete.
|
||||
/// </summary>
|
||||
public bool IsDone
|
||||
{
|
||||
get { return !IsValid() || InternalOp.IsDone; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the handle references an internal operation.
|
||||
/// </summary>
|
||||
/// <returns>True if valid.</returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
return m_InternalOp != null && m_InternalOp.Version == m_Version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception for a failed operation. This will be null unless Status is failed.
|
||||
/// </summary>
|
||||
public Exception OperationException
|
||||
{
|
||||
get { return InternalOp.OperationException; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The progress of the internal operation.
|
||||
/// This is evenly weighted between all sub-operations. For example, a LoadAssetAsync call could potentially
|
||||
/// be chained with InitializeAsync and have multiple dependent operations that download and load content.
|
||||
/// In that scenario, PercentComplete would reflect how far the overal operation was, and would not accurately
|
||||
/// represent just percent downloaded or percent loaded into memory.
|
||||
/// For accurate download percentages, use GetDownloadStatus().
|
||||
/// </summary>
|
||||
public float PercentComplete
|
||||
{
|
||||
get { return InternalOp.PercentComplete; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current reference count of the internal operation.
|
||||
/// </summary>
|
||||
internal int ReferenceCount
|
||||
{
|
||||
get { return InternalOp.ReferenceCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the handle. If the internal operation reference count reaches 0, the resource will be released.
|
||||
/// </summary>
|
||||
internal void Release()
|
||||
{
|
||||
InternalOp.DecrementReferenceCount();
|
||||
m_InternalOp = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The result object of the operations.
|
||||
/// </summary>
|
||||
public TObject Result
|
||||
{
|
||||
get { return InternalOp.Result; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The status of the internal operation.
|
||||
/// </summary>
|
||||
public AsyncOperationStatus Status
|
||||
{
|
||||
get { return InternalOp.Status; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a Task object to wait on when using async await.
|
||||
/// </summary>
|
||||
public System.Threading.Tasks.Task<TObject> Task
|
||||
{
|
||||
get { return InternalOp.Task; }
|
||||
}
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return Result; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overload for <see cref="IEnumerator.MoveNext"/>.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the enumerator can advance to the next element in the collectin. Returns false otherwise.</returns>
|
||||
bool IEnumerator.MoveNext()
|
||||
{
|
||||
return !IsDone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overload for <see cref="IEnumerator.Reset"/>.
|
||||
/// </summary>
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non typed operation handle. This allows for reference counting and checking for valid references.
|
||||
/// </summary>
|
||||
public struct AsyncOperationHandle : IEnumerator
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
private static bool m_IsWaitingForCompletion = false;
|
||||
/// <summary>
|
||||
/// Indicates that the async operation is in the process of being completed synchronously.
|
||||
/// </summary>
|
||||
public static bool IsWaitingForCompletion
|
||||
{
|
||||
get { return m_IsWaitingForCompletion; }
|
||||
internal set { m_IsWaitingForCompletion = value; }
|
||||
}
|
||||
#endif
|
||||
|
||||
internal IAsyncOperation m_InternalOp;
|
||||
int m_Version;
|
||||
string m_LocationName;
|
||||
|
||||
internal string LocationName
|
||||
{
|
||||
get { return m_LocationName; }
|
||||
set { m_LocationName = value; }
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op)
|
||||
{
|
||||
m_InternalOp = op;
|
||||
m_Version = op?.Version ?? 0;
|
||||
m_LocationName = null;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op, int version)
|
||||
{
|
||||
m_InternalOp = op;
|
||||
m_Version = version;
|
||||
m_LocationName = null;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op, string locationName)
|
||||
{
|
||||
m_InternalOp = op;
|
||||
m_Version = op?.Version ?? 0;
|
||||
m_LocationName = locationName;
|
||||
}
|
||||
|
||||
internal AsyncOperationHandle(IAsyncOperation op, int version, string locationName)
|
||||
{
|
||||
m_InternalOp = op;
|
||||
m_Version = version;
|
||||
m_LocationName = locationName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquire a new handle to the internal operation. This will increment the reference count, therefore the returned handle must also be released.
|
||||
/// </summary>
|
||||
/// <returns>A new handle to the operation. This handle must also be released.</returns>
|
||||
internal AsyncOperationHandle Acquire()
|
||||
{
|
||||
InternalOp.IncrementReferenceCount();
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completion event for the internal operation. If this is assigned on a completed operation, the callback is deferred until the LateUpdate of the current frame.
|
||||
/// </summary>
|
||||
public event Action<AsyncOperationHandle> Completed
|
||||
{
|
||||
add { InternalOp.CompletedTypeless += value; }
|
||||
remove { InternalOp.CompletedTypeless -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts handle to be typed. This does not increment the reference count.
|
||||
/// To convert back to non-typed, implicit conversion is available.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the handle.</typeparam>
|
||||
/// <returns>A new handle that is typed.</returns>
|
||||
public AsyncOperationHandle<T> Convert<T>()
|
||||
{
|
||||
return new AsyncOperationHandle<T>(InternalOp, m_Version, m_LocationName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide equality for this struct.
|
||||
/// </summary>
|
||||
/// <param name="other">The operation to compare to.</param>
|
||||
/// <returns>True if the the operation handles reference the same AsyncOperation and the version is the same.</returns>
|
||||
public bool Equals(AsyncOperationHandle other)
|
||||
{
|
||||
return m_Version == other.m_Version && m_InternalOp == other.m_InternalOp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Debug name of the operation.
|
||||
/// </summary>
|
||||
public string DebugName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!IsValid())
|
||||
return "InvalidHandle";
|
||||
return InternalOp.DebugName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for handling the destruction of the operation.
|
||||
/// </summary>
|
||||
public event Action<AsyncOperationHandle> Destroyed
|
||||
{
|
||||
add { InternalOp.Destroyed += value; }
|
||||
remove { InternalOp.Destroyed -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get dependency operations.
|
||||
/// </summary>
|
||||
/// <param name="deps"></param>
|
||||
public void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
InternalOp.GetDependencies(deps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hash code of this struct.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return m_InternalOp == null ? 0 : m_InternalOp.GetHashCode() * 17 + m_Version;
|
||||
}
|
||||
|
||||
IAsyncOperation InternalOp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_InternalOp == null || m_InternalOp.Version != m_Version)
|
||||
throw new Exception("Attempting to use an invalid operation handle");
|
||||
return m_InternalOp;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the operation is complete.
|
||||
/// </summary>
|
||||
public bool IsDone
|
||||
{
|
||||
get { return !IsValid() || InternalOp.IsDone; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the internal operation is not null and has the same version of this handle.
|
||||
/// </summary>
|
||||
/// <returns>True if valid.</returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
return m_InternalOp != null && m_InternalOp.Version == m_Version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception for a failed operation. This will be null unless Status is failed.
|
||||
/// </summary>
|
||||
public Exception OperationException
|
||||
{
|
||||
get { return InternalOp.OperationException; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The progress of the internal operation.
|
||||
/// This is evenly weighted between all sub-operations. For example, a LoadAssetAsync call could potentially
|
||||
/// be chained with InitializeAsync and have multiple dependent operations that download and load content.
|
||||
/// In that scenario, PercentComplete would reflect how far the overal operation was, and would not accurately
|
||||
/// represent just percent downloaded or percent loaded into memory.
|
||||
/// For accurate download percentages, use GetDownloadStatus().
|
||||
/// </summary>
|
||||
public float PercentComplete
|
||||
{
|
||||
get { return InternalOp.PercentComplete; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the current download status for this operation and its dependencies. In some instances, the information will not be available. This can happen if the operation
|
||||
/// is dependent on the initialization operation for addressables. Once the initialization operation completes, the information returned will be accurate.
|
||||
/// </summary>
|
||||
/// <returns>The download status.</returns>
|
||||
public DownloadStatus GetDownloadStatus()
|
||||
{
|
||||
return InternalGetDownloadStatus(new HashSet<object>());
|
||||
}
|
||||
|
||||
internal DownloadStatus InternalGetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
if (visited == null)
|
||||
visited = new HashSet<object>();
|
||||
return visited.Add(InternalOp) ? InternalOp.GetDownloadStatus(visited) : new DownloadStatus() {IsDone = IsDone};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current reference count of the internal operation.
|
||||
/// </summary>
|
||||
internal int ReferenceCount
|
||||
{
|
||||
get { return InternalOp.ReferenceCount; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release the handle. If the internal operation reference count reaches 0, the resource will be released.
|
||||
/// </summary>
|
||||
internal void Release()
|
||||
{
|
||||
InternalOp.DecrementReferenceCount();
|
||||
m_InternalOp = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The result object of the operations.
|
||||
/// </summary>
|
||||
public object Result
|
||||
{
|
||||
get { return InternalOp.GetResultAsObject(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The status of the internal operation.
|
||||
/// </summary>
|
||||
public AsyncOperationStatus Status
|
||||
{
|
||||
get { return InternalOp.Status; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a Task object to wait on when using async await.
|
||||
/// </summary>
|
||||
public System.Threading.Tasks.Task<object> Task
|
||||
{
|
||||
get { return InternalOp.Task; }
|
||||
}
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get { return Result; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overload for <see cref="IEnumerator.MoveNext"/>.
|
||||
/// </summary>
|
||||
/// <returns>Returns true if the enumerator can advance to the next element in the collectin. Returns false otherwise.</returns>
|
||||
bool IEnumerator.MoveNext()
|
||||
{
|
||||
return !IsDone;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overload for <see cref="IEnumerator.Reset"/>.
|
||||
/// </summary>
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously complete the async operation.
|
||||
/// </summary>
|
||||
/// <returns>The result of the operation or null.</returns>
|
||||
public object WaitForCompletion()
|
||||
{
|
||||
#if !UNITY_2021_1_OR_NEWER
|
||||
IsWaitingForCompletion = true;
|
||||
try
|
||||
{
|
||||
if (IsValid() && !InternalOp.IsDone)
|
||||
InternalOp.WaitForCompletion();
|
||||
if (IsValid())
|
||||
return Result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsWaitingForCompletion = false;
|
||||
}
|
||||
#else
|
||||
if (IsValid() && !InternalOp.IsDone)
|
||||
InternalOp.WaitForCompletion();
|
||||
if (IsValid())
|
||||
return Result;
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c3b0971a8e5dab44a959bfe453348846
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for IAsyncOperations status values
|
||||
/// </summary>
|
||||
public enum AsyncOperationStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Use to indicate that the operation is still in progress.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Use to indicate that the operation succeeded.
|
||||
/// </summary>
|
||||
Succeeded,
|
||||
|
||||
/// <summary>
|
||||
/// Use to indicate that the operation failed.
|
||||
/// </summary>
|
||||
Failed
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f2b227c56ee439f46b08efa638b27a84
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,258 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement
|
||||
{
|
||||
class ChainOperation<TObject, TObjectDependency> : AsyncOperationBase<TObject>
|
||||
{
|
||||
AsyncOperationHandle<TObjectDependency> m_DepOp;
|
||||
AsyncOperationHandle<TObject> m_WrappedOp;
|
||||
DownloadStatus m_depStatus = default;
|
||||
DownloadStatus m_wrapStatus = default;
|
||||
Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> m_Callback;
|
||||
Action<AsyncOperationHandle<TObject>> m_CachedOnWrappedCompleted;
|
||||
bool m_ReleaseDependenciesOnFailure = true;
|
||||
|
||||
public ChainOperation()
|
||||
{
|
||||
m_CachedOnWrappedCompleted = OnWrappedCompleted;
|
||||
}
|
||||
|
||||
protected override string DebugName
|
||||
{
|
||||
get { return $"ChainOperation<{typeof(TObject).Name},{typeof(TObjectDependency).Name}> - {m_DepOp.DebugName}"; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
deps.Add(m_DepOp);
|
||||
}
|
||||
|
||||
public void Init(AsyncOperationHandle<TObjectDependency> dependentOp, Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> callback, bool releaseDependenciesOnFailure)
|
||||
{
|
||||
m_DepOp = dependentOp;
|
||||
m_DepOp.Acquire();
|
||||
m_Callback = callback;
|
||||
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
|
||||
RefreshDownloadStatus();
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
if (IsDone)
|
||||
return true;
|
||||
|
||||
if (!m_DepOp.IsDone)
|
||||
m_DepOp.WaitForCompletion();
|
||||
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
|
||||
if (!m_WrappedOp.IsValid())
|
||||
return m_WrappedOp.IsDone;
|
||||
m_WrappedOp.WaitForCompletion();
|
||||
return m_WrappedOp.IsDone;
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
m_WrappedOp = m_Callback(m_DepOp);
|
||||
m_WrappedOp.Completed += m_CachedOnWrappedCompleted;
|
||||
m_Callback = null;
|
||||
}
|
||||
|
||||
private void OnWrappedCompleted(AsyncOperationHandle<TObject> x)
|
||||
{
|
||||
OperationException ex = null;
|
||||
if (x.Status == AsyncOperationStatus.Failed)
|
||||
ex = new OperationException($"ChainOperation failed because dependent operation failed", x.OperationException);
|
||||
Complete(m_WrappedOp.Result, x.Status == AsyncOperationStatus.Succeeded, ex, m_ReleaseDependenciesOnFailure);
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
if (m_WrappedOp.IsValid())
|
||||
m_WrappedOp.Release();
|
||||
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
}
|
||||
|
||||
internal override void ReleaseDependencies()
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
}
|
||||
|
||||
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
RefreshDownloadStatus(visited);
|
||||
return new DownloadStatus() {DownloadedBytes = m_depStatus.DownloadedBytes + m_wrapStatus.DownloadedBytes, TotalBytes = m_depStatus.TotalBytes + m_wrapStatus.TotalBytes, IsDone = IsDone};
|
||||
}
|
||||
|
||||
void RefreshDownloadStatus(HashSet<object> visited = default)
|
||||
{
|
||||
m_depStatus = m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : m_depStatus;
|
||||
m_wrapStatus = m_WrappedOp.IsValid() ? m_WrappedOp.InternalGetDownloadStatus(visited) : m_wrapStatus;
|
||||
}
|
||||
|
||||
protected override float Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
DownloadStatus downloadStatus = GetDownloadStatus(new HashSet<object>());
|
||||
if (!downloadStatus.IsDone && downloadStatus.DownloadedBytes == 0)
|
||||
return 0.0f;
|
||||
|
||||
float total = 0f;
|
||||
int numberOfOps = 2;
|
||||
|
||||
if (m_DepOp.IsValid())
|
||||
total += m_DepOp.PercentComplete;
|
||||
else
|
||||
total++;
|
||||
|
||||
if (m_WrappedOp.IsValid())
|
||||
total += m_WrappedOp.PercentComplete;
|
||||
else
|
||||
total++;
|
||||
|
||||
return total / numberOfOps;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChainOperationTypelessDepedency<TObject> : AsyncOperationBase<TObject>
|
||||
{
|
||||
AsyncOperationHandle m_DepOp;
|
||||
AsyncOperationHandle<TObject> m_WrappedOp;
|
||||
DownloadStatus m_depStatus = default;
|
||||
DownloadStatus m_wrapStatus = default;
|
||||
Func<AsyncOperationHandle, AsyncOperationHandle<TObject>> m_Callback;
|
||||
Action<AsyncOperationHandle<TObject>> m_CachedOnWrappedCompleted;
|
||||
bool m_ReleaseDependenciesOnFailure = true;
|
||||
|
||||
internal AsyncOperationHandle<TObject> WrappedOp => m_WrappedOp;
|
||||
|
||||
public ChainOperationTypelessDepedency()
|
||||
{
|
||||
m_CachedOnWrappedCompleted = OnWrappedCompleted;
|
||||
}
|
||||
|
||||
protected override string DebugName
|
||||
{
|
||||
get { return $"ChainOperation<{typeof(TObject).Name}> - {m_DepOp.DebugName}"; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
deps.Add(m_DepOp);
|
||||
}
|
||||
|
||||
public void Init(AsyncOperationHandle dependentOp, Func<AsyncOperationHandle, AsyncOperationHandle<TObject>> callback, bool releaseDependenciesOnFailure)
|
||||
{
|
||||
m_DepOp = dependentOp;
|
||||
m_DepOp.Acquire();
|
||||
m_Callback = callback;
|
||||
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
|
||||
RefreshDownloadStatus();
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
if (IsDone)
|
||||
return true;
|
||||
|
||||
if (!m_DepOp.IsDone)
|
||||
m_DepOp.WaitForCompletion();
|
||||
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
|
||||
if (!m_WrappedOp.IsValid())
|
||||
return m_WrappedOp.IsDone;
|
||||
Result = m_WrappedOp.WaitForCompletion();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
m_WrappedOp = m_Callback(m_DepOp);
|
||||
m_WrappedOp.Completed += m_CachedOnWrappedCompleted;
|
||||
m_Callback = null;
|
||||
}
|
||||
|
||||
private void OnWrappedCompleted(AsyncOperationHandle<TObject> x)
|
||||
{
|
||||
OperationException ex = null;
|
||||
if (x.Status == AsyncOperationStatus.Failed)
|
||||
ex = new OperationException($"ChainOperation failed because dependent operation failed", x.OperationException);
|
||||
Complete(m_WrappedOp.Result, x.Status == AsyncOperationStatus.Succeeded, ex, m_ReleaseDependenciesOnFailure);
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
if (m_WrappedOp.IsValid())
|
||||
m_WrappedOp.Release();
|
||||
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
}
|
||||
|
||||
internal override void ReleaseDependencies()
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
}
|
||||
|
||||
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
RefreshDownloadStatus(visited);
|
||||
return new DownloadStatus() {DownloadedBytes = m_depStatus.DownloadedBytes + m_wrapStatus.DownloadedBytes, TotalBytes = m_depStatus.TotalBytes + m_wrapStatus.TotalBytes, IsDone = IsDone};
|
||||
}
|
||||
|
||||
void RefreshDownloadStatus(HashSet<object> visited = default)
|
||||
{
|
||||
m_depStatus = m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : m_depStatus;
|
||||
m_wrapStatus = m_WrappedOp.IsValid() ? m_WrappedOp.InternalGetDownloadStatus(visited) : m_wrapStatus;
|
||||
}
|
||||
|
||||
protected override float Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
DownloadStatus downloadStatus = GetDownloadStatus(new HashSet<object>());
|
||||
if (!downloadStatus.IsDone && downloadStatus.DownloadedBytes == 0)
|
||||
return 0.0f;
|
||||
|
||||
float total = 0f;
|
||||
int numberOfOps = 2;
|
||||
|
||||
if (m_DepOp.IsValid())
|
||||
total += m_DepOp.PercentComplete;
|
||||
else
|
||||
total++;
|
||||
|
||||
if (m_WrappedOp.IsValid())
|
||||
total += m_WrappedOp.PercentComplete;
|
||||
else
|
||||
total++;
|
||||
|
||||
return total / numberOfOps;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4d148220e8d54054e851e5c220feea5f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains download information for async operations.
|
||||
/// </summary>
|
||||
public struct DownloadStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of bytes downloaded by the operation and all of its dependencies.
|
||||
/// </summary>
|
||||
public long TotalBytes;
|
||||
|
||||
/// <summary>
|
||||
/// The total number of bytes needed to download by the operation and dependencies.
|
||||
/// </summary>
|
||||
public long DownloadedBytes;
|
||||
|
||||
/// <summary>
|
||||
/// Is the operation completed. This is used to determine if the computed Percent should be 0 or 1 when TotalBytes is 0.
|
||||
/// </summary>
|
||||
public bool IsDone;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the computed percent complete as a float value between 0 & 1. If TotalBytes == 0, 1 is returned.
|
||||
/// </summary>
|
||||
public float Percent => (TotalBytes > 0) ? ((float)DownloadedBytes / (float)TotalBytes) : (IsDone ? 1.0f : 0f);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: faf32c22a012a554c8e6e71e64844c23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,231 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
class GroupOperation : AsyncOperationBase<IList<AsyncOperationHandle>>, ICachable
|
||||
{
|
||||
[Flags]
|
||||
public enum GroupOperationSettings
|
||||
{
|
||||
None = 0,
|
||||
ReleaseDependenciesOnFailure = 1,
|
||||
AllowFailedDependencies = 2
|
||||
}
|
||||
|
||||
Action<AsyncOperationHandle> m_InternalOnComplete;
|
||||
int m_LoadedCount;
|
||||
GroupOperationSettings m_Settings;
|
||||
string debugName = null;
|
||||
private const int k_MaxDisplayedLocationLength = 45;
|
||||
|
||||
public GroupOperation()
|
||||
{
|
||||
m_InternalOnComplete = OnOperationCompleted;
|
||||
Result = new List<AsyncOperationHandle>();
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
//If Result is null then we've auto released and need to return
|
||||
if (IsDone || Result == null)
|
||||
return true;
|
||||
|
||||
foreach (var r in Result)
|
||||
{
|
||||
r.WaitForCompletion();
|
||||
if (Result == null)
|
||||
return true;
|
||||
}
|
||||
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
if (!IsDone && Result != null)
|
||||
Execute();
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
return IsDone;
|
||||
}
|
||||
|
||||
IOperationCacheKey ICachable.Key { get; set; }
|
||||
|
||||
internal IList<AsyncOperationHandle> GetDependentOps()
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
deps.AddRange(Result);
|
||||
}
|
||||
|
||||
internal override void ReleaseDependencies()
|
||||
{
|
||||
for (int i = 0; i < Result.Count; i++)
|
||||
if (Result[i].IsValid())
|
||||
Result[i].Release();
|
||||
Result.Clear();
|
||||
}
|
||||
|
||||
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
var status = new DownloadStatus() {IsDone = IsDone};
|
||||
for (int i = 0; i < Result.Count; i++)
|
||||
{
|
||||
if (Result[i].IsValid())
|
||||
{
|
||||
var depStatus = Result[i].InternalGetDownloadStatus(visited);
|
||||
status.DownloadedBytes += depStatus.DownloadedBytes;
|
||||
status.TotalBytes += depStatus.TotalBytes;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
HashSet<string> m_CachedDependencyLocations = new HashSet<string>();
|
||||
|
||||
private bool DependenciesAreUnchanged(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
if (m_CachedDependencyLocations.Count != deps.Count) return false;
|
||||
foreach (var d in deps)
|
||||
if (!m_CachedDependencyLocations.Contains(d.LocationName))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override string DebugName
|
||||
{
|
||||
get
|
||||
{
|
||||
List<AsyncOperationHandle> deps = new List<AsyncOperationHandle>();
|
||||
GetDependencies(deps);
|
||||
|
||||
if (deps.Count == 0)
|
||||
return "Dependencies";
|
||||
|
||||
//Only recalculate DebugName if a name hasn't been generated for currently held dependencies
|
||||
if (debugName != null && DependenciesAreUnchanged(deps))
|
||||
return debugName;
|
||||
|
||||
m_CachedDependencyLocations.Clear();
|
||||
|
||||
string toBeDisplayed = "Dependencies [";
|
||||
for (var i = 0; i < deps.Count; i++)
|
||||
{
|
||||
var d = deps[i];
|
||||
|
||||
var locationString = d.LocationName;
|
||||
m_CachedDependencyLocations.Add(locationString);
|
||||
|
||||
if (locationString == null)
|
||||
continue;
|
||||
|
||||
//Prevent location display from being excessively long
|
||||
if (locationString.Length > k_MaxDisplayedLocationLength)
|
||||
{
|
||||
locationString = AsyncOperationBase<object>.ShortenPath(locationString, true);
|
||||
locationString = locationString.Substring(0, Math.Min(k_MaxDisplayedLocationLength, locationString.Length)) + "...";
|
||||
}
|
||||
|
||||
if (i == deps.Count - 1)
|
||||
toBeDisplayed += locationString;
|
||||
else
|
||||
toBeDisplayed += locationString + ", ";
|
||||
}
|
||||
|
||||
toBeDisplayed += "]";
|
||||
|
||||
debugName = toBeDisplayed;
|
||||
|
||||
return debugName;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
m_LoadedCount = 0;
|
||||
for (int i = 0; i < Result.Count; i++)
|
||||
{
|
||||
if (Result[i].IsDone)
|
||||
m_LoadedCount++;
|
||||
else
|
||||
Result[i].Completed += m_InternalOnComplete;
|
||||
}
|
||||
|
||||
CompleteIfDependenciesComplete();
|
||||
}
|
||||
|
||||
private void CompleteIfDependenciesComplete()
|
||||
{
|
||||
if (m_LoadedCount == Result.Count)
|
||||
{
|
||||
bool success = true;
|
||||
OperationException ex = null;
|
||||
if (!m_Settings.HasFlag(GroupOperationSettings.AllowFailedDependencies))
|
||||
{
|
||||
for (int i = 0; i < Result.Count; i++)
|
||||
{
|
||||
if (Result[i].Status != AsyncOperationStatus.Succeeded)
|
||||
{
|
||||
success = false;
|
||||
ex = new OperationException("GroupOperation failed because one of its dependencies failed", Result[i].OperationException);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Complete(Result, success, ex, m_Settings.HasFlag(GroupOperationSettings.ReleaseDependenciesOnFailure));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
ReleaseDependencies();
|
||||
}
|
||||
|
||||
protected override float Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
float total = 0f;
|
||||
for (int i = 0; i < Result.Count; i++)
|
||||
{
|
||||
var handle = Result[i];
|
||||
if (!handle.IsDone)
|
||||
total += handle.PercentComplete;
|
||||
else
|
||||
total++;
|
||||
}
|
||||
|
||||
return total / Result.Count;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Init(List<AsyncOperationHandle> operations, bool releaseDependenciesOnFailure = true, bool allowFailedDependencies = false)
|
||||
{
|
||||
Result = new List<AsyncOperationHandle>(operations);
|
||||
m_Settings = releaseDependenciesOnFailure ? GroupOperationSettings.ReleaseDependenciesOnFailure : GroupOperationSettings.None;
|
||||
if (allowFailedDependencies)
|
||||
m_Settings |= GroupOperationSettings.AllowFailedDependencies;
|
||||
}
|
||||
|
||||
public void Init(List<AsyncOperationHandle> operations, GroupOperationSettings settings)
|
||||
{
|
||||
Result = new List<AsyncOperationHandle>(operations);
|
||||
m_Settings = settings;
|
||||
}
|
||||
|
||||
void OnOperationCompleted(AsyncOperationHandle op)
|
||||
{
|
||||
m_LoadedCount++;
|
||||
CompleteIfDependenciesComplete();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f5fa1c5d453431041ab1e49b72e569e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,303 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
using UnityEngine.ResourceManagement.Util;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
internal interface IGenericProviderOperation
|
||||
{
|
||||
void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp);
|
||||
void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp, bool releaseDependenciesOnFailure);
|
||||
int ProvideHandleVersion { get; }
|
||||
IResourceLocation Location { get; }
|
||||
int DependencyCount { get; }
|
||||
void GetDependencies(IList<object> dstList);
|
||||
TDepObject GetDependency<TDepObject>(int index);
|
||||
void SetProgressCallback(Func<float> callback);
|
||||
void ProviderCompleted<T>(T result, bool status, Exception e);
|
||||
Type RequestedType { get; }
|
||||
void SetDownloadProgressCallback(Func<DownloadStatus> callback);
|
||||
void SetWaitForCompletionCallback(Func<bool> callback);
|
||||
}
|
||||
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
internal class ProviderOperation<TObject> : AsyncOperationBase<TObject>, IGenericProviderOperation, ICachable
|
||||
{
|
||||
private bool m_ReleaseDependenciesOnFailure = true;
|
||||
private Action<int, object, bool, Exception> m_CompletionCallback;
|
||||
private Action<int, IList<object>> m_GetDepCallback;
|
||||
private Func<float> m_GetProgressCallback;
|
||||
private Func<DownloadStatus> m_GetDownloadProgressCallback;
|
||||
private Func<bool> m_WaitForCompletionCallback;
|
||||
private bool m_ProviderCompletedCalled = false; //This is used to fix a race condition with WaitForCompletion, like in AssetBundleProvider
|
||||
private DownloadStatus m_DownloadStatus;
|
||||
private IResourceProvider m_Provider;
|
||||
internal AsyncOperationHandle<IList<AsyncOperationHandle>> m_DepOp;
|
||||
private IResourceLocation m_Location;
|
||||
private int m_ProvideHandleVersion;
|
||||
private bool m_NeedsRelease;
|
||||
IOperationCacheKey ICachable.Key { get; set; }
|
||||
private ResourceManager m_ResourceManager;
|
||||
private const float k_OperationWaitingToCompletePercentComplete = 0.99f;
|
||||
|
||||
public int ProvideHandleVersion
|
||||
{
|
||||
get { return m_ProvideHandleVersion; }
|
||||
}
|
||||
|
||||
public IResourceLocation Location
|
||||
{
|
||||
get { return m_Location; }
|
||||
}
|
||||
|
||||
public void SetDownloadProgressCallback(Func<DownloadStatus> callback)
|
||||
{
|
||||
m_GetDownloadProgressCallback = callback;
|
||||
if (m_GetDownloadProgressCallback != null)
|
||||
m_DownloadStatus = m_GetDownloadProgressCallback();
|
||||
}
|
||||
|
||||
public void SetWaitForCompletionCallback(Func<bool> callback)
|
||||
{
|
||||
m_WaitForCompletionCallback = callback;
|
||||
}
|
||||
|
||||
///<inheritdoc />
|
||||
protected override bool InvokeWaitForCompletion()
|
||||
{
|
||||
if (IsDone || m_ProviderCompletedCalled)
|
||||
return true;
|
||||
|
||||
if (m_DepOp.IsValid() && !m_DepOp.IsDone)
|
||||
m_DepOp.WaitForCompletion();
|
||||
|
||||
m_RM?.Update(Time.unscaledDeltaTime);
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
|
||||
if (m_WaitForCompletionCallback == null)
|
||||
return false;
|
||||
|
||||
return m_WaitForCompletionCallback.Invoke();
|
||||
}
|
||||
|
||||
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
||||
{
|
||||
var depDLS = m_DepOp.IsValid() ? m_DepOp.InternalGetDownloadStatus(visited) : default;
|
||||
|
||||
if (m_GetDownloadProgressCallback != null)
|
||||
m_DownloadStatus = m_GetDownloadProgressCallback();
|
||||
|
||||
if (Status == AsyncOperationStatus.Succeeded)
|
||||
m_DownloadStatus.DownloadedBytes = m_DownloadStatus.TotalBytes;
|
||||
|
||||
return new DownloadStatus() {DownloadedBytes = m_DownloadStatus.DownloadedBytes + depDLS.DownloadedBytes, TotalBytes = m_DownloadStatus.TotalBytes + depDLS.TotalBytes, IsDone = IsDone};
|
||||
}
|
||||
|
||||
public ProviderOperation()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDependencies(List<AsyncOperationHandle> deps)
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
deps.Add(m_DepOp);
|
||||
}
|
||||
|
||||
internal override void ReleaseDependencies()
|
||||
{
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
}
|
||||
|
||||
protected override string DebugName
|
||||
{
|
||||
get { return string.Format("Resource<{0}>({1})", typeof(TObject).Name, m_Location == null ? "Invalid" : ShortenPath(m_Location.InternalId, true)); }
|
||||
}
|
||||
|
||||
internal const string kInvalidHandleMsg = "The ProvideHandle is invalid. After the handle has been completed, it can no longer be used";
|
||||
|
||||
public void GetDependencies(IList<object> dstList)
|
||||
{
|
||||
dstList.Clear();
|
||||
|
||||
if (!m_DepOp.IsValid())
|
||||
return;
|
||||
|
||||
if (m_DepOp.Result == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < m_DepOp.Result.Count; i++)
|
||||
dstList.Add(m_DepOp.Result[i].Result);
|
||||
}
|
||||
|
||||
public Type RequestedType
|
||||
{
|
||||
get { return typeof(TObject); }
|
||||
}
|
||||
|
||||
public int DependencyCount
|
||||
{
|
||||
get { return (!m_DepOp.IsValid() || m_DepOp.Result == null) ? 0 : m_DepOp.Result.Count; }
|
||||
}
|
||||
|
||||
public TDepObject GetDependency<TDepObject>(int index)
|
||||
{
|
||||
if (!m_DepOp.IsValid() || m_DepOp.Result == null)
|
||||
throw new Exception("Cannot get dependency because no dependencies were available");
|
||||
|
||||
return (TDepObject)(m_DepOp.Result[index].Result);
|
||||
}
|
||||
|
||||
public void SetProgressCallback(Func<float> callback)
|
||||
{
|
||||
m_GetProgressCallback = callback;
|
||||
}
|
||||
|
||||
public void ProviderCompleted<T>(T result, bool status, Exception e)
|
||||
{
|
||||
m_ProvideHandleVersion++;
|
||||
m_GetProgressCallback = null;
|
||||
m_GetDownloadProgressCallback = null;
|
||||
m_WaitForCompletionCallback = null;
|
||||
m_NeedsRelease = status;
|
||||
m_ProviderCompletedCalled = true;
|
||||
|
||||
ProviderOperation<T> top = this as ProviderOperation<T>;
|
||||
if (top != null)
|
||||
{
|
||||
top.Result = result;
|
||||
}
|
||||
else if (result == null && !typeof(TObject).IsValueType)
|
||||
{
|
||||
Result = (TObject)(object)null;
|
||||
}
|
||||
else if (result != null && typeof(TObject).IsAssignableFrom(result.GetType()))
|
||||
{
|
||||
Result = (TObject)(object)result;
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMsg = string.Format(
|
||||
"Provider of type {0} with id {1} has provided a result of type {2} which cannot be converted to requested type {3}. The operation will be marked as failed.",
|
||||
m_Provider.GetType().ToString(), m_Provider.ProviderId, typeof(T), typeof(TObject));
|
||||
Complete(Result, false, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
|
||||
Complete(Result, status, e, m_ReleaseDependenciesOnFailure);
|
||||
}
|
||||
|
||||
protected override float Progress
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
float numberOfOps = 1f;
|
||||
float total = 0f;
|
||||
if (m_GetProgressCallback != null)
|
||||
total += m_GetProgressCallback();
|
||||
|
||||
if (!m_DepOp.IsValid() || m_DepOp.Result == null || m_DepOp.Result.Count == 0)
|
||||
{
|
||||
total++;
|
||||
numberOfOps++;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var handle in m_DepOp.Result)
|
||||
{
|
||||
total += handle.PercentComplete;
|
||||
numberOfOps++;
|
||||
}
|
||||
}
|
||||
|
||||
float result = total / numberOfOps;
|
||||
//This is done because all AssetBundle operations (m_DepOp.Result) can complete as well as the
|
||||
//BundledAssetRequest operation (m_GetProgressCallBack) but this overall operation hasn't completed yet.
|
||||
//Once the operation has a chance to complete we short circut calling into Progress here and just return 1.0f
|
||||
return Mathf.Min(result, k_OperationWaitingToCompletePercentComplete);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
Debug.Assert(m_DepOp.IsDone);
|
||||
|
||||
if (m_DepOp.IsValid() && m_DepOp.Status == AsyncOperationStatus.Failed && (m_Provider.BehaviourFlags & ProviderBehaviourFlags.CanProvideWithFailedDependencies) == 0)
|
||||
{
|
||||
ProviderCompleted(default(TObject), false, new Exception("Dependency Exception", m_DepOp.OperationException));
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Provider.Provide(new ProvideHandle(m_ResourceManager, this));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ProviderCompleted(default(TObject), false, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp)
|
||||
{
|
||||
m_DownloadStatus = default;
|
||||
m_ResourceManager = rm;
|
||||
m_DepOp = depOp;
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Acquire();
|
||||
m_Provider = provider;
|
||||
m_Location = location;
|
||||
m_ReleaseDependenciesOnFailure = true;
|
||||
m_ProviderCompletedCalled = false;
|
||||
SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
}
|
||||
|
||||
public void Init(ResourceManager rm, IResourceProvider provider, IResourceLocation location, AsyncOperationHandle<IList<AsyncOperationHandle>> depOp, bool releaseDependenciesOnFailure)
|
||||
{
|
||||
m_DownloadStatus = default;
|
||||
m_ResourceManager = rm;
|
||||
m_DepOp = depOp;
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Acquire();
|
||||
m_Provider = provider;
|
||||
m_Location = location;
|
||||
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
|
||||
m_ProviderCompletedCalled = false;
|
||||
SetWaitForCompletionCallback(WaitForCompletionHandler);
|
||||
}
|
||||
|
||||
bool WaitForCompletionHandler()
|
||||
{
|
||||
if (IsDone)
|
||||
return true;
|
||||
|
||||
if (!m_DepOp.IsDone)
|
||||
m_DepOp.WaitForCompletion();
|
||||
if (!HasExecuted)
|
||||
InvokeExecute();
|
||||
|
||||
return IsDone;
|
||||
}
|
||||
|
||||
protected override void Destroy()
|
||||
{
|
||||
if (m_NeedsRelease)
|
||||
m_Provider.Release(m_Location, Result);
|
||||
if (m_DepOp.IsValid())
|
||||
m_DepOp.Release();
|
||||
Result = default(TObject);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b28e4f74756c9e3418600f517d56e9f2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace UnityEngine.ResourceManagement.AsyncOperations
|
||||
{
|
||||
internal class UnityWebRequestOperation : AsyncOperationBase<UnityWebRequest>
|
||||
{
|
||||
UnityWebRequest m_UWR;
|
||||
|
||||
public UnityWebRequestOperation(UnityWebRequest webRequest)
|
||||
{
|
||||
m_UWR = webRequest;
|
||||
}
|
||||
|
||||
protected override void Execute()
|
||||
{
|
||||
m_UWR.SendWebRequest().completed += (request) => { Complete(m_UWR, string.IsNullOrEmpty(m_UWR.error), m_UWR.error); };
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d9a6f10d6aba7e40b982e7ed18ecfff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue