7502018d20
There is an asset in the store I grabbed. the coding is WAY above my head, I got about half of it and integrated and adapted what I can to it. im going as far as I can with it and ill come back in a few month when I understand t better.
1095 lines
50 KiB
C#
1095 lines
50 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.Networking;
|
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
using UnityEngine.ResourceManagement.Exceptions;
|
|
using UnityEngine.ResourceManagement.ResourceLocations;
|
|
using UnityEngine.ResourceManagement.ResourceProviders;
|
|
using UnityEngine.ResourceManagement.Util;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace UnityEngine.ResourceManagement
|
|
{
|
|
/// <summary>
|
|
/// Entry point for ResourceManager API
|
|
/// </summary>
|
|
public class ResourceManager : IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Options for event types that will be sent by the ResourceManager
|
|
/// </summary>
|
|
public enum DiagnosticEventType
|
|
{
|
|
/// <summary>
|
|
/// Use to indicate that an operation failed.
|
|
/// </summary>
|
|
AsyncOperationFail,
|
|
/// <summary>
|
|
/// Use to indicate that an operation was created.
|
|
/// </summary>
|
|
AsyncOperationCreate,
|
|
/// <summary>
|
|
/// Use to indicate the percentage of completion for an operation was updated.
|
|
/// </summary>
|
|
AsyncOperationPercentComplete,
|
|
/// <summary>
|
|
/// Use to indicate that an operation has completed.
|
|
/// </summary>
|
|
AsyncOperationComplete,
|
|
/// <summary>
|
|
/// Use to indicate that the reference count of an operation was modified.
|
|
/// </summary>
|
|
AsyncOperationReferenceCount,
|
|
/// <summary>
|
|
/// Use to indicate that an operation was destroyed.
|
|
/// </summary>
|
|
AsyncOperationDestroy,
|
|
}
|
|
|
|
internal bool postProfilerEvents = false;
|
|
|
|
/// <summary>
|
|
/// Container for information associated with a Diagnostics event.
|
|
/// </summary>
|
|
public struct DiagnosticEventContext
|
|
{
|
|
/// <summary>
|
|
/// Operation handle for the event.
|
|
/// </summary>
|
|
public AsyncOperationHandle OperationHandle { get; }
|
|
|
|
/// <summary>
|
|
/// The type of diagnostic event.
|
|
/// </summary>
|
|
public DiagnosticEventType Type { get; }
|
|
|
|
/// <summary>
|
|
/// The value for this event.
|
|
/// </summary>
|
|
public int EventValue { get; }
|
|
|
|
/// <summary>
|
|
/// The IResourceLocation being provided by the operation triggering this event.
|
|
/// This value is null if the event is not while providing a resource.
|
|
/// </summary>
|
|
public IResourceLocation Location { get; }
|
|
|
|
/// <summary>
|
|
/// Addition data included with this event.
|
|
/// </summary>
|
|
public object Context { get; }
|
|
|
|
/// <summary>
|
|
/// Any error that occured.
|
|
/// </summary>
|
|
public string Error { get; }
|
|
|
|
/// <summary>
|
|
/// Construct a new DiagnosticEventContext.
|
|
/// </summary>
|
|
/// <param name="op">Operation handle for the event.</param>
|
|
/// <param name="type">The type of diagnostic event.</param>
|
|
/// <param name="eventValue">The value for this event.</param>
|
|
/// <param name="error">Any error that occured.</param>
|
|
/// <param name="context">Additional context data.</param>
|
|
public DiagnosticEventContext(AsyncOperationHandle op, DiagnosticEventType type, int eventValue = 0, string error = null, object context = null)
|
|
{
|
|
OperationHandle = op;
|
|
Type = type;
|
|
EventValue = eventValue;
|
|
Location = op.m_InternalOp != null && op.m_InternalOp is IGenericProviderOperation gen
|
|
? gen.Location
|
|
: null;
|
|
Error = error;
|
|
Context = context;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Global exception handler. This will be called whenever an IAsyncOperation.OperationException is set to a non-null value.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code source="../../Tests/Editor/DocExampleCode/AddExceptionHandler.cs" region="doc_AddExceptionHandler" title="Adding a global exception hanlder"/>
|
|
/// </example>
|
|
public static Action<AsyncOperationHandle, Exception> ExceptionHandler { get; set; }
|
|
|
|
/// <summary>
|
|
/// Functor to transform internal ids before being used by the providers.
|
|
/// See the [TransformInternalId](xref:addressables-api-transform-internal-id) documentation for more details.
|
|
/// </summary>
|
|
public Func<IResourceLocation, string> InternalIdTransformFunc { get; set; }
|
|
|
|
/// <summary>
|
|
/// Checks for an internal id transform function and uses it to modify the internal id value.
|
|
/// </summary>
|
|
/// <param name="location">The location to transform the internal id of.</param>
|
|
/// <returns>If a transform func is set, use it to pull the local id; otherwise, the InternalId property of the location is used.</returns>
|
|
public string TransformInternalId(IResourceLocation location)
|
|
{
|
|
return InternalIdTransformFunc == null ? location.InternalId : InternalIdTransformFunc(location);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delegate that can be used to override the web request options before being sent.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The web request passed to this delegate has already been preconfigured internally. Override at your own risk.
|
|
/// </remarks>
|
|
public Action<UnityWebRequest> WebRequestOverride { get; set; }
|
|
|
|
internal bool CallbackHooksEnabled = true; // tests might need to disable the callback hooks to manually pump updating
|
|
|
|
ListWithEvents<IResourceProvider> m_ResourceProviders = new ListWithEvents<IResourceProvider>();
|
|
IAllocationStrategy m_allocator;
|
|
|
|
// list of all the providers in s_ResourceProviders that implement IUpdateReceiver
|
|
ListWithEvents<IUpdateReceiver> m_UpdateReceivers = new ListWithEvents<IUpdateReceiver>();
|
|
List<IUpdateReceiver> m_UpdateReceiversToRemove = null;
|
|
//this prevents removing receivers during iteration
|
|
bool m_UpdatingReceivers = false;
|
|
//this prevents re-entrance into the Update method, which can cause stack overflow and infinite loops
|
|
bool m_InsideUpdateMethod = false;
|
|
internal int OperationCacheCount { get { return m_AssetOperationCache.Count; } }
|
|
internal int InstanceOperationCount { get { return m_TrackedInstanceOperations.Count; } }
|
|
//cache of type + providerId to IResourceProviders for faster lookup
|
|
internal Dictionary<int, IResourceProvider> m_providerMap = new Dictionary<int, IResourceProvider>();
|
|
Dictionary<IOperationCacheKey, IAsyncOperation> m_AssetOperationCache = new Dictionary<IOperationCacheKey, IAsyncOperation>();
|
|
HashSet<InstanceOperation> m_TrackedInstanceOperations = new HashSet<InstanceOperation>();
|
|
DelegateList<float> m_UpdateCallbacks = DelegateList<float>.CreateWithGlobalCache();
|
|
List<IAsyncOperation> m_DeferredCompleteCallbacks = new List<IAsyncOperation>();
|
|
|
|
bool m_InsideExecuteDeferredCallbacksMethod = false;
|
|
List<DeferredCallbackRegisterRequest> m_DeferredCallbacksToRegister = null;
|
|
private struct DeferredCallbackRegisterRequest
|
|
{
|
|
internal IAsyncOperation operation;
|
|
internal bool incrementRefCount;
|
|
}
|
|
|
|
Action<AsyncOperationHandle, DiagnosticEventType, int, object> m_obsoleteDiagnosticsHandler; // For use in working with Obsolete RegisterDiagnosticCallback method.
|
|
Action<DiagnosticEventContext> m_diagnosticsHandler;
|
|
Action<IAsyncOperation> m_ReleaseOpNonCached;
|
|
Action<IAsyncOperation> m_ReleaseOpCached;
|
|
Action<IAsyncOperation> m_ReleaseInstanceOp;
|
|
static int s_GroupOperationTypeHash = typeof(GroupOperation).GetHashCode();
|
|
static int s_InstanceOperationTypeHash = typeof(InstanceOperation).GetHashCode();
|
|
/// <summary>
|
|
/// Add an update reveiver.
|
|
/// </summary>
|
|
/// <param name="receiver">The object to add. The Update method will be called until the object is removed. </param>
|
|
public void AddUpdateReceiver(IUpdateReceiver receiver)
|
|
{
|
|
if (receiver == null)
|
|
return;
|
|
m_UpdateReceivers.Add(receiver);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove update receiver.
|
|
/// </summary>
|
|
/// <param name="receiver">The object to remove.</param>
|
|
public void RemoveUpdateReciever(IUpdateReceiver receiver)
|
|
{
|
|
if (receiver == null)
|
|
return;
|
|
|
|
if (m_UpdatingReceivers)
|
|
{
|
|
if (m_UpdateReceiversToRemove == null)
|
|
m_UpdateReceiversToRemove = new List<IUpdateReceiver>();
|
|
m_UpdateReceiversToRemove.Add(receiver);
|
|
}
|
|
else
|
|
{
|
|
m_UpdateReceivers.Remove(receiver);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The allocation strategy object.
|
|
/// </summary>
|
|
public IAllocationStrategy Allocator { get { return m_allocator; } set { m_allocator = value; } }
|
|
|
|
/// <summary>
|
|
/// Gets the list of configured <see cref="IResourceProvider"/> objects. Resource Providers handle load and release operations for <see cref="IResourceLocation"/> objects.
|
|
/// </summary>
|
|
/// <value>The resource providers list.</value>
|
|
public IList<IResourceProvider> ResourceProviders { get { return m_ResourceProviders; } }
|
|
|
|
/// <summary>
|
|
/// The CertificateHandler instance object.
|
|
/// </summary>
|
|
public CertificateHandler CertificateHandlerInstance { get; set; }
|
|
|
|
/// <summary>
|
|
/// Constructor for the resource manager.
|
|
/// </summary>
|
|
/// <param name="alloc">The allocation strategy to use.</param>
|
|
public ResourceManager(IAllocationStrategy alloc = null)
|
|
{
|
|
m_ReleaseOpNonCached = OnOperationDestroyNonCached;
|
|
m_ReleaseOpCached = OnOperationDestroyCached;
|
|
m_ReleaseInstanceOp = OnInstanceOperationDestroy;
|
|
m_allocator = alloc == null ? new LRUCacheAllocationStrategy(1000, 1000, 100, 10) : alloc;
|
|
m_ResourceProviders.OnElementAdded += OnObjectAdded;
|
|
m_ResourceProviders.OnElementRemoved += OnObjectRemoved;
|
|
m_UpdateReceivers.OnElementAdded += x => RegisterForCallbacks();
|
|
}
|
|
|
|
private void OnObjectAdded(object obj)
|
|
{
|
|
IUpdateReceiver updateReceiver = obj as IUpdateReceiver;
|
|
if (updateReceiver != null)
|
|
AddUpdateReceiver(updateReceiver);
|
|
}
|
|
|
|
private void OnObjectRemoved(object obj)
|
|
{
|
|
IUpdateReceiver updateReceiver = obj as IUpdateReceiver;
|
|
if (updateReceiver != null)
|
|
RemoveUpdateReciever(updateReceiver);
|
|
}
|
|
|
|
bool m_RegisteredForCallbacks = false;
|
|
internal void RegisterForCallbacks()
|
|
{
|
|
if (CallbackHooksEnabled && !m_RegisteredForCallbacks)
|
|
{
|
|
m_RegisteredForCallbacks = true;
|
|
MonoBehaviourCallbackHooks.Instance.OnUpdateDelegate += Update;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears out the diagnostics callback handler.
|
|
/// </summary>
|
|
[Obsolete("ClearDiagnosticsCallback is Obsolete, use ClearDiagnosticCallbacks instead.")]
|
|
public void ClearDiagnosticsCallback()
|
|
{
|
|
m_diagnosticsHandler = null;
|
|
m_obsoleteDiagnosticsHandler = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears out the diagnostics callbacks handler.
|
|
/// </summary>
|
|
public void ClearDiagnosticCallbacks()
|
|
{
|
|
m_diagnosticsHandler = null;
|
|
m_obsoleteDiagnosticsHandler = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unregister a handler for diagnostic events.
|
|
/// </summary>
|
|
/// <param name="func">The event handler function.</param>
|
|
public void UnregisterDiagnosticCallback(Action<DiagnosticEventContext> func)
|
|
{
|
|
if (m_diagnosticsHandler != null)
|
|
m_diagnosticsHandler -= func;
|
|
else
|
|
Debug.LogError("No Diagnostic callbacks registered, cannot remove callback.");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register a handler for diagnostic events.
|
|
/// </summary>
|
|
/// <param name="func">The event handler function.</param>
|
|
[Obsolete]
|
|
public void RegisterDiagnosticCallback(Action<AsyncOperationHandle, ResourceManager.DiagnosticEventType, int, object> func)
|
|
{
|
|
m_obsoleteDiagnosticsHandler = func;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register a handler for diagnostic events.
|
|
/// </summary>
|
|
/// <param name="func">The event handler function.</param>
|
|
public void RegisterDiagnosticCallback(Action<DiagnosticEventContext> func)
|
|
{
|
|
m_diagnosticsHandler += func;
|
|
}
|
|
|
|
internal void PostDiagnosticEvent(DiagnosticEventContext context)
|
|
{
|
|
m_diagnosticsHandler?.Invoke(context);
|
|
|
|
if (m_obsoleteDiagnosticsHandler == null)
|
|
return;
|
|
m_obsoleteDiagnosticsHandler(context.OperationHandle, context.Type, context.EventValue, string.IsNullOrEmpty(context.Error) ? context.Context : context.Error);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the appropriate <see cref="IResourceProvider"/> for the given <paramref name="location"/> and <paramref name="type"/>.
|
|
/// </summary>
|
|
/// <returns>The resource provider. Or null if an appropriate provider cannot be found</returns>
|
|
/// <param name="t">The desired object type to be loaded from the provider.</param>
|
|
/// <param name="location">The resource location.</param>
|
|
public IResourceProvider GetResourceProvider(Type t, IResourceLocation location)
|
|
{
|
|
if (location != null)
|
|
{
|
|
IResourceProvider prov = null;
|
|
var hash = location.ProviderId.GetHashCode() * 31 + (t == null ? 0 : t.GetHashCode());
|
|
if (!m_providerMap.TryGetValue(hash, out prov))
|
|
{
|
|
for (int i = 0; i < ResourceProviders.Count; i++)
|
|
{
|
|
var p = ResourceProviders[i];
|
|
if (p.ProviderId.Equals(location.ProviderId, StringComparison.Ordinal) && (t == null || p.CanProvide(t, location)))
|
|
{
|
|
m_providerMap.Add(hash, prov = p);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return prov;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Type GetDefaultTypeForLocation(IResourceLocation loc)
|
|
{
|
|
var provider = GetResourceProvider(null, loc);
|
|
if (provider == null)
|
|
return typeof(object);
|
|
Type t = provider.GetDefaultType(loc);
|
|
return t != null ? t : typeof(object);
|
|
}
|
|
|
|
private int CalculateLocationsHash(IList<IResourceLocation> locations, Type t = null)
|
|
{
|
|
if (locations == null || locations.Count == 0)
|
|
return 0;
|
|
int hash = 17;
|
|
foreach (var loc in locations)
|
|
{
|
|
Type t2 = t != null ? t : GetDefaultTypeForLocation(loc);
|
|
hash = hash * 31 + loc.Hash(t2);
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the <typeparamref name="TObject"/> at the specified <paramref name="location"/>.
|
|
/// </summary>
|
|
/// <returns>An async operation.</returns>
|
|
/// <param name="location">Location to load.</param>
|
|
/// <param name="releaseDependenciesOnFailure">When true, if the operation fails, dependencies will be released.</param>
|
|
/// <typeparam name="TObject">Object type to load.</typeparam>
|
|
private AsyncOperationHandle ProvideResource(IResourceLocation location, Type desiredType = null, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
if (location == null)
|
|
throw new ArgumentNullException("location");
|
|
IResourceProvider provider = null;
|
|
if (desiredType == null)
|
|
{
|
|
provider = GetResourceProvider(desiredType, location);
|
|
if (provider == null)
|
|
{
|
|
var ex = new UnknownResourceProviderException(location);
|
|
return CreateCompletedOperationInternal<object>(null, false, ex, releaseDependenciesOnFailure);
|
|
}
|
|
desiredType = provider.GetDefaultType(location);
|
|
}
|
|
|
|
IAsyncOperation op;
|
|
var key = new LocationCacheKey(location, desiredType);
|
|
if (m_AssetOperationCache.TryGetValue(key, out op))
|
|
{
|
|
op.IncrementReferenceCount();
|
|
return new AsyncOperationHandle(op, location.ToString());;
|
|
}
|
|
|
|
Type provType;
|
|
if (!m_ProviderOperationTypeCache.TryGetValue(desiredType, out provType))
|
|
m_ProviderOperationTypeCache.Add(desiredType, provType = typeof(ProviderOperation<>).MakeGenericType(new Type[] { desiredType }));
|
|
op = CreateOperation<IAsyncOperation>(provType, provType.GetHashCode(), key, m_ReleaseOpCached);
|
|
|
|
// Calculate the hash of the dependencies
|
|
int depHash = location.DependencyHashCode;
|
|
var depOp = location.HasDependencies ?
|
|
ProvideResourceGroupCached(location.Dependencies, depHash, null, null, releaseDependenciesOnFailure) :
|
|
default(AsyncOperationHandle<IList<AsyncOperationHandle>>);
|
|
if (provider == null)
|
|
provider = GetResourceProvider(desiredType, location);
|
|
|
|
((IGenericProviderOperation)op).Init(this, provider, location, depOp, releaseDependenciesOnFailure);
|
|
|
|
var handle = StartOperation(op, depOp);
|
|
handle.LocationName = location.ToString();
|
|
|
|
if (depOp.IsValid())
|
|
depOp.Release();
|
|
|
|
return handle;
|
|
}
|
|
|
|
Dictionary<Type, Type> m_ProviderOperationTypeCache = new Dictionary<Type, Type>();
|
|
|
|
/// <summary>
|
|
/// Load the <typeparamref name="TObject"/> at the specified <paramref name="location"/>.
|
|
/// </summary>
|
|
/// <returns>An async operation.</returns>
|
|
/// <param name="location">Location to load.</param>
|
|
/// <typeparam name="TObject">Object type to load.</typeparam>
|
|
public AsyncOperationHandle<TObject> ProvideResource<TObject>(IResourceLocation location)
|
|
{
|
|
AsyncOperationHandle handle = ProvideResource(location, typeof(TObject));
|
|
return handle.Convert<TObject>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers an operation with the ResourceManager. The operation will be executed when the <paramref name="dependency"/> completes.
|
|
/// This should only be used when creating custom operations.
|
|
/// </summary>
|
|
/// <returns>The AsyncOperationHandle used to access the result and status of the operation.</returns>
|
|
/// <param name="operation">The custom AsyncOperationBase object</param>
|
|
/// <param name="dependency">Execution of the operation will not occur until this handle completes. A default handle can be passed if no dependency is required.</param>
|
|
/// <typeparam name="TObject">Object type associated with this operation.</typeparam>
|
|
public AsyncOperationHandle<TObject> StartOperation<TObject>(AsyncOperationBase<TObject> operation, AsyncOperationHandle dependency)
|
|
{
|
|
operation.Start(this, dependency, m_UpdateCallbacks);
|
|
return operation.Handle;
|
|
}
|
|
|
|
internal AsyncOperationHandle StartOperation(IAsyncOperation operation, AsyncOperationHandle dependency)
|
|
{
|
|
operation.Start(this, dependency, m_UpdateCallbacks);
|
|
return operation.Handle;
|
|
}
|
|
|
|
class CompletedOperation<TObject> : AsyncOperationBase<TObject>
|
|
{
|
|
bool m_Success;
|
|
Exception m_Exception;
|
|
bool m_ReleaseDependenciesOnFailure;
|
|
public CompletedOperation() {}
|
|
public void Init(TObject result, bool success, string errorMsg, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
Init(result, success, !string.IsNullOrEmpty(errorMsg) ? new Exception(errorMsg) : null, releaseDependenciesOnFailure);
|
|
}
|
|
|
|
public void Init(TObject result, bool success, Exception exception, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
Result = result;
|
|
m_Success = success;
|
|
m_Exception = exception;
|
|
m_ReleaseDependenciesOnFailure = releaseDependenciesOnFailure;
|
|
}
|
|
|
|
protected override string DebugName
|
|
{
|
|
get { return "CompletedOperation";}
|
|
}
|
|
|
|
///<inheritdoc />
|
|
protected override bool InvokeWaitForCompletion()
|
|
{
|
|
m_RM?.Update(Time.unscaledDeltaTime);
|
|
if (!HasExecuted)
|
|
InvokeExecute();
|
|
return true;
|
|
}
|
|
|
|
protected override void Execute()
|
|
{
|
|
Complete(Result, m_Success, m_Exception, m_ReleaseDependenciesOnFailure);
|
|
}
|
|
}
|
|
|
|
void OnInstanceOperationDestroy(IAsyncOperation o)
|
|
{
|
|
m_TrackedInstanceOperations.Remove(o as InstanceOperation);
|
|
Allocator.Release(o.GetType().GetHashCode(), o);
|
|
}
|
|
|
|
void OnOperationDestroyNonCached(IAsyncOperation o)
|
|
{
|
|
Allocator.Release(o.GetType().GetHashCode(), o);
|
|
}
|
|
|
|
void OnOperationDestroyCached(IAsyncOperation o)
|
|
{
|
|
Allocator.Release(o.GetType().GetHashCode(), o);
|
|
var cachable = o as ICachable;
|
|
if (cachable?.Key != null)
|
|
RemoveOperationFromCache(cachable.Key);
|
|
}
|
|
|
|
internal T CreateOperation<T>(Type actualType, int typeHash, IOperationCacheKey cacheKey, Action<IAsyncOperation> onDestroyAction) where T : IAsyncOperation
|
|
{
|
|
if (cacheKey == null)
|
|
{
|
|
var op = (T)Allocator.New(actualType, typeHash);
|
|
op.OnDestroy = onDestroyAction;
|
|
return op;
|
|
}
|
|
else
|
|
{
|
|
var op = (T)Allocator.New(actualType, typeHash);
|
|
op.OnDestroy = onDestroyAction;
|
|
if (op is ICachable cachable)
|
|
{
|
|
cachable.Key = cacheKey;
|
|
AddOperationToCache(cacheKey, op);
|
|
}
|
|
return op;
|
|
}
|
|
}
|
|
|
|
internal void AddOperationToCache(IOperationCacheKey key, IAsyncOperation operation)
|
|
{
|
|
if (!IsOperationCached(key))
|
|
m_AssetOperationCache.Add(key, operation);
|
|
}
|
|
|
|
internal bool RemoveOperationFromCache(IOperationCacheKey key)
|
|
{
|
|
if (!IsOperationCached(key))
|
|
return true;
|
|
|
|
return m_AssetOperationCache.Remove(key);
|
|
}
|
|
|
|
internal bool IsOperationCached(IOperationCacheKey key)
|
|
{
|
|
return m_AssetOperationCache.ContainsKey(key);
|
|
}
|
|
|
|
internal int CachedOperationCount()
|
|
{
|
|
return m_AssetOperationCache.Count;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an operation that has already completed with a specified result and error message./>.
|
|
/// </summary>
|
|
/// <param name="result">The result that the operation will provide.</param>
|
|
/// <param name="errorMsg">The error message if the operation should be in the failed state. Otherwise null or empty string.</param>
|
|
/// <typeparam name="TObject">Object type.</typeparam>
|
|
/// <returns>The operation handle used for the completed operation.</returns>
|
|
public AsyncOperationHandle<TObject> CreateCompletedOperation<TObject>(TObject result, string errorMsg)
|
|
{
|
|
var success = string.IsNullOrEmpty(errorMsg);
|
|
return CreateCompletedOperationInternal(result, success, !success ? new Exception(errorMsg) : null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an operation that has already completed with a specified result and error message./>.
|
|
/// </summary>
|
|
/// <param name="result">The result that the operation will provide.</param>
|
|
/// <param name="exception">The exception with an error message if the operation should be in the failed state. Otherwise null.</param>
|
|
/// <typeparam name="TObject">Object type.</typeparam>
|
|
/// <returns>The operation handle used for the completed operation.</returns>
|
|
public AsyncOperationHandle<TObject> CreateCompletedOperationWithException<TObject>(TObject result, Exception exception)
|
|
{
|
|
return CreateCompletedOperationInternal(result, exception == null, exception);
|
|
}
|
|
|
|
internal AsyncOperationHandle<TObject> CreateCompletedOperationInternal<TObject>(TObject result, bool success, Exception exception, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
var cop = CreateOperation<CompletedOperation<TObject>>(typeof(CompletedOperation<TObject>), typeof(CompletedOperation<TObject>).GetHashCode(), null, m_ReleaseOpNonCached);
|
|
cop.Init(result, success, exception, releaseDependenciesOnFailure);
|
|
return StartOperation(cop, default(AsyncOperationHandle));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release the operation associated with the specified handle
|
|
/// </summary>
|
|
/// <param name="handle">The handle to release.</param>
|
|
public void Release(AsyncOperationHandle handle)
|
|
{
|
|
handle.Release();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Increment reference count of operation handle.
|
|
/// </summary>
|
|
/// <param name="handle">The handle to the resource to increment the reference count for.</param>
|
|
public void Acquire(AsyncOperationHandle handle)
|
|
{
|
|
handle.Acquire();
|
|
}
|
|
|
|
private GroupOperation AcquireGroupOpFromCache(IOperationCacheKey key)
|
|
{
|
|
IAsyncOperation opGeneric;
|
|
if (m_AssetOperationCache.TryGetValue(key, out opGeneric))
|
|
{
|
|
opGeneric.IncrementReferenceCount();
|
|
return (GroupOperation)opGeneric;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a group operation for a set of locations.
|
|
/// </summary>
|
|
/// <typeparam name="T">The expected object type for the operations.</typeparam>
|
|
/// <param name="locations">The list of locations to load.</param>
|
|
/// <returns>The operation for the entire group.</returns>
|
|
public AsyncOperationHandle<IList<AsyncOperationHandle>> CreateGroupOperation<T>(IList<IResourceLocation> locations)
|
|
{
|
|
var op = CreateOperation<GroupOperation>(typeof(GroupOperation), s_GroupOperationTypeHash, null, m_ReleaseOpNonCached);
|
|
var ops = new List<AsyncOperationHandle>(locations.Count);
|
|
foreach (var loc in locations)
|
|
ops.Add(ProvideResource<T>(loc));
|
|
|
|
op.Init(ops);
|
|
return StartOperation(op, default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a group operation for a set of locations.
|
|
/// </summary>
|
|
/// <typeparam name="T">The expected object type for the operations.</typeparam>
|
|
/// <param name="locations">The list of locations to load.</param>
|
|
/// <param name="allowFailedDependencies">The operation succeeds if any grouped locations fail.</param>
|
|
/// <returns>The operation for the entire group.</returns>
|
|
internal AsyncOperationHandle<IList<AsyncOperationHandle>> CreateGroupOperation<T>(IList<IResourceLocation> locations, bool allowFailedDependencies)
|
|
{
|
|
var op = CreateOperation<GroupOperation>(typeof(GroupOperation), s_GroupOperationTypeHash, null, m_ReleaseOpNonCached);
|
|
var ops = new List<AsyncOperationHandle>(locations.Count);
|
|
foreach (var loc in locations)
|
|
ops.Add(ProvideResource<T>(loc));
|
|
|
|
GroupOperation.GroupOperationSettings settings = GroupOperation.GroupOperationSettings.None;
|
|
if (allowFailedDependencies)
|
|
settings |= GroupOperation.GroupOperationSettings.AllowFailedDependencies;
|
|
op.Init(ops, settings);
|
|
return StartOperation(op, default);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a group operation for a set of AsyncOperationHandles
|
|
/// </summary>
|
|
/// <param name="operations">The list of operations that need to complete.</param>
|
|
/// <param name="releasedCachedOpOnComplete">Determine if the cached operation should be released or not.</param>
|
|
/// <returns>The operation for the entire group</returns>
|
|
public AsyncOperationHandle<IList<AsyncOperationHandle>> CreateGenericGroupOperation(List<AsyncOperationHandle> operations, bool releasedCachedOpOnComplete = false)
|
|
{
|
|
var op = CreateOperation<GroupOperation>(typeof(GroupOperation), s_GroupOperationTypeHash, new AsyncOpHandlesCacheKey(operations), releasedCachedOpOnComplete ? m_ReleaseOpCached : m_ReleaseOpNonCached);
|
|
op.Init(operations);
|
|
return StartOperation(op, default);
|
|
}
|
|
|
|
internal AsyncOperationHandle<IList<AsyncOperationHandle>> ProvideResourceGroupCached(
|
|
IList<IResourceLocation> locations, int groupHash, Type desiredType, Action<AsyncOperationHandle> callback, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
var depsKey = new DependenciesCacheKey(locations, groupHash);
|
|
GroupOperation op = AcquireGroupOpFromCache(depsKey);
|
|
AsyncOperationHandle<IList<AsyncOperationHandle>> handle;
|
|
if (op == null)
|
|
{
|
|
op = CreateOperation<GroupOperation>(typeof(GroupOperation), s_GroupOperationTypeHash, depsKey, m_ReleaseOpCached);
|
|
var ops = new List<AsyncOperationHandle>(locations.Count);
|
|
foreach (var loc in locations)
|
|
ops.Add(ProvideResource(loc, desiredType, releaseDependenciesOnFailure));
|
|
|
|
op.Init(ops, releaseDependenciesOnFailure);
|
|
|
|
handle = StartOperation(op, default(AsyncOperationHandle));
|
|
}
|
|
else
|
|
{
|
|
handle = op.Handle;
|
|
}
|
|
|
|
if (callback != null)
|
|
{
|
|
var depOps = op.GetDependentOps();
|
|
for (int i = 0; i < depOps.Count; i++)
|
|
{
|
|
depOps[i].Completed += callback;
|
|
}
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asynchronously load all objects in the given collection of <paramref name="locations"/>.
|
|
/// If any matching location fails, all loads and dependencies will be released. The returned .Result will be null, and .Status will be Failed.
|
|
/// </summary>
|
|
/// <returns>An async operation that will complete when all individual async load operations are complete.</returns>
|
|
/// <param name="locations">locations to load.</param>
|
|
/// <param name="callback">This callback will be invoked once for each object that is loaded.</param>
|
|
/// <typeparam name="TObject">Object type to load.</typeparam>
|
|
public AsyncOperationHandle<IList<TObject>> ProvideResources<TObject>(IList<IResourceLocation> locations, Action<TObject> callback = null)
|
|
{
|
|
return ProvideResources(locations, true, callback);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asynchronously load all objects in the given collection of <paramref name="locations"/>.
|
|
/// </summary>
|
|
/// <returns>An async operation that will complete when all individual async load operations are complete.</returns>
|
|
/// <param name="locations">locations to load.</param>
|
|
/// <param name="releaseDependenciesOnFailure">
|
|
/// If all matching locations succeed, this parameter is ignored.
|
|
/// When true, if any matching location fails, all loads and dependencies will be released. The returned .Result will be null, and .Status will be Failed.
|
|
/// When false, if any matching location fails, the returned .Result will be an IList of size equal to the number of locations attempted. Any failed location will
|
|
/// correlate to a null in the IList, while successful loads will correlate to a TObject in the list. The .Status will still be Failed.
|
|
/// When true, op does not need to be released if anything fails, when false, it must always be released.
|
|
/// </param>
|
|
/// <param name="callback">This callback will be invoked once for each object that is loaded.</param>
|
|
/// <typeparam name="TObject">Object type to load.</typeparam>
|
|
public AsyncOperationHandle<IList<TObject>> ProvideResources<TObject>(IList<IResourceLocation> locations, bool releaseDependenciesOnFailure, Action<TObject> callback = null)
|
|
{
|
|
if (locations == null)
|
|
return CreateCompletedOperation<IList<TObject>>(null, "Null Location");
|
|
|
|
Action<AsyncOperationHandle> callbackGeneric = null;
|
|
if (callback != null)
|
|
{
|
|
callbackGeneric = (x) => callback((TObject)(x.Result));
|
|
}
|
|
var typelessHandle = ProvideResourceGroupCached(locations, CalculateLocationsHash(locations, typeof(TObject)), typeof(TObject), callbackGeneric, releaseDependenciesOnFailure);
|
|
var chainOp = CreateChainOperation<IList<TObject>>(typelessHandle, (resultHandle) =>
|
|
{
|
|
AsyncOperationHandle<IList<AsyncOperationHandle>> handleToHandles = resultHandle.Convert<IList<AsyncOperationHandle>>();
|
|
|
|
var list = new List<TObject>();
|
|
Exception exception = null;
|
|
if (handleToHandles.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
foreach (var r in handleToHandles.Result)
|
|
list.Add(r.Convert<TObject>().Result);
|
|
}
|
|
else
|
|
{
|
|
bool foundSuccess = false;
|
|
if (!releaseDependenciesOnFailure)
|
|
{
|
|
foreach (AsyncOperationHandle handle in handleToHandles.Result)
|
|
{
|
|
if (handle.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
list.Add(handle.Convert<TObject>().Result);
|
|
foundSuccess = true;
|
|
}
|
|
else
|
|
list.Add(default(TObject));
|
|
}
|
|
}
|
|
|
|
if (!foundSuccess)
|
|
{
|
|
list = null;
|
|
exception = new ResourceManagerException("ProvideResources failed", handleToHandles.OperationException);
|
|
}
|
|
else
|
|
{
|
|
exception = new ResourceManagerException("Partial success in ProvideResources. Some items failed to load. See earlier logs for more info.", handleToHandles.OperationException);
|
|
}
|
|
}
|
|
|
|
return CreateCompletedOperationInternal<IList<TObject>>(list, exception == null, exception, releaseDependenciesOnFailure);
|
|
}, releaseDependenciesOnFailure);
|
|
|
|
// chain operation holds the dependency
|
|
typelessHandle.Release();
|
|
return chainOp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a chain operation to handle dependencies.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The type of operation handle to return.</typeparam>
|
|
/// <typeparam name="TObjectDependency">The type of the dependency operation.</typeparam>
|
|
/// <param name="dependentOp">The dependency operation.</param>
|
|
/// <param name="callback">The callback method that will create the dependent operation from the dependency operation.</param>
|
|
/// <returns>The operation handle.</returns>
|
|
public AsyncOperationHandle<TObject> CreateChainOperation<TObject, TObjectDependency>(AsyncOperationHandle<TObjectDependency> dependentOp, Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> callback)
|
|
{
|
|
var op = CreateOperation<ChainOperation<TObject, TObjectDependency>>(typeof(ChainOperation<TObject, TObjectDependency>), typeof(ChainOperation<TObject, TObjectDependency>).GetHashCode(), null, null);
|
|
op.Init(dependentOp, callback, true);
|
|
return StartOperation(op, dependentOp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a chain operation to handle dependencies.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The type of operation handle to return.</typeparam>
|
|
/// <param name="dependentOp">The dependency operation.</param>
|
|
/// <param name="callback">The callback method that will create the dependent operation from the dependency operation.</param>
|
|
/// <returns>The operation handle.</returns>
|
|
public AsyncOperationHandle<TObject> CreateChainOperation<TObject>(AsyncOperationHandle dependentOp, Func<AsyncOperationHandle, AsyncOperationHandle<TObject>> callback)
|
|
{
|
|
var cOp = new ChainOperationTypelessDepedency<TObject>();
|
|
cOp.Init(dependentOp, callback, true);
|
|
return StartOperation(cOp, dependentOp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a chain operation to handle dependencies.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The type of operation handle to return.</typeparam>
|
|
/// <typeparam name="TObjectDependency">The type of the dependency operation.</typeparam>
|
|
/// <param name="dependentOp">The dependency operation.</param>
|
|
/// <param name="callback">The callback method that will create the dependent operation from the dependency operation.</param>
|
|
/// <param name="releaseDependenciesOnFailure"> Whether to release dependencies if the created operation has failed.</param>
|
|
/// <returns>The operation handle.</returns>
|
|
public AsyncOperationHandle<TObject> CreateChainOperation<TObject, TObjectDependency>(AsyncOperationHandle<TObjectDependency> dependentOp, Func<AsyncOperationHandle<TObjectDependency>, AsyncOperationHandle<TObject>> callback, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
var op = CreateOperation<ChainOperation<TObject, TObjectDependency>>(typeof(ChainOperation<TObject, TObjectDependency>), typeof(ChainOperation<TObject, TObjectDependency>).GetHashCode(), null, null);
|
|
op.Init(dependentOp, callback, releaseDependenciesOnFailure);
|
|
return StartOperation(op, dependentOp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a chain operation to handle dependencies.
|
|
/// </summary>
|
|
/// <typeparam name="TObject">The type of operation handle to return.</typeparam>
|
|
/// <param name="dependentOp">The dependency operation.</param>
|
|
/// <param name="callback">The callback method that will create the dependent operation from the dependency operation.</param>
|
|
/// <param name="releaseDependenciesOnFailure"> Whether to release dependencies if the created operation has failed.</param>
|
|
/// <returns>The operation handle.</returns>
|
|
public AsyncOperationHandle<TObject> CreateChainOperation<TObject>(AsyncOperationHandle dependentOp, Func<AsyncOperationHandle, AsyncOperationHandle<TObject>> callback, bool releaseDependenciesOnFailure = true)
|
|
{
|
|
var cOp = new ChainOperationTypelessDepedency<TObject>();
|
|
cOp.Init(dependentOp, callback, releaseDependenciesOnFailure);
|
|
return StartOperation(cOp, dependentOp);
|
|
}
|
|
|
|
internal class InstanceOperation : AsyncOperationBase<GameObject>
|
|
{
|
|
AsyncOperationHandle<GameObject> m_dependency;
|
|
InstantiationParameters m_instantiationParams;
|
|
IInstanceProvider m_instanceProvider;
|
|
GameObject m_instance;
|
|
Scene m_scene;
|
|
|
|
public void Init(ResourceManager rm, IInstanceProvider instanceProvider, InstantiationParameters instantiationParams, AsyncOperationHandle<GameObject> dependency)
|
|
{
|
|
m_RM = rm;
|
|
m_dependency = dependency;
|
|
m_instanceProvider = instanceProvider;
|
|
m_instantiationParams = instantiationParams;
|
|
m_scene = default(Scene);
|
|
}
|
|
|
|
internal override DownloadStatus GetDownloadStatus(HashSet<object> visited)
|
|
{
|
|
return m_dependency.IsValid() ? m_dependency.InternalGetDownloadStatus(visited) : new DownloadStatus() { IsDone = IsDone };
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public override void GetDependencies(List<AsyncOperationHandle> deps)
|
|
{
|
|
deps.Add(m_dependency);
|
|
}
|
|
|
|
protected override string DebugName
|
|
{
|
|
get
|
|
{
|
|
if (m_instanceProvider == null)
|
|
return "Instance<Invalid>";
|
|
return string.Format("Instance<{0}>({1}", m_instanceProvider.GetType().Name, m_dependency.IsValid() ? m_dependency.DebugName : "Invalid");
|
|
}
|
|
}
|
|
|
|
public Scene InstanceScene() => m_scene;
|
|
|
|
protected override void Destroy()
|
|
{
|
|
m_instanceProvider.ReleaseInstance(m_RM, m_instance);
|
|
}
|
|
|
|
protected override float Progress
|
|
{
|
|
get
|
|
{
|
|
return m_dependency.PercentComplete;
|
|
}
|
|
}
|
|
|
|
///<inheritdoc />
|
|
protected override bool InvokeWaitForCompletion()
|
|
{
|
|
if (m_dependency.IsValid() && !m_dependency.IsDone)
|
|
m_dependency.WaitForCompletion();
|
|
|
|
m_RM?.Update(Time.unscaledDeltaTime);
|
|
if (m_instance == null && !HasExecuted)
|
|
InvokeExecute();
|
|
|
|
return IsDone;
|
|
}
|
|
|
|
protected override void Execute()
|
|
{
|
|
Exception e = m_dependency.OperationException;
|
|
if (m_dependency.Status == AsyncOperationStatus.Succeeded)
|
|
{
|
|
m_instance = m_instanceProvider.ProvideInstance(m_RM, m_dependency, m_instantiationParams);
|
|
if (m_instance != null)
|
|
m_scene = m_instance.scene;
|
|
Complete(m_instance, true, null);
|
|
}
|
|
else
|
|
{
|
|
Complete(m_instance, false, string.Format("Dependency operation failed with {0}.", e));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Load a scene at a specificed resource location.
|
|
/// </summary>
|
|
/// <param name="sceneProvider">The scene provider instance.</param>
|
|
/// <param name="location">The location of the scene.</param>
|
|
/// <param name="loadMode">The load mode for the scene.</param>
|
|
/// <param name="activateOnLoad">If false, the scene will be loaded in the background and not activated when complete.</param>
|
|
/// <param name="priority">The priority for the load operation.</param>
|
|
/// <returns>Async operation handle that will complete when the scene is loaded. If activateOnLoad is false, then Activate() will need to be called on the SceneInstance returned.</returns>
|
|
public AsyncOperationHandle<SceneInstance> ProvideScene(ISceneProvider sceneProvider, IResourceLocation location, LoadSceneMode loadMode, bool activateOnLoad, int priority)
|
|
{
|
|
if (sceneProvider == null)
|
|
throw new NullReferenceException("sceneProvider is null");
|
|
|
|
return sceneProvider.ProvideScene(this, location, loadMode, activateOnLoad, priority);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release a scene.
|
|
/// </summary>
|
|
/// <param name="sceneProvider">The scene provider.</param>
|
|
/// <param name="sceneLoadHandle">The operation handle used to load the scene.</param>
|
|
/// <returns>An operation handle for the unload.</returns>
|
|
public AsyncOperationHandle<SceneInstance> ReleaseScene(ISceneProvider sceneProvider, AsyncOperationHandle<SceneInstance> sceneLoadHandle)
|
|
{
|
|
if (sceneProvider == null)
|
|
throw new NullReferenceException("sceneProvider is null");
|
|
// if (sceneLoadHandle.ReferenceCount == 0)
|
|
// return CreateCompletedOperation<SceneInstance>(default(SceneInstance), "");
|
|
return sceneProvider.ReleaseScene(this, sceneLoadHandle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Asynchronously instantiate a prefab (GameObject) at the specified <paramref name="location"/>.
|
|
/// </summary>
|
|
/// <returns>Async operation that will complete when the prefab is instantiated.</returns>
|
|
/// <param name="provider">An implementation of IInstanceProvider that will be used to instantiate and destroy the GameObject.</param>
|
|
/// <param name="location">Location of the prefab.</param>
|
|
/// <param name="instantiateParameters">A struct containing the parameters to pass the the Instantiation call.</param>
|
|
public AsyncOperationHandle<GameObject> ProvideInstance(IInstanceProvider provider, IResourceLocation location, InstantiationParameters instantiateParameters)
|
|
{
|
|
if (provider == null)
|
|
throw new NullReferenceException("provider is null. Assign a valid IInstanceProvider object before using.");
|
|
|
|
if (location == null)
|
|
throw new ArgumentNullException("location");
|
|
|
|
var depOp = ProvideResource<GameObject>(location);
|
|
var baseOp = CreateOperation<InstanceOperation>(typeof(InstanceOperation), s_InstanceOperationTypeHash, null, m_ReleaseInstanceOp);
|
|
baseOp.Init(this, provider, instantiateParameters, depOp);
|
|
m_TrackedInstanceOperations.Add(baseOp);
|
|
return StartOperation<GameObject>(baseOp, depOp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases all instances the given scence.
|
|
/// </summary>
|
|
/// <param name="scene">The scene whose instances should be released.</param>
|
|
public void CleanupSceneInstances(Scene scene)
|
|
{
|
|
List<InstanceOperation> handlesToRelease = null;
|
|
foreach (var h in m_TrackedInstanceOperations)
|
|
{
|
|
if (h.Result == null && scene == h.InstanceScene())
|
|
{
|
|
if (handlesToRelease == null)
|
|
handlesToRelease = new List<InstanceOperation>();
|
|
handlesToRelease.Add(h);
|
|
}
|
|
}
|
|
if (handlesToRelease != null)
|
|
{
|
|
foreach (var h in handlesToRelease)
|
|
{
|
|
m_TrackedInstanceOperations.Remove(h);
|
|
h.DecrementReferenceCount();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ExecuteDeferredCallbacks()
|
|
{
|
|
m_InsideExecuteDeferredCallbacksMethod = true;
|
|
for (int i = 0; i < m_DeferredCompleteCallbacks.Count; i++)
|
|
{
|
|
m_DeferredCompleteCallbacks[i].InvokeCompletionEvent();
|
|
m_DeferredCompleteCallbacks[i].DecrementReferenceCount();
|
|
}
|
|
m_DeferredCompleteCallbacks.Clear();
|
|
m_InsideExecuteDeferredCallbacksMethod = false;
|
|
}
|
|
|
|
internal void RegisterForDeferredCallback(IAsyncOperation op, bool incrementRefCount = true)
|
|
{
|
|
if (CallbackHooksEnabled && m_InsideExecuteDeferredCallbacksMethod)
|
|
{
|
|
if (m_DeferredCallbacksToRegister == null)
|
|
m_DeferredCallbacksToRegister = new List<DeferredCallbackRegisterRequest>();
|
|
m_DeferredCallbacksToRegister.Add
|
|
(
|
|
new DeferredCallbackRegisterRequest()
|
|
{
|
|
operation = op,
|
|
incrementRefCount = incrementRefCount
|
|
}
|
|
);
|
|
}
|
|
else
|
|
{
|
|
if (incrementRefCount)
|
|
op.IncrementReferenceCount();
|
|
m_DeferredCompleteCallbacks.Add(op);
|
|
RegisterForCallbacks();
|
|
}
|
|
}
|
|
|
|
internal void Update(float unscaledDeltaTime)
|
|
{
|
|
if (m_InsideUpdateMethod)
|
|
throw new Exception("Reentering the Update method is not allowed. This can happen when calling WaitForCompletion on an operation while inside of a callback.");
|
|
m_InsideUpdateMethod = true;
|
|
m_UpdateCallbacks.Invoke(unscaledDeltaTime);
|
|
m_UpdatingReceivers = true;
|
|
for (int i = 0; i < m_UpdateReceivers.Count; i++)
|
|
m_UpdateReceivers[i].Update(unscaledDeltaTime);
|
|
m_UpdatingReceivers = false;
|
|
if (m_UpdateReceiversToRemove != null)
|
|
{
|
|
foreach (var r in m_UpdateReceiversToRemove)
|
|
m_UpdateReceivers.Remove(r);
|
|
m_UpdateReceiversToRemove = null;
|
|
}
|
|
if (m_DeferredCallbacksToRegister != null)
|
|
{
|
|
foreach (DeferredCallbackRegisterRequest callback in m_DeferredCallbacksToRegister)
|
|
RegisterForDeferredCallback(callback.operation, callback.incrementRefCount);
|
|
m_DeferredCallbacksToRegister = null;
|
|
}
|
|
ExecuteDeferredCallbacks();
|
|
m_InsideUpdateMethod = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposes internal resources used by the resource manager
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (MonoBehaviourCallbackHooks.Exists && m_RegisteredForCallbacks)
|
|
{
|
|
MonoBehaviourCallbackHooks.Instance.OnUpdateDelegate -= Update;
|
|
m_RegisteredForCallbacks = false;
|
|
}
|
|
}
|
|
}
|
|
}
|