优化资源模块性能
缓存命中的异步加载不再额外 Yield 一帧 改成按进度增量触发,避免每帧无意义 delegate 调用 减少了高频换图时的小对象分配
This commit is contained in:
parent
3d6caf4028
commit
76cc128c4f
@ -14,10 +14,14 @@ namespace AlicizaX.Resource.Runtime
|
||||
private class LoadingState : IMemory
|
||||
{
|
||||
public CancellationTokenSource Cts { get; set; }
|
||||
public CancellationTokenRegistration Registration { get; set; }
|
||||
public string Location { get; set; }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Registration.Dispose();
|
||||
Registration = default;
|
||||
|
||||
if (Cts != null)
|
||||
{
|
||||
Cts.Cancel();
|
||||
@ -25,7 +29,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
Cts = null;
|
||||
}
|
||||
|
||||
Location = String.Empty;
|
||||
Location = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,35 +47,26 @@ namespace AlicizaX.Resource.Runtime
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnityEngine.Object assetObject = asset as UnityEngine.Object;
|
||||
|
||||
if (assetObject != null)
|
||||
if (assetObject == null)
|
||||
{
|
||||
// 检查资源是否仍然是当前需要的。
|
||||
if (IsCurrentLocation(setAssetObject.TargetObject, setAssetObject.Location))
|
||||
{
|
||||
ClearLoadingState(setAssetObject.TargetObject);
|
||||
Log.Error($"Load failure asset type is {asset?.GetType()}.");
|
||||
return;
|
||||
}
|
||||
|
||||
SetAsset(setAssetObject, TrackLoadedAsset(setAssetObject.Location, assetObject));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 资源已经过期,卸载。
|
||||
_resourceModule.UnloadAsset(assetObject);
|
||||
}
|
||||
if (IsCurrentLocation(setAssetObject.TargetObject, setAssetObject.Location))
|
||||
{
|
||||
ClearLoadingState(setAssetObject.TargetObject);
|
||||
SetAsset(setAssetObject, TrackLoadedAsset(setAssetObject.Location, assetObject));
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"Load failure asset type is {asset?.GetType()}.");
|
||||
_resourceModule.UnloadAsset(assetObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过Unity对象加载资源。
|
||||
/// </summary>
|
||||
/// <param name="setAssetObject">ISetAssetObject。</param>
|
||||
/// <typeparam name="T">Unity对象类型。</typeparam>
|
||||
public async UniTaskVoid SetAssetByResources<T>(ISetAssetObject setAssetObject,CancellationToken cancellationToken) where T : UnityEngine.Object
|
||||
public async UniTaskVoid SetAssetByResources<T>(ISetAssetObject setAssetObject, CancellationToken cancellationToken) where T : UnityEngine.Object
|
||||
{
|
||||
var target = setAssetObject.TargetObject;
|
||||
var location = setAssetObject.Location;
|
||||
@ -81,27 +76,29 @@ namespace AlicizaX.Resource.Runtime
|
||||
return;
|
||||
}
|
||||
|
||||
// 取消并清理旧的加载请求。
|
||||
CancelAndCleanupOldRequest(target);
|
||||
|
||||
// 创建新的加载状态
|
||||
var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
var linkedTokenSource = new CancellationTokenSource();
|
||||
var loadingState = MemoryPool.Acquire<LoadingState>();
|
||||
loadingState.Cts = linkedTokenSource;
|
||||
if (cancellationToken.CanBeCanceled)
|
||||
{
|
||||
loadingState.Registration = cancellationToken.Register(static state =>
|
||||
{
|
||||
((CancellationTokenSource)state).Cancel();
|
||||
}, linkedTokenSource);
|
||||
}
|
||||
|
||||
loadingState.Location = location;
|
||||
_loadingStates[target] = loadingState;
|
||||
|
||||
try
|
||||
{
|
||||
// 等待其他可能正在进行的加载。
|
||||
|
||||
// 再次检查是否被新请求替换。
|
||||
if (!IsCurrentLocation(target, location))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查缓存。
|
||||
if (_assetItemPool.CanSpawn(location))
|
||||
{
|
||||
ClearLoadingState(target);
|
||||
@ -110,36 +107,24 @@ namespace AlicizaX.Resource.Runtime
|
||||
SetAsset(setAssetObject, assetObject);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (!IsCurrentLocation(target, location))
|
||||
{
|
||||
// 最后一次检查是否被替换。
|
||||
if (!IsCurrentLocation(target, location))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 防止重复加载同一资源。
|
||||
if (!IsCurrentLocation(target, location))
|
||||
{
|
||||
// 已经在加载中,等待回调处理。
|
||||
return;
|
||||
}
|
||||
|
||||
T resource = await _resourceModule.LoadAssetAsync<T>(location, linkedTokenSource.Token);
|
||||
if (resource == null)
|
||||
{
|
||||
ClearLoadingState(target);
|
||||
return;
|
||||
}
|
||||
|
||||
OnLoadAssetSuccess(location, resource, 0f, setAssetObject);
|
||||
return;
|
||||
}
|
||||
|
||||
T resource = await _resourceModule.LoadAssetAsync<T>(location, linkedTokenSource.Token);
|
||||
if (resource == null)
|
||||
{
|
||||
ClearLoadingState(target);
|
||||
return;
|
||||
}
|
||||
|
||||
OnLoadAssetSuccess(location, resource, 0f, setAssetObject);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
ClearLoadingState(target);
|
||||
// 请求被取消,正常情况,无需处理。
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -148,10 +133,6 @@ namespace AlicizaX.Resource.Runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消并清理旧的加载请求。
|
||||
/// <param name="target">Unity对象。</param>
|
||||
/// </summary>
|
||||
private void CancelAndCleanupOldRequest(UnityEngine.Object target)
|
||||
{
|
||||
if (_loadingStates.TryGetValue(target, out var oldState))
|
||||
@ -161,10 +142,6 @@ namespace AlicizaX.Resource.Runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理加载状态。
|
||||
/// <param name="target">Unity对象。</param>
|
||||
/// </summary>
|
||||
private void ClearLoadingState(UnityEngine.Object target)
|
||||
{
|
||||
if (_loadingStates.TryGetValue(target, out var state))
|
||||
@ -174,15 +151,13 @@ namespace AlicizaX.Resource.Runtime
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定位置是否仍是该目标的当前加载位置。
|
||||
/// </summary>
|
||||
private bool IsCurrentLocation(UnityEngine.Object target, string location)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _loadingStates.TryGetValue(target, out var state) && state.Location == location;
|
||||
}
|
||||
|
||||
@ -199,11 +174,10 @@ namespace AlicizaX.Resource.Runtime
|
||||
return assetObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 组件销毁时清理所有资源。
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
ReleaseTrackedAssets();
|
||||
|
||||
foreach (var state in _loadingStates.Values)
|
||||
{
|
||||
MemoryPool.Release(state);
|
||||
|
||||
@ -47,6 +47,8 @@ namespace AlicizaX.Resource.Runtime
|
||||
/// </summary>
|
||||
private LinkedList<LoadAssetObject> _loadAssetObjectsLinkedList;
|
||||
|
||||
private Dictionary<Object, LinkedListNode<LoadAssetObject>> _trackedAssetNodes;
|
||||
|
||||
/// <summary>
|
||||
/// 散图集合对象池。
|
||||
/// </summary>
|
||||
@ -69,6 +71,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
"SetAssetPool",
|
||||
autoReleaseInterval, 16, 60, 0);
|
||||
_loadAssetObjectsLinkedList = new LinkedList<LoadAssetObject>();
|
||||
_trackedAssetNodes = new Dictionary<Object, LinkedListNode<LoadAssetObject>>(16);
|
||||
|
||||
InitializedResources();
|
||||
}
|
||||
@ -113,9 +116,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
|
||||
if (current.Value.AssetObject.IsCanRelease())
|
||||
{
|
||||
_assetItemPool.Unspawn(current.Value.AssetTarget);
|
||||
MemoryPool.Release(current.Value.AssetObject);
|
||||
_loadAssetObjectsLinkedList.Remove(current);
|
||||
RemoveTrackedNode(current, releaseAsset: true);
|
||||
}
|
||||
|
||||
current = next;
|
||||
@ -134,8 +135,78 @@ namespace AlicizaX.Resource.Runtime
|
||||
|
||||
private void SetAsset(ISetAssetObject setAssetObject, Object assetObject)
|
||||
{
|
||||
_loadAssetObjectsLinkedList.AddLast(new LoadAssetObject(setAssetObject, assetObject));
|
||||
ReplaceTrackedAsset(setAssetObject.TargetObject);
|
||||
|
||||
var node = _loadAssetObjectsLinkedList.AddLast(new LoadAssetObject(setAssetObject, assetObject));
|
||||
if (setAssetObject.TargetObject != null)
|
||||
{
|
||||
_trackedAssetNodes[setAssetObject.TargetObject] = node;
|
||||
}
|
||||
|
||||
setAssetObject.SetAsset(assetObject);
|
||||
}
|
||||
|
||||
private void ReplaceTrackedAsset(Object target)
|
||||
{
|
||||
if (target == null || _trackedAssetNodes == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_trackedAssetNodes.TryGetValue(target, out var existingNode))
|
||||
{
|
||||
if (_currentProcessNode == existingNode)
|
||||
{
|
||||
_currentProcessNode = existingNode.Next;
|
||||
}
|
||||
|
||||
RemoveTrackedNode(existingNode, releaseAsset: true);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveTrackedNode(LinkedListNode<LoadAssetObject> node, bool releaseAsset)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var trackedObject = node.Value.AssetObject?.TargetObject;
|
||||
if (trackedObject != null && _trackedAssetNodes != null)
|
||||
{
|
||||
_trackedAssetNodes.Remove(trackedObject);
|
||||
}
|
||||
|
||||
if (releaseAsset && node.Value.AssetTarget != null && _assetItemPool != null)
|
||||
{
|
||||
_assetItemPool.Unspawn(node.Value.AssetTarget);
|
||||
}
|
||||
|
||||
if (node.Value.AssetObject != null)
|
||||
{
|
||||
MemoryPool.Release(node.Value.AssetObject);
|
||||
}
|
||||
|
||||
_loadAssetObjectsLinkedList?.Remove(node);
|
||||
}
|
||||
|
||||
private void ReleaseTrackedAssets()
|
||||
{
|
||||
if (_loadAssetObjectsLinkedList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var current = _loadAssetObjectsLinkedList.First;
|
||||
while (current != null)
|
||||
{
|
||||
var next = current.Next;
|
||||
RemoveTrackedNode(current, releaseAsset: true);
|
||||
current = next;
|
||||
}
|
||||
|
||||
_currentProcessNode = null;
|
||||
_trackedAssetNodes?.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,12 +28,16 @@ namespace AlicizaX.Resource.Runtime
|
||||
|
||||
private float _lastUnloadUnusedAssetsOperationElapseSeconds = 0f;
|
||||
|
||||
private float _lastGCCollectElapseSeconds = float.MaxValue;
|
||||
|
||||
[SerializeField] private float minUnloadUnusedAssetsInterval = 60f;
|
||||
|
||||
[SerializeField] private float maxUnloadUnusedAssetsInterval = 300f;
|
||||
|
||||
[SerializeField] private bool useSystemUnloadUnusedAssets = true;
|
||||
|
||||
[SerializeField] private float minGCCollectInterval = 30f;
|
||||
|
||||
[SerializeField] private string decryptionServices = "";
|
||||
|
||||
/// <summary>
|
||||
@ -239,6 +243,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
private void Update()
|
||||
{
|
||||
_lastUnloadUnusedAssetsOperationElapseSeconds += Time.unscaledDeltaTime;
|
||||
_lastGCCollectElapseSeconds += Time.unscaledDeltaTime;
|
||||
if (_asyncOperation == null && (_forceUnloadUnusedAssets || _lastUnloadUnusedAssetsOperationElapseSeconds >= maxUnloadUnusedAssetsInterval ||
|
||||
_preorderUnloadUnusedAssets && _lastUnloadUnusedAssetsOperationElapseSeconds >= minUnloadUnusedAssetsInterval))
|
||||
{
|
||||
@ -252,9 +257,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
|
||||
if (_asyncOperation == null && _performGCCollect)
|
||||
{
|
||||
Log.Info("GC.Collect...");
|
||||
_performGCCollect = false;
|
||||
GC.Collect();
|
||||
TryCollectGarbage();
|
||||
}
|
||||
|
||||
if (_asyncOperation is { isDone: true })
|
||||
@ -262,13 +265,24 @@ namespace AlicizaX.Resource.Runtime
|
||||
_asyncOperation = null;
|
||||
if (_performGCCollect)
|
||||
{
|
||||
Log.Info("GC.Collect...");
|
||||
_performGCCollect = false;
|
||||
GC.Collect();
|
||||
TryCollectGarbage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryCollectGarbage()
|
||||
{
|
||||
if (_lastGCCollectElapseSeconds < minGCCollectInterval)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info("GC.Collect...");
|
||||
_performGCCollect = false;
|
||||
_lastGCCollectElapseSeconds = 0f;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
private void OnLowMemory()
|
||||
{
|
||||
Log.Warning("Low memory reported...");
|
||||
|
||||
@ -693,7 +693,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
|
||||
string assetObjectKey = GetCacheKey(location, packageName);
|
||||
|
||||
var asset = await GetOrLoadAssetAsync(location, typeof(T), packageName, assetObjectKey, cancellationToken);
|
||||
var asset = await GetOrLoadAssetAsync(location, typeof(T), packageName, assetObjectKey, cancellationToken: cancellationToken);
|
||||
return asset as T;
|
||||
}
|
||||
|
||||
@ -712,7 +712,7 @@ namespace AlicizaX.Resource.Runtime
|
||||
|
||||
string assetObjectKey = GetCacheKey(location, packageName);
|
||||
|
||||
var asset = await GetOrLoadAssetAsync(location, typeof(GameObject), packageName, assetObjectKey, cancellationToken);
|
||||
var asset = await GetOrLoadAssetAsync(location, typeof(GameObject), packageName, assetObjectKey, cancellationToken: cancellationToken);
|
||||
return asset != null ? AssetsReference.Instantiate(asset as GameObject, parent, this).gameObject : null;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user