From 3809da762c5ad0aa20e4e08ac2eeb5b51133dea2 Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Fri, 7 Apr 2023 18:02:33 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=9B=BF=E6=8D=A2=E7=AD=96?= =?UTF-8?q?=E7=95=A5=EF=BC=88Cache=20Replacement=20Policy=EF=BC=89-=20ARC?= =?UTF-8?q?=EF=BC=88Adaptive=20Replacement=20Cache=EF=BC=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 缓存替换策略(Cache Replacement Policy)- ARC(Adaptive Replacement Cache)。 --- .../GameFramework/Resource/ArcCache.meta | 3 + .../Resource/ArcCache/ArcCacheTable.cs | 273 ++++++++++++++++++ .../Resource/ArcCache/ArcCacheTable.cs.meta | 3 + .../Resource/ArcCache/QueueNode.cs | 82 ++++++ .../Resource/ArcCache/QueueNode.cs.meta | 3 + .../GameFramework/Resource/LruCache.meta | 3 + .../Resource/LruCache/AssetLruCacheTable.cs | 20 ++ .../LruCache/AssetLruCacheTable.cs.meta | 3 + .../Resource/LruCache/LruCacheTable.cs | 198 +++++++++++++ .../Resource/LruCache/LruCacheTable.cs.meta | 11 + .../Resource/LruCache/LruGroupAttribute.cs | 14 + .../LruCache/LruGroupAttribute.cs.meta | 3 + .../Resource/LruCache/LruGroupType.cs | 12 + .../Resource/LruCache/LruGroupType.cs.meta | 3 + .../Resource/LruCache/Resource.cs | 88 ++++++ .../Resource/LruCache/Resource.cs.meta | 3 + 16 files changed, 722 insertions(+) create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/ArcCache.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs.meta diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache.meta b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache.meta new file mode 100644 index 00000000..30ebd612 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3e380d86c4544d77a6560f769520ad62 +timeCreated: 1680855013 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs new file mode 100644 index 00000000..580f0427 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.ArcCache +{ + /// + /// 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(TValue data); + + public delegate void OnRemove(TValue data); + + /// + /// ARC缓存表添加成功回调。 + /// + public OnAdd OnAddCallback; + + /// + /// ARC缓存表移除回调。 + /// + public OnRemove OnRemoveCallback; + + /// + /// ARC缓存表哈希表构造。 + /// + /// 容量。 + /// LRU缓存表添加成功回调。 + /// LRU缓存表移除回调。 + public ArcCacheTable(int capacity, OnAdd onAdd = null, OnRemove onRemove = null) + { + this.Capacity = capacity; + OnAddCallback = onAdd; + OnRemoveCallback = onRemove; + this.CacheStorageMap = new Dictionary>(); + this._t1Head = new QueueNode(); + this._t2Head = new QueueNode(); + this._b1Head = new QueueNode(); + this._b2Head = new QueueNode(); + } + + /// + /// 对象推入ARC缓存表。 + /// + /// 键。 + /// 值。 + public void PutCache(TKey key, TValue value) + { + QueueNode queueNode = CacheStorageMap[key]; + + if (queueNode == null) + { + OnMissOnAllQueue(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) + { + QueueNode queueNode = CacheStorageMap[key]; + + 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 + { + //Dispose(value); + } + catch (System.Exception e) + { + Debug.Log(e); + } + } + + 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/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs.meta new file mode 100644 index 00000000..fe2e6e2c --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/ArcCacheTable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ef5afbe17b2748638f6fcd341958a4f2 +timeCreated: 1680857519 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs new file mode 100644 index 00000000..9e0dfe50 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs @@ -0,0 +1,82 @@ +namespace TEngine.ArcCache +{ + /// + /// 队列类型。 + /// 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/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs.meta new file mode 100644 index 00000000..2bcf47e1 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/ArcCache/QueueNode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 908fe3016e154893908fb7f3b4050a74 +timeCreated: 1680857337 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache.meta b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache.meta new file mode 100644 index 00000000..be67c5e7 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 02b781ce209c45098d54e0e0f11dc856 +timeCreated: 1680833959 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs new file mode 100644 index 00000000..8b64cacd --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs @@ -0,0 +1,20 @@ +using YooAsset; + +namespace TEngine +{ + /// + /// 资源Lru缓存表。 + /// + public sealed class AssetLruCacheTable:LruCacheTable + { + public AssetLruCacheTable(int capacity) : base(capacity) + { + OnRemoveCallback += OnRelease; + } + + private void OnRelease(OperationHandleBase handleBase) + { + handleBase.ReleaseInternal(); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs.meta new file mode 100644 index 00000000..527b4d4a --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/AssetLruCacheTable.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c371cc78061c4ed78a4df5b315314e43 +timeCreated: 1680835698 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs new file mode 100644 index 00000000..94941bba --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; + +namespace TEngine +{ + /// + /// LRU缓存表。 + /// + /// LRUKey。 + /// LRUValue + /// 缓存替换策略(Cache Replacement Policy)- LRU(Least Recently Used)。 + public class LruCacheTable + { + /// + /// LRU缓存表头节点。 + /// + protected readonly DoubleLinkedListNode Head; + + /// + /// LRU缓存表尾节点。 + /// + protected readonly DoubleLinkedListNode Tail; + + /// + /// 哈希表来存储键值对。 + /// Dictionary 中存储 key 和 LinkedList 中节点的映射关系。 + /// 算法优化的关键在于如何降低链表的删除操作的时间复杂度。不管是在插入、删除、查找缓存的时候,都可以通过这种联系来将时间复杂度降低到 O(1)。 + /// + protected readonly Dictionary> LinkedListNodesMap; + + protected readonly int Capacity; + + public delegate void OnAdd(TValue data); + public delegate void OnRemove(TValue data); + + /// + /// LRU缓存表添加成功回调。 + /// + public OnAdd OnAddCallback; + + /// + /// LRU缓存表移除回调。 + /// + public OnRemove OnRemoveCallback; + + /// + /// LRU缓存表构造。 + /// + /// 容量。 + /// LRU缓存表添加成功回调。 + /// LRU缓存表移除回调。 + public LruCacheTable(int capacity,OnAdd onAdd = null,OnRemove onRemove = null) + { + Capacity = capacity; + OnAddCallback = onAdd; + OnRemoveCallback = onRemove; + Head = new DoubleLinkedListNode(); + Tail = new DoubleLinkedListNode(); + Head.Next = Tail; + Tail.Previous = Head; + LinkedListNodesMap = new Dictionary>(Capacity); + } + + /// + /// 从LRU缓存表中获取。 + /// + /// 键。 + /// 值。 + /// 在链表中删除 key,然后将 key 添加到链表的尾部,这样就可以保证链表的尾部就是最近访问的数据,链表的头部就是最久没有被访问的数据。 + public virtual TValue Get(TKey key) + { + if (LinkedListNodesMap.TryGetValue(key, out var node)) + { + RemoveNode(node); + AddLastNode(node); + return node.Value; + } + + return default; + } + + /// + /// 放入LRU缓存表。 + /// + /// 键。 + /// 值。 + public virtual void Put(TKey key, TValue value) + { + if (LinkedListNodesMap.TryGetValue(key, out var node)) + { + // 如果插入的 key 已经存在,将 key 对应的值更新,然后将 key 移动到链表的尾部。 + RemoveNode(node); + OnRemoveCallback?.Invoke(node.Value); + AddLastNode(node); + node.Value = value; + OnAddCallback?.Invoke(value); + } + else + { + if (LinkedListNodesMap.Count == Capacity) + { + // 缓存满了,删除链表的头部,也就是最久没有被访问的数据。 + var firstNode = RemoveFirstNode(); + LinkedListNodesMap.Remove(firstNode.Key); + OnRemoveCallback?.Invoke(firstNode.Value); + } + + var newNode = new DoubleLinkedListNode(key, value); + AddLastNode(newNode); + LinkedListNodesMap.Add(key, newNode); + OnAddCallback?.Invoke(value); + } + } + + /// + /// 从LRU缓存表中移除。 + /// + /// 键。 + public virtual void Remove(TKey key) + { + if (LinkedListNodesMap.TryGetValue(key, out var node)) + { + LinkedListNodesMap.Remove(key); + RemoveNode(node); + OnRemoveCallback?.Invoke(node.Value); + } + } + + /// + /// 清理LRU缓存表中所有数据。 + /// + public virtual void Clear() + { + int protectedIndex = Capacity; + while (Head.Next != null) + { + var firstNode = RemoveFirstNode(); + LinkedListNodesMap.Remove(firstNode.Key); + OnRemoveCallback?.Invoke(firstNode.Value); + protectedIndex--; + if (protectedIndex <0) + { + break; + } + } + } + + protected void AddLastNode(DoubleLinkedListNode node) + { + node.Previous = Tail.Previous; + node.Next = Tail; + Tail.Previous.Next = node; + Tail.Previous = node; + } + + protected DoubleLinkedListNode RemoveFirstNode() + { + var firstNode = Head.Next; + if (firstNode == null) + { + return firstNode; + } + Head.Next = firstNode.Next; + firstNode.Next.Previous = Head; + firstNode.Next = null; + firstNode.Previous = null; + return firstNode; + } + + protected void RemoveNode(DoubleLinkedListNode node) + { + node.Previous.Next = node.Next; + node.Next.Previous = node.Previous; + node.Next = null; + node.Previous = null; + } + + protected class DoubleLinkedListNode + { + public DoubleLinkedListNode() + { + } + + public DoubleLinkedListNode(TNodeKey key, TNodeValue value) + { + Key = key; + Value = value; + } + + public TNodeKey Key { get; set; } + + public TNodeValue Value { get; set; } + + public DoubleLinkedListNode Previous { get; set; } + + public DoubleLinkedListNode Next { get; set; } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs.meta new file mode 100644 index 00000000..10761cf1 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruCacheTable.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3f37f202a857ede4dbe45f6645eb9765 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs new file mode 100644 index 00000000..855085ae --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace TEngine +{ + public class LruGroupAttribute:Attribute + { + public int Capacity; + + public LruGroupAttribute(int capacity) + { + Capacity = capacity; + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs.meta new file mode 100644 index 00000000..fdc3d040 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b20916852da04186917626247af54dae +timeCreated: 1680850559 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs new file mode 100644 index 00000000..37110c93 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs @@ -0,0 +1,12 @@ +namespace TEngine +{ + public enum LruGroupType : byte + { + [LruGroup(20)] + GameActor, + [LruGroup(50)] + GameObject, + [LruGroup(100)] + Sprite, + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs.meta new file mode 100644 index 00000000..2c18fdc0 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/LruGroupType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 04c756fa6c5b4d52b421e31ce051ba43 +timeCreated: 1680850512 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs new file mode 100644 index 00000000..86840ae7 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs @@ -0,0 +1,88 @@ +using System; +using YooAsset; +using UnityEngine; +using Cysharp.Threading.Tasks; +using System.Collections.Generic; + +namespace TEngine +{ + public static class Resource + { + private static readonly Dictionary ObjectHandlesMap = new Dictionary(); + + private static readonly Dictionary GameObjectsMap = new Dictionary(); + + public static async UniTask InstantiateAsync(string location, Transform parent = null, bool stayWorldSpace = false) + { + var handle = YooAssets.LoadAssetAsync(location); + + await handle.ToUniTask(); + + if (!handle.IsValid) + { + throw new Exception($"[Resource] InstantiateAsync Failed to load asset: {location}"); + } + + ObjectHandlesMap.Add(handle.AssetObject, handle); + + GameObject go = UnityEngine.Object.Instantiate(handle.AssetObject, parent, stayWorldSpace) as GameObject; + if (go == null) + { + Release(handle.AssetObject); + throw new Exception($"[Resource] InstantiateAsync Failed to instantiate asset: {location}"); + } + + GameObjectsMap.Add(go, handle.AssetObject); + + return go; + } + + public static async UniTask LoadAssetAsync(string location) where T : UnityEngine.Object + { + var handle = YooAssets.LoadAssetAsync(location); + + await handle.ToUniTask(); + + if (!handle.IsValid) + { + throw new Exception($"[Resource] LoadAssetAsync Failed to load asset: {location}"); + } + + ObjectHandlesMap.Add(handle.AssetObject, handle); + + return handle.AssetObject as T; + } + + public static void ReleaseInstance(GameObject gameObject) + { + if (gameObject is null) + { + return; + } + + UnityEngine.Object.Destroy(gameObject); + + if (GameObjectsMap.TryGetValue(gameObject, out UnityEngine.Object obj)) + { + GameObjectsMap.Remove(gameObject); + + Release(obj); + } + } + + public static void Release(UnityEngine.Object unityObject) + { + if (unityObject is null) + { + return; + } + + if (ObjectHandlesMap.TryGetValue(unityObject, out OperationHandleBase handle)) + { + ObjectHandlesMap.Remove(unityObject); + + handle?.ReleaseInternal(); + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs.meta b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs.meta new file mode 100644 index 00000000..ecefc0a9 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Resource/LruCache/Resource.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 115780f0702a4a4a824b789e0ec1fe7b +timeCreated: 1680838058 \ No newline at end of file