diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs b/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs index e05dcf8..32eba83 100644 --- a/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs @@ -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); } } - /// - /// 通过Unity对象加载资源。 - /// - /// ISetAssetObject。 - /// Unity对象类型。 - public async UniTaskVoid SetAssetByResources(ISetAssetObject setAssetObject,CancellationToken cancellationToken) where T : UnityEngine.Object + public async UniTaskVoid SetAssetByResources(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.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(location, linkedTokenSource.Token); - if (resource == null) - { - ClearLoadingState(target); - return; - } - - OnLoadAssetSuccess(location, resource, 0f, setAssetObject); return; } + + T resource = await _resourceModule.LoadAssetAsync(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 } } - /// - /// 取消并清理旧的加载请求。 - /// Unity对象。 - /// private void CancelAndCleanupOldRequest(UnityEngine.Object target) { if (_loadingStates.TryGetValue(target, out var oldState)) @@ -161,10 +142,6 @@ namespace AlicizaX.Resource.Runtime } } - /// - /// 清理加载状态。 - /// Unity对象。 - /// private void ClearLoadingState(UnityEngine.Object target) { if (_loadingStates.TryGetValue(target, out var state)) @@ -174,15 +151,13 @@ namespace AlicizaX.Resource.Runtime } } - /// - /// 检查指定位置是否仍是该目标的当前加载位置。 - /// 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; } - /// - /// 组件销毁时清理所有资源。 - /// private void OnDestroy() { + ReleaseTrackedAssets(); + foreach (var state in _loadingStates.Values) { MemoryPool.Release(state); @@ -212,4 +186,4 @@ namespace AlicizaX.Resource.Runtime _loadingStates.Clear(); } } -} +} \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs b/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs index 21518f7..9bb225c 100644 --- a/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs @@ -47,6 +47,8 @@ namespace AlicizaX.Resource.Runtime /// private LinkedList _loadAssetObjectsLinkedList; + private Dictionary> _trackedAssetNodes; + /// /// 散图集合对象池。 /// @@ -69,6 +71,7 @@ namespace AlicizaX.Resource.Runtime "SetAssetPool", autoReleaseInterval, 16, 60, 0); _loadAssetObjectsLinkedList = new LinkedList(); + _trackedAssetNodes = new Dictionary>(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 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(); + } } } diff --git a/Runtime/Resource/Resource/ResourceComponent.cs b/Runtime/Resource/Resource/ResourceComponent.cs index 51ab132..86e5b50 100644 --- a/Runtime/Resource/Resource/ResourceComponent.cs +++ b/Runtime/Resource/Resource/ResourceComponent.cs @@ -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 = ""; /// @@ -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..."); diff --git a/Runtime/Resource/Resource/ResourceModule.cs b/Runtime/Resource/Resource/ResourceModule.cs index 560fcdd..945a59b 100644 --- a/Runtime/Resource/Resource/ResourceModule.cs +++ b/Runtime/Resource/Resource/ResourceModule.cs @@ -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; }