From b983e85416ec8f4f15191fad6705fc3d87ec7947 Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Fri, 13 Oct 2023 15:53:16 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=A8=A1=E5=9D=97=E4=BC=98?= =?UTF-8?q?=E5=8C=96-=E4=BD=BF=E7=94=A8=E8=B5=84=E6=BA=90=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E8=A1=A8=E4=BB=A5=E5=8F=8A=E8=B5=84=E6=BA=90=E6=B7=98?= =?UTF-8?q?=E6=B1=B0=E7=AE=97=E6=B3=95=EF=BC=8C=E7=BC=93=E5=AD=98=E5=B8=B8?= =?UTF-8?q?=E7=94=A8=E8=B5=84=E6=BA=90=EF=BC=8C=E5=88=A9=E4=BA=8E=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E3=80=81=E6=B7=98=E6=B1=B0=E4=B8=8D=E5=B8=B8=E7=94=A8?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 资源模块优化-使用资源缓存表以及资源淘汰算法,缓存常用资源,利于获取、淘汰不常用资源。 --- .../GameLogic/Common/RedNote/RedNoteWidget.cs | 3 +- .../Inspector/ResourceModuleInspector.cs | 15 + .../Runtime/Extension/UnityExtension.cs | 10 +- .../Modules/ResourceModule/ArcCacheTable.cs | 276 ++++++++++++++++++ .../ResourceModule/ArcCacheTable.cs.meta | 3 + .../Modules/ResourceModule/AssetGroup.cs | 27 +- .../Modules/ResourceModule/AssetReference.cs | 5 +- .../ResourceModule/IResourceManager.cs | 5 + .../Modules/ResourceModule/QueueNode.cs | 82 ++++++ .../Modules/ResourceModule/QueueNode.cs.meta | 3 + .../Modules/ResourceModule/ResourceManager.cs | 103 ++++++- .../Modules/ResourceModule/ResourceModule.cs | 32 +- .../Runtime/Modules/UIModule/UIBase.cs | 178 +---------- .../Runtime/Modules/UIModule/UIWidget.cs | 16 +- .../Runtime/Modules/UIModule/UIWindow.cs | 27 +- 15 files changed, 523 insertions(+), 262 deletions(-) create mode 100644 UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs create mode 100644 UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs.meta create mode 100644 UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs create mode 100644 UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs.meta diff --git a/UnityProject/Assets/GameScripts/HotFix/GameLogic/Common/RedNote/RedNoteWidget.cs b/UnityProject/Assets/GameScripts/HotFix/GameLogic/Common/RedNote/RedNoteWidget.cs index da0f3001..9313425d 100644 --- a/UnityProject/Assets/GameScripts/HotFix/GameLogic/Common/RedNote/RedNoteWidget.cs +++ b/UnityProject/Assets/GameScripts/HotFix/GameLogic/Common/RedNote/RedNoteWidget.cs @@ -42,8 +42,7 @@ namespace GameLogic public void SetSprite(string sprite) { - m_image.sprite = LoadAsset(sprite); - m_image.SetNativeSize(); + m_image.SetSprite(sprite); } public override void OnUpdate() diff --git a/UnityProject/Assets/TEngine/Editor/Inspector/ResourceModuleInspector.cs b/UnityProject/Assets/TEngine/Editor/Inspector/ResourceModuleInspector.cs index 637ac3e1..6d9165a2 100644 --- a/UnityProject/Assets/TEngine/Editor/Inspector/ResourceModuleInspector.cs +++ b/UnityProject/Assets/TEngine/Editor/Inspector/ResourceModuleInspector.cs @@ -34,6 +34,7 @@ namespace TEngine.Editor.Inspector private SerializedProperty m_MaxUnloadUnusedAssetsInterval = null; private SerializedProperty m_DownloadingMaxNum = null; private SerializedProperty m_FailedTryAgain = null; + private SerializedProperty m_adaptiveReplacementCacheCapacity = null; private int m_ResourceModeIndex = 0; private int m_PackageIndex = 0; @@ -54,6 +55,7 @@ namespace TEngine.Editor.Inspector m_MaxUnloadUnusedAssetsInterval = serializedObject.FindProperty("maxUnloadUnusedAssetsInterval"); m_DownloadingMaxNum = serializedObject.FindProperty("downloadingMaxNum"); m_FailedTryAgain = serializedObject.FindProperty("failedTryAgain"); + m_adaptiveReplacementCacheCapacity = serializedObject.FindProperty("adaptiveReplacementCacheCapacity"); RefreshModes(); RefreshTypeNames(); @@ -199,6 +201,19 @@ namespace TEngine.Editor.Inspector m_FailedTryAgain.intValue = (int)failedTryAgain; } } + + int adaptiveReplacementCacheCapacity = (int)EditorGUILayout.Slider("ARC Table Capacity", m_adaptiveReplacementCacheCapacity.intValue, 8f, 128f); + if (adaptiveReplacementCacheCapacity != m_adaptiveReplacementCacheCapacity.intValue) + { + if (EditorApplication.isPlaying) + { + t.adaptiveReplacementCacheCapacity = adaptiveReplacementCacheCapacity; + } + else + { + m_adaptiveReplacementCacheCapacity.intValue = adaptiveReplacementCacheCapacity; + } + } if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject)) diff --git a/UnityProject/Assets/TEngine/Runtime/Extension/UnityExtension.cs b/UnityProject/Assets/TEngine/Runtime/Extension/UnityExtension.cs index eb56069e..98760532 100644 --- a/UnityProject/Assets/TEngine/Runtime/Extension/UnityExtension.cs +++ b/UnityProject/Assets/TEngine/Runtime/Extension/UnityExtension.cs @@ -159,7 +159,7 @@ namespace TEngine { if (image == null) { - goto Dispose; + return; } image.sprite = operation.AssetObject as Sprite; @@ -167,9 +167,6 @@ namespace TEngine { image.SetNativeSize(); } - - Dispose: - operation.Dispose(); }); } } @@ -204,13 +201,10 @@ namespace TEngine { if (spriteRenderer == null) { - goto Dispose; + return; } spriteRenderer.sprite = operation.AssetObject as Sprite; - - Dispose: - operation.Dispose(); }); } } diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs new file mode 100644 index 00000000..34469292 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Debug = UnityEngine.Debug; + +namespace TEngine +{ + /// + /// Adaptive Replacement Cache缓存表。 + /// + /// Adaptive Replacement CacheKey。 + /// Adaptive Replacement CacheValue + /// 缓存替换策略(Cache Replacement Policy)- ARC(Adaptive Replacement Cache)。 + public class ArcCacheTable + { + /// + /// ARC缓存表哈希表。 + /// Dictionary 中存储 key 和 QueueNode 中节点的映射关系。 + /// 算法优化的关键在于如何降低链表的删除操作的时间复杂度。不管是在插入、删除、查找缓存的时候,都可以通过这种联系来将时间复杂度降低到 O(1)。 + /// + protected readonly Dictionary> CacheStorageMap; + + private readonly QueueNode _t1Head; + private readonly QueueNode _t2Head; + private readonly QueueNode _b1Head; + private readonly QueueNode _b2Head; + + private int _adaptiveParameter; + private int _t1Size; + private int _t2Size; + private int _b1Size; + private int _b2Size; + protected readonly int Capacity; + + public delegate void OnAdd(TKey key,TValue data); + + public delegate void OnRemove(TKey key,TValue data); + + /// + /// ARC缓存表添加成功回调。 + /// + public readonly OnAdd OnAddCallback; + + /// + /// ARC缓存表移除回调。 + /// + public readonly OnRemove OnRemoveCallback; + + /// + /// ARC缓存表哈希表构造。 + /// + /// 容量。 + /// LRU缓存表添加成功回调。 + /// LRU缓存表移除回调。 + public ArcCacheTable(int capacity, OnAdd onAdd = null, OnRemove onRemove = null) + { + Capacity = capacity; + OnAddCallback = onAdd; + OnRemoveCallback = onRemove; + CacheStorageMap = new Dictionary>(); + _t1Head = new QueueNode(); + _t2Head = new QueueNode(); + _b1Head = new QueueNode(); + _b2Head = new QueueNode(); + } + + /// + /// 对象推入ARC缓存表。 + /// + /// 键。 + /// 值。 + public void PutCache(TKey key, TValue value) + { + CacheStorageMap.TryGetValue(key, out QueueNode queueNode); + + if (queueNode == null) + { + OnMissOnAllQueue(key, value); + OnAddCallback?.Invoke(key,value); + } + else if (queueNode.QueueType == QueueType.B1) + { + queueNode.Set(value); + OnHitOnB1(queueNode); + } + else if (queueNode.QueueType == QueueType.B2) + { + queueNode.Set(value); + OnHitOnB2(queueNode); + } + else + { + queueNode.Set(value); + OnHitOnT1orT2(queueNode); + } + } + + /// + /// 从ARC缓存表取出对象。 + /// + /// 键。 + /// TValue from cache if exists or null。 + public TValue GetCache(TKey key) + { + CacheStorageMap.TryGetValue(key, out QueueNode queueNode); + + if (queueNode == null) + { + return default; + } + else + { + return queueNode.Get(); + } + } + + /// + /// 在所有队列中未命中时执行任务(Case:Key不在(T1 u B1 u T2 u B2))。 + /// + /// key cache key。 + /// value to inset in cache。 + private void OnMissOnAllQueue(TKey key, TValue value) + { + QueueNode queueNode = new QueueNode(key, value); + queueNode.QueueType = QueueType.T1; + + int sizeL1 = _t1Size + _b1Size; + int sizeL2 = _t2Size + _b2Size; + if (sizeL1 == Capacity) + { + if (_t1Size < Capacity) + { + QueueNode queueNodeToBeRemoved = _b1Head.Next; + RemoveFromQueue(queueNodeToBeRemoved); + queueNodeToBeRemoved.Remove(); + _b1Size--; + + Replace(queueNode); + } + else + { + QueueNode queueNodeToBeRemoved = _t1Head.Next; + RemoveFromQueue(queueNodeToBeRemoved); + queueNodeToBeRemoved.Remove(); + _t1Size--; + } + } + else if (sizeL1 < Capacity && sizeL1 + sizeL2 >= Capacity) + { + if (sizeL1 + sizeL2 >= 2 * Capacity) + { + QueueNode queueNodeToBeRemoved = _b2Head.Next; + RemoveFromQueue(queueNodeToBeRemoved); + queueNodeToBeRemoved.Remove(); + _b2Size--; + } + + Replace(queueNode); + } + + _t1Size++; + CacheStorageMap.Add(key, queueNode); + queueNode.AddToLast(_t1Head); + } + + /// + /// 执行任务命中B1 (Case:Key在B1中)。 + /// + /// queueNode queue node。 + private void OnHitOnB1(QueueNode queueNode) + { + _adaptiveParameter = Math.Min(Capacity, _adaptiveParameter + Math.Max(_b2Size / _b1Size, 1)); + Replace(queueNode); + + _t2Size++; + _b1Size--; + queueNode.Remove(); + queueNode.QueueType = QueueType.T2; + queueNode.AddToLast(_t2Head); + } + + /// + /// 执行任务命中B2 (Case:Key在B2中)。 + /// + /// queueNode queue node。 + private void OnHitOnB2(QueueNode queueNode) + { + _adaptiveParameter = Math.Max(0, _adaptiveParameter - Math.Max(_b1Size / _b2Size, 1)); + Replace(queueNode); + + _t2Size++; + _b2Size--; + queueNode.Remove(); + queueNode.QueueType = QueueType.T2; + queueNode.AddToLast(_t2Head); + } + + /// + /// 执行任务命中T1或者T2 (Case:Key在T1或者T2中)。 + /// + /// queueNode queue node。 + private void OnHitOnT1orT2(QueueNode queueNode) + { + if (queueNode.QueueType == QueueType.T1) + { + _t1Size--; + _t2Size++; + } + + queueNode.Remove(); + queueNode.QueueType = QueueType.T2; + queueNode.AddToLast(_t2Head); + } + + /// + /// 替换队列节点(情况:L1(T1 u B1)少于c个) + /// + /// queueNode queue node。 + private void Replace(QueueNode queueNode) + { + if (_t1Size >= 1 && ((queueNode.QueueType == QueueType.B2 && _t1Size == _adaptiveParameter) || _t1Size > _adaptiveParameter)) + { + QueueNode queueNodeToBeRemoved = _t1Head.Next; + queueNodeToBeRemoved.Remove(); + queueNodeToBeRemoved.QueueType = QueueType.B1; + queueNodeToBeRemoved.AddToLast(_b1Head); + _t1Size--; + _b1Size++; + } + else + { + QueueNode queueNodeToBeRemoved = _t2Head.Next; + queueNodeToBeRemoved.Remove(); + queueNodeToBeRemoved.QueueType = QueueType.B2; + queueNodeToBeRemoved.AddToLast(_b2Head); + _t2Size--; + _b2Size++; + } + } + + + /// + /// Remove TValue data from queue and dispose it + /// + /// queueNodeToBeRemoved queue node to be remove from queue + public void RemoveFromQueue(QueueNode queueNodeToBeRemoved) + { + CacheStorageMap.Remove(queueNodeToBeRemoved.Key); + TValue value = queueNodeToBeRemoved.Get(); + try + { + OnRemoveCallback?.Invoke(queueNodeToBeRemoved.Key,value); + } + catch (Exception e) + { + Debug.Log(e); + } + } + + [Conditional("UNITY_EDITOR")] + public void PrintCacheIdsFromQueue() + { + String keys = ""; + foreach (var queueNode in CacheStorageMap) + { + var key = queueNode.Key; + if (keys == "") + keys += key; + else + keys += " | " + key; + } + + Debug.Log("All Existing Keys in Cache are : " + keys); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs.meta b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs.meta new file mode 100644 index 00000000..dd5d8c2b --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ArcCacheTable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2486d1f34ad744b7bb19b1b220952ee9 +timeCreated: 1697174054 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetGroup.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetGroup.cs index 131d41fa..9a6f0ae1 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetGroup.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetGroup.cs @@ -245,7 +245,7 @@ namespace TEngine } } - AssetOperationHandle handle = GameModule.Resource.LoadAssetGetOperation(assetName); + AssetOperationHandle handle = YooAssets.LoadAssetSync(assetName); Reference(handle); @@ -289,7 +289,7 @@ namespace TEngine } } - assetOperationHandle = GameModule.Resource.LoadAssetGetOperation(assetName); + assetOperationHandle = YooAssets.LoadAssetSync(assetName); Reference(assetOperationHandle); @@ -334,7 +334,7 @@ namespace TEngine } } - assetOperationHandle = GameModule.Resource.LoadAssetGetOperation(assetName); + assetOperationHandle = YooAssets.LoadAssetSync(assetName); Reference(assetOperationHandle); @@ -354,11 +354,10 @@ namespace TEngine /// /// 要加载的实例名称。 /// 取消操作Token。 - /// 资源操作句柄。 /// 资源实实例。 // ReSharper disable once UnusedParameter.Global // ReSharper disable once RedundantAssignment - public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken,AssetOperationHandle assetOperationHandle = null) where T : Object + public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken = default) where T : Object { if (string.IsNullOrEmpty(assetName)) { @@ -379,7 +378,7 @@ namespace TEngine } } - AssetOperationHandle handle = GameModule.Resource.LoadAssetAsyncHandle(assetName); + AssetOperationHandle handle = YooAssets.LoadAssetAsync(assetName); Reference(handle); @@ -391,8 +390,6 @@ namespace TEngine return null; } - - assetOperationHandle = handle; if (typeof(T) == typeof(GameObject)) { @@ -411,21 +408,9 @@ namespace TEngine /// 要加载的游戏物体名称。 /// 取消操作Token。 /// 异步游戏物体实例。 - public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken) + public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken = default) { return await LoadAssetAsync(assetName,cancellationToken); } - - /// - /// 异步加载游戏物体。 - /// - /// 要加载的游戏物体名称。 - /// 取消操作Token。 - /// 资源操作句柄。 - /// 异步游戏物体实例。 - public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken,AssetOperationHandle assetOperationHandle) - { - return await LoadAssetAsync(assetName,cancellationToken,assetOperationHandle); - } } } \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetReference.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetReference.cs index 922486c5..f29b0712 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetReference.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/AssetReference.cs @@ -195,8 +195,7 @@ namespace TEngine /// 要加载的实例名称。 /// 取消操作Token。 /// 资源实实例。 - public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken) - where T : Object + public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken = default) where T : Object { DirtyInitAssetGroup(); return await _assetGroup.LoadAssetAsync(assetName, cancellationToken); @@ -208,7 +207,7 @@ namespace TEngine /// 要加载的游戏物体名称。 /// 取消操作Token。 /// 异步游戏物体实例。 - public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken) + public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken = default) { DirtyInitAssetGroup(); return await _assetGroup.LoadGameObjectAsync(assetName, cancellationToken); diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/IResourceManager.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/IResourceManager.cs index f637095f..89f6cbea 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/IResourceManager.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/IResourceManager.cs @@ -61,6 +61,11 @@ namespace TEngine /// 获取或设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒)。 /// long Milliseconds { get; set; } + + /// + /// 资源缓存表容量。 + /// + int ARCTableCapacity { get; set; } /// /// 设置资源只读区路径。 diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs new file mode 100644 index 00000000..53d665b9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs @@ -0,0 +1,82 @@ +namespace TEngine +{ + /// + /// 队列类型。 + /// T1, recent cache entries. + /// T2, ghost entries recently evicted from the T1 cache. + /// B1, frequent entries. + /// B2, ghost entries recently evicted from the T2 cache. + /// + /// + public enum QueueType + { + None, + /// + /// T1, recent cache entries. + /// + T1, + /// + /// B1, frequent entries. + /// + B1, + /// + /// T2, ghost entries recently evicted from the T1 cache. + /// + T2, + /// + /// B2, ghost entries recently evicted from the T2 cache. + /// + B2 + } + + public class QueueNode + { + public readonly TKey Key; + public QueueNode Prev; + public QueueNode Next; + public QueueType QueueType; + public TValue Value; + + public QueueNode() + { + this.Prev = this; + this.Next = this; + } + + public QueueNode(TKey key, TValue data) + { + this.Key = key; + this.Value = data; + } + + public TValue Get() + { + return Value; + } + + public void Set(TValue value) + { + this.Value = value; + } + + public void AddToLast(QueueNode head) + { + QueueNode tail = head.Prev; + head.Prev = this; + tail.Next = this; + Next = head; + Prev = tail; + } + + public void Remove() + { + if (Prev != null && Next != null) + { + Prev.Next = Next; + Next.Prev = Prev; + Prev = Next = null; + QueueType = QueueType.None; + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs.meta b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs.meta new file mode 100644 index 00000000..2bfbcbde --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/QueueNode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9a9c1bf1a1e443eaaf17b46fe6b29277 +timeCreated: 1697174054 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceManager.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceManager.cs index 8c2a6386..63ee32e5 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceManager.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceManager.cs @@ -86,6 +86,10 @@ namespace TEngine /// 优先级较高的模块会优先轮询,并且关闭操作会后进行。 internal override int Priority => 4; + /// + /// 资源缓存表容量。 + /// + public int ARCTableCapacity { get; set; } #endregion #region 生命周期 @@ -135,6 +139,66 @@ namespace TEngine #endregion + private Dictionary _releaseMaps; + + private ArcCacheTable _arcCacheTable; + + private void OnAddAsset(string location,AssetOperationHandle handle) + { + if (_releaseMaps.ContainsKey(location)) + { + _releaseMaps.Remove(location); + } + } + + private void OnRemoveAsset(string location,AssetOperationHandle handle) + { + _releaseMaps[location] = handle; + GameModule.Resource.UnloadUnusedAssets(performGCCollect:false); + } + + /// + /// 从缓存中获取同步资源句柄。 + /// + /// 资源定位地址。 + /// 资源类型。 + /// 资源句柄。 + private AssetOperationHandle GetHandleSync(string location) where T : Object + { + AssetOperationHandle handle = null; + // 尝试从从ARC缓存表取出对象。 + handle = _arcCacheTable.GetCache(location); + + if (handle == null) + { + handle = YooAssets.LoadAssetSync(location); + } + // 对象推入ARC缓存表。 + _arcCacheTable.PutCache(location, handle); + return handle; + } + + /// + /// 从缓存中获取异步资源句柄。 + /// + /// 资源定位地址。 + /// 资源类型。 + /// 资源句柄。 + private AssetOperationHandle GetHandleAsync(string location) where T : Object + { + AssetOperationHandle handle = null; + // 尝试从从ARC缓存表取出对象。 + handle = _arcCacheTable.GetCache(location); + + if (handle == null) + { + handle = YooAssets.LoadAssetAsync(location); + } + // 对象推入ARC缓存表。 + _arcCacheTable.PutCache(location, handle); + return handle; + } + /// /// 初始化资源模块。 /// @@ -155,6 +219,9 @@ namespace TEngine } ResourcePool.Initialize(GameModule.Get().gameObject); + + _releaseMaps ??= new Dictionary(ARCTableCapacity); + _arcCacheTable ??= new ArcCacheTable(ARCTableCapacity, OnAddAsset, OnRemoveAsset); } /// @@ -240,6 +307,19 @@ namespace TEngine /// public void UnloadUnusedAssets() { + var iter = _releaseMaps.Values.GetEnumerator(); + while (iter.MoveNext()) + { + AssetOperationHandle handle = iter.Current; + if (handle != null) + { + handle.Dispose(); + handle = null; + } + } + iter.Dispose(); + _releaseMaps.Clear(); + if (DefaultPackage == null) { throw new GameFrameworkException("Package is invalid."); @@ -381,18 +461,16 @@ namespace TEngine return default; } - AssetOperationHandle handle = YooAssets.LoadAssetSync(location); + AssetOperationHandle handle = GetHandleSync(location); if (typeof(T) == typeof(GameObject)) { GameObject ret = handle.InstantiateSync(); - AssetReference.BindAssetReference(ret, handle, location); return ret as T; } else { T ret = handle.AssetObject as T; - handle.Dispose(); return ret; } } @@ -412,18 +490,16 @@ namespace TEngine return default; } - AssetOperationHandle handle = YooAssets.LoadAssetSync(location); + AssetOperationHandle handle = GetHandleSync(location); if (typeof(T) == typeof(GameObject)) { GameObject ret = handle.InstantiateSync(parent); - AssetReference.BindAssetReference(ret, handle, location); return ret as T; } else { T ret = handle.AssetObject as T; - handle.Dispose(); return ret; } } @@ -437,8 +513,7 @@ namespace TEngine /// 资源实例。 public T LoadAsset(string location, out AssetOperationHandle handle) where T : Object { - handle = YooAssets.LoadAssetSync(location); - + handle = GetHandleSync(location); if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); @@ -466,8 +541,8 @@ namespace TEngine /// 资源实例。 public T LoadAsset(string location, Transform parent, out AssetOperationHandle handle) where T : Object { - handle = YooAssets.LoadAssetSync(location); - + handle = GetHandleSync(location); + if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); @@ -493,7 +568,7 @@ namespace TEngine /// 同步加载资源句柄。 public AssetOperationHandle LoadAssetGetOperation(string location) where T : Object { - return YooAssets.LoadAssetSync(location); + return GetHandleSync(location); } /// @@ -504,7 +579,7 @@ namespace TEngine /// 异步加载资源句柄。 public AssetOperationHandle LoadAssetAsyncHandle(string location) where T : Object { - return YooAssets.LoadAssetAsync(location); + return GetHandleAsync(location); } /// @@ -602,8 +677,6 @@ namespace TEngine { GameObject ret = handle.InstantiateSync(); - AssetReference.BindAssetReference(ret, handle, location); - return ret as T; } else @@ -631,8 +704,6 @@ namespace TEngine GameObject ret = handle.InstantiateSync(); - AssetReference.BindAssetReference(ret, handle, location); - return ret; } diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceModule.cs b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceModule.cs index 53e0b819..001f2c79 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceModule.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/ResourceModule/ResourceModule.cs @@ -98,6 +98,11 @@ namespace TEngine public int downloadingMaxNum = 3; public int failedTryAgain = 3; + /// + /// 资源缓存表容量。 + /// + public int adaptiveReplacementCacheCapacity = 32; + private IResourceManager m_ResourceManager; private AsyncOperation m_AsyncOperation = null; private bool m_ForceUnloadUnusedAssets = false; @@ -214,6 +219,7 @@ namespace TEngine m_ResourceManager.PlayMode = playMode; m_ResourceManager.VerifyLevel = verifyLevel; m_ResourceManager.Milliseconds = milliseconds; + m_ResourceManager.ARCTableCapacity = adaptiveReplacementCacheCapacity; m_ResourceManager.Initialize(); Log.Info($"ResourceModule Run Mode:{playMode}"); if (playMode == EPlayMode.EditorSimulateMode && !m_InitPackageByProcedure) @@ -294,9 +300,27 @@ namespace TEngine /// public void ClearSandbox() { - // TODO // YooAssets.ClearSandbox(); } + + /// + /// 卸载资源。 + /// + /// 要卸载的资源。 + public void UnloadAsset(object asset) + { + m_ResourceManager.UnloadAsset(asset); + } + + /// + /// 预订执行释放未被使用的资源。 + /// + /// 是否使用垃圾回收。 + public void UnloadUnusedAssets(bool performGCCollect) + { + m_PreorderUnloadUnusedAssets = true; + m_PerformGCCollect = performGCCollect; + } /// /// 强制执行释放未被使用的资源。 @@ -317,8 +341,10 @@ namespace TEngine private void Update() { m_LastUnloadUnusedAssetsOperationElapseSeconds += GameTime.unscaledDeltaTime; - if (m_AsyncOperation == null && (m_ForceUnloadUnusedAssets || m_LastUnloadUnusedAssetsOperationElapseSeconds >= maxUnloadUnusedAssetsInterval || - m_PreorderUnloadUnusedAssets && m_LastUnloadUnusedAssetsOperationElapseSeconds >= minUnloadUnusedAssetsInterval)) + if (m_AsyncOperation == null && + (m_ForceUnloadUnusedAssets || + m_LastUnloadUnusedAssetsOperationElapseSeconds >= maxUnloadUnusedAssetsInterval || + m_PreorderUnloadUnusedAssets && m_LastUnloadUnusedAssetsOperationElapseSeconds >= minUnloadUnusedAssetsInterval)) { Log.Info("Unload unused assets..."); m_ForceUnloadUnusedAssets = false; diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIBase.cs b/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIBase.cs index 1e3532ad..1d618716 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIBase.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIBase.cs @@ -73,11 +73,6 @@ namespace TEngine /// public AssetOperationHandle Handle { protected set; get; } - /// - /// 资源引用数据。 - /// - public AssetReference AssetReference { protected set; get; } - /// /// 资源是否准备完毕。 /// @@ -316,13 +311,7 @@ namespace TEngine /// UIWidget实例。 public T CreateWidgetByPath(Transform parentTrans, string assetLocation, bool visible = true) where T : UIWidget, new() { - if (AssetReference == null) - { - Log.Fatal($"CreateWidgetByPath Failed => {this}.AssetReference is null"); - return null; - } - - GameObject goInst = AssetReference.LoadAsset(assetLocation, parentTrans); + GameObject goInst = GameModule.Resource.LoadAsset(assetLocation, parentTrans); return CreateWidget(goInst, visible); } @@ -336,13 +325,7 @@ namespace TEngine /// UIWidget实例。 public async UniTask CreateWidgetByPathAsync(Transform parentTrans, string assetLocation, bool visible = true) where T : UIWidget, new() { - if (AssetReference == null) - { - Log.Fatal($"CreateWidgetByPath Failed => {this}.AssetReference is null"); - return null; - } - - GameObject goInst = await AssetReference.LoadAssetAsync(assetLocation, gameObject.GetCancellationTokenOnDestroy()); + GameObject goInst = await GameModule.Resource.LoadAssetAsync(assetLocation, gameObject.GetCancellationTokenOnDestroy()); goInst.transform.SetParent(parentTrans); return CreateWidget(goInst, visible); } @@ -531,163 +514,6 @@ namespace TEngine #endregion - #region AssetRefrence Methods - - /// - /// 引用资源数据到资源组内。 - /// - /// 资源操作句柄。 - /// 资源标识。 - /// 是否注册成功。 - public bool Reference(AssetOperationHandle handle, string assetTag = "") - { - if (AssetReference == null) - { - Log.Fatal($"Reference Failed => {this}.AssetReference is null"); - return false; - } - - return AssetReference.Reference(handle, assetTag); - } - - /// - /// 引用资源数据到资源组内。 - /// - /// 资源定位地址。 - /// 资源操作句柄。 - /// 资源标识。 - /// 是否注册成功。 - public bool Reference(string address, AssetOperationHandle handle, string assetTag = "") - { - if (AssetReference == null) - { - Log.Fatal($"Reference Failed => {this}.AssetReference is null"); - return false; - } - - return AssetReference.Reference(address, handle, assetTag); - } - - /// - /// 从资源组内释放资源数据。 - /// - /// - /// - public bool ReleaseByTag(string assetTag) - { - if (AssetReference == null) - { - Log.Fatal($"Release Failed => {this}.AssetReference is null"); - return false; - } - - return AssetReference.ReleaseByTag(assetTag); - } - - /// - /// 从资源组内释放资源数据。 - /// - /// - /// - public bool Release(AssetOperationHandle handle) - { - if (AssetReference == null) - { - Log.Fatal($"Release Failed => {this}.AssetReference is null"); - return false; - } - - return AssetReference.Release(handle); - } - - /// - /// 从资源组内释放资源数据。 - /// - /// 资源定位地址。 - /// 是否释放成功。 - public bool Release(string address) - { - if (AssetReference == null) - { - Log.Fatal($"Release Failed => {this}.AssetReference is null"); - return false; - } - - return AssetReference.Release(address); - } - - /// - /// 同步加载资源。 - /// - /// 要加载资源的名称。 - /// 要加载资源的类型。 - /// 资源实例。 - public T LoadAsset(string assetName) where T : UnityEngine.Object - { - if (AssetReference == null) - { - Log.Fatal($"LoadAsset Failed => {this}.AssetReference is null"); - return default; - } - - return AssetReference.LoadAsset(assetName); - } - - /// - /// 同步加载资源。 - /// - /// 要加载资源的名称。 - /// 父节点位置。 - /// 要加载资源的类型。 - /// 资源实例。 - public T LoadAsset(string assetName, Transform parentTrans) where T : UnityEngine.Object - { - if (AssetReference == null) - { - Log.Fatal($"LoadAsset Failed => {this}.AssetReference is null"); - return default; - } - - return AssetReference.LoadAsset(assetName, parentTrans); - } - - /// - /// 异步加载资源实例。 - /// - /// 要加载的实例名称。 - /// 取消操作Token。 - /// 资源实实例。 - public async UniTask LoadAssetAsync(string assetName, CancellationToken cancellationToken) - where T : UnityEngine.Object - { - if (AssetReference == null) - { - Log.Fatal($"LoadAssetAsync Failed => {this}.AssetReference is null"); - return default; - } - - return await AssetReference.LoadAssetAsync(assetName, cancellationToken); - } - - /// - /// 异步加载游戏物体。 - /// - /// 要加载的游戏物体名称。 - /// 取消操作Token。 - /// 异步游戏物体实例。 - public async UniTask LoadGameObjectAsync(string assetName, CancellationToken cancellationToken) - { - if (AssetReference == null) - { - Log.Fatal($"LoadAssetAsync Failed => {this}.AssetReference is null"); - return default; - } - - return await AssetReference.LoadGameObjectAsync(assetName, cancellationToken); - } - - #endregion - #region UIElement /// diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWidget.cs b/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWidget.cs index 2e5ecc4d..cbc2ad88 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWidget.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWidget.cs @@ -149,7 +149,7 @@ namespace TEngine /// public bool CreateByPath(string resPath, UIBase parentUI, Transform parentTrans = null, bool visible = true) { - GameObject goInst = parentUI.AssetReference.LoadAsset(resPath, parentTrans); + GameObject goInst = GameModule.Resource.LoadAsset(resPath, parentTrans); if (goInst == null) { return false; @@ -191,11 +191,6 @@ namespace TEngine return false; } - if (AssetReference == null) - { - AssetReference = AssetReference.BindAssetReference(widgetRoot, parent: parentUI.AssetReference); - } - RestChildCanvas(parentUI); parent = parentUI; Parent.ListChild.Add(this); @@ -203,6 +198,7 @@ namespace TEngine BindMemberProperty(); RegisterEvent(); OnCreate(); + OnRefresh(); IsPrepare = true; if (!visible) @@ -269,14 +265,6 @@ namespace TEngine uiChild.OnDestroyWidget(); } - if (Handle != null) - { - if (AssetReference != null && AssetReference.Parent != null) - { - AssetReference.Parent.Release(Handle); - } - } - if (gameObject != null) { Object.Destroy(gameObject); diff --git a/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWindow.cs b/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWindow.cs index 83036697..fdf0e8a0 100644 --- a/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWindow.cs +++ b/UnityProject/Assets/TEngine/Runtime/Modules/UIModule/UIWindow.cs @@ -245,17 +245,11 @@ namespace TEngine internal void InternalLoad(string location, System.Action prepareCallback, System.Object[] userDatas) { - if (Handle != null) - { - return; - } - _prepareCallback = prepareCallback; this.userDatas = userDatas; if (!FromResources) { - Handle = YooAssets.LoadAssetAsync(location); - Handle.Completed += Handle_Completed; + GameModule.Resource.LoadAssetAsync(location, Handle_Completed); } else { @@ -369,13 +363,6 @@ namespace TEngine // 注销回调函数 _prepareCallback = null; - // 卸载面板资源 - if (Handle != null) - { - Handle.Release(); - Handle = null; - } - OnDestroy(); // 销毁面板对象 @@ -393,11 +380,16 @@ namespace TEngine /// private void Handle_Completed(AssetOperationHandle handle) { + if (handle == null) + { + throw new GameFrameworkException("Load uiWindows failed because AssetOperationHandle is null"); + } if (handle.AssetObject == null) { - return; + throw new GameFrameworkException("Load uiWindows Failed because AssetObject is null"); } - + Handle = handle; + // 实例化对象 var panel = handle.InstantiateSync(UIModule.UIRootStatic); Handle_Completed(panel); @@ -417,9 +409,6 @@ namespace TEngine _panel = panel; _panel.transform.localPosition = Vector3.zero; - // 绑定引用 - AssetReference = AssetReference.BindAssetReference(_panel); - // 获取组件 _canvas = _panel.GetComponent(); if (_canvas == null)