mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-07 16:45:10 +00:00
缓存替换策略(Cache Replacement Policy)- ARC(Adaptive Replacement Cache)。
缓存替换策略(Cache Replacement Policy)- ARC(Adaptive Replacement Cache)。
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e380d86c4544d77a6560f769520ad62
|
||||
timeCreated: 1680855013
|
@@ -0,0 +1,273 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngine.ArcCache
|
||||
{
|
||||
/// <summary>
|
||||
/// Adaptive Replacement Cache缓存表。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">Adaptive Replacement CacheKey。</typeparam>
|
||||
/// <typeparam name="TValue">Adaptive Replacement CacheValue</typeparam>
|
||||
/// <remarks>缓存替换策略(Cache Replacement Policy)- ARC(Adaptive Replacement Cache)。</remarks>
|
||||
public class ArcCacheTable<TKey, TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// ARC缓存表哈希表。
|
||||
/// <remarks>Dictionary 中存储 key 和 QueueNode 中节点的映射关系。</remarks>
|
||||
/// <remarks>算法优化的关键在于如何降低链表的删除操作的时间复杂度。不管是在插入、删除、查找缓存的时候,都可以通过这种联系来将时间复杂度降低到 O(1)。</remarks>
|
||||
/// </summary>
|
||||
protected readonly Dictionary<TKey, QueueNode<TKey, TValue>> CacheStorageMap;
|
||||
|
||||
private readonly QueueNode<TKey, TValue> _t1Head;
|
||||
private readonly QueueNode<TKey, TValue> _t2Head;
|
||||
private readonly QueueNode<TKey, TValue> _b1Head;
|
||||
private readonly QueueNode<TKey, TValue> _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);
|
||||
|
||||
/// <summary>
|
||||
/// ARC缓存表添加成功回调。
|
||||
/// </summary>
|
||||
public OnAdd OnAddCallback;
|
||||
|
||||
/// <summary>
|
||||
/// ARC缓存表移除回调。
|
||||
/// </summary>
|
||||
public OnRemove OnRemoveCallback;
|
||||
|
||||
/// <summary>
|
||||
/// ARC缓存表哈希表构造。
|
||||
/// </summary>
|
||||
/// <param name="capacity">容量。</param>
|
||||
/// <param name="onAdd">LRU缓存表添加成功回调。</param>
|
||||
/// <param name="onRemove">LRU缓存表移除回调。</param>
|
||||
public ArcCacheTable(int capacity, OnAdd onAdd = null, OnRemove onRemove = null)
|
||||
{
|
||||
this.Capacity = capacity;
|
||||
OnAddCallback = onAdd;
|
||||
OnRemoveCallback = onRemove;
|
||||
this.CacheStorageMap = new Dictionary<TKey, QueueNode<TKey, TValue>>();
|
||||
this._t1Head = new QueueNode<TKey, TValue>();
|
||||
this._t2Head = new QueueNode<TKey, TValue>();
|
||||
this._b1Head = new QueueNode<TKey, TValue>();
|
||||
this._b2Head = new QueueNode<TKey, TValue>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对象推入ARC缓存表。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
public void PutCache(TKey key, TValue value)
|
||||
{
|
||||
QueueNode<TKey, TValue> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ARC缓存表取出对象。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <returns>TValue from cache if exists or null。</returns>
|
||||
public TValue GetCache(TKey key)
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNode = CacheStorageMap[key];
|
||||
|
||||
if (queueNode == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
else
|
||||
{
|
||||
return queueNode.Get();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在所有队列中未命中时执行任务(Case:Key不在(T1 u B1 u T2 u B2))。
|
||||
/// </summary>
|
||||
/// <param name="key">key cache key。</param>
|
||||
/// <param name="value">value to inset in cache。</param>
|
||||
private void OnMissOnAllQueue(TKey key, TValue value)
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNode = new QueueNode<TKey, TValue>(key, value);
|
||||
queueNode.QueueType = QueueType.T1;
|
||||
|
||||
int sizeL1 = (_t1Size + _b1Size);
|
||||
int sizeL2 = (_t2Size + _b2Size);
|
||||
if (sizeL1 == Capacity)
|
||||
{
|
||||
if (_t1Size < Capacity)
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNodeToBeRemoved = _b1Head.Next;
|
||||
RemoveFromQueue(queueNodeToBeRemoved);
|
||||
queueNodeToBeRemoved.Remove();
|
||||
_b1Size--;
|
||||
|
||||
Replace(queueNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNodeToBeRemoved = _t1Head.Next;
|
||||
RemoveFromQueue(queueNodeToBeRemoved);
|
||||
queueNodeToBeRemoved.Remove();
|
||||
_t1Size--;
|
||||
}
|
||||
}
|
||||
else if ((sizeL1 < Capacity) && ((sizeL1 + sizeL2) >= Capacity))
|
||||
{
|
||||
if ((sizeL1 + sizeL2) >= (2 * Capacity))
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNodeToBeRemoved = _b2Head.Next;
|
||||
RemoveFromQueue(queueNodeToBeRemoved);
|
||||
queueNodeToBeRemoved.Remove();
|
||||
_b2Size--;
|
||||
}
|
||||
|
||||
Replace(queueNode);
|
||||
}
|
||||
|
||||
_t1Size++;
|
||||
CacheStorageMap.Add(key, queueNode);
|
||||
queueNode.AddToLast(_t1Head);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行任务命中B1 (Case:Key在B1中)。
|
||||
/// </summary>
|
||||
/// <param name="queueNode">queueNode queue node。</param>
|
||||
private void OnHitOnB1(QueueNode<TKey, TValue> queueNode)
|
||||
{
|
||||
_adaptiveParameter = Math.Min(Capacity, _adaptiveParameter + Math.Max(_b2Size / _b1Size, 1));
|
||||
Replace(queueNode);
|
||||
|
||||
_t2Size++;
|
||||
_b1Size--;
|
||||
queueNode.Remove();
|
||||
queueNode.QueueType = QueueType.T2;
|
||||
queueNode.AddToLast(_t2Head);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行任务命中B2 (Case:Key在B2中)。
|
||||
/// </summary>
|
||||
/// <param name="queueNode">queueNode queue node。</param>
|
||||
private void OnHitOnB2(QueueNode<TKey, TValue> queueNode)
|
||||
{
|
||||
_adaptiveParameter = Math.Max(0, _adaptiveParameter - Math.Max(_b1Size / _b2Size, 1));
|
||||
Replace(queueNode);
|
||||
|
||||
_t2Size++;
|
||||
_b2Size--;
|
||||
queueNode.Remove();
|
||||
queueNode.QueueType = QueueType.T2;
|
||||
queueNode.AddToLast(_t2Head);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行任务命中T1或者T2 (Case:Key在T1或者T2中)。
|
||||
/// </summary>
|
||||
/// <param name="queueNode">queueNode queue node。</param>
|
||||
private void OnHitOnT1orT2(QueueNode<TKey, TValue> queueNode)
|
||||
{
|
||||
if (queueNode.QueueType == QueueType.T1)
|
||||
{
|
||||
_t1Size--;
|
||||
_t2Size++;
|
||||
}
|
||||
|
||||
queueNode.Remove();
|
||||
queueNode.QueueType = QueueType.T2;
|
||||
queueNode.AddToLast(_t2Head);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 替换队列节点(情况:L1(T1 u B1)少于c个)
|
||||
/// </summary>
|
||||
/// <param name="queueNode">queueNode queue node。</param>
|
||||
private void Replace(QueueNode<TKey, TValue> queueNode)
|
||||
{
|
||||
if ((_t1Size >= 1) && (((queueNode.QueueType == QueueType.B2) && (_t1Size == _adaptiveParameter)) || (_t1Size > _adaptiveParameter)))
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNodeToBeRemoved = _t1Head.Next;
|
||||
queueNodeToBeRemoved.Remove();
|
||||
queueNodeToBeRemoved.QueueType = QueueType.B1;
|
||||
queueNodeToBeRemoved.AddToLast(_b1Head);
|
||||
_t1Size--;
|
||||
_b1Size++;
|
||||
}
|
||||
else
|
||||
{
|
||||
QueueNode<TKey, TValue> queueNodeToBeRemoved = _t2Head.Next;
|
||||
queueNodeToBeRemoved.Remove();
|
||||
queueNodeToBeRemoved.QueueType = QueueType.B2;
|
||||
queueNodeToBeRemoved.AddToLast(_b2Head);
|
||||
_t2Size--;
|
||||
_b2Size++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove TValue data from queue and dispose it
|
||||
/// </summary>
|
||||
/// <param name="queueNodeToBeRemoved">queueNodeToBeRemoved queue node to be remove from queue</param>
|
||||
public void RemoveFromQueue(QueueNode<TKey, TValue> 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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef5afbe17b2748638f6fcd341958a4f2
|
||||
timeCreated: 1680857519
|
@@ -0,0 +1,82 @@
|
||||
namespace TEngine.ArcCache
|
||||
{
|
||||
/// <summary>
|
||||
/// 队列类型。
|
||||
/// 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.
|
||||
/// <remarks></remarks>
|
||||
/// </summary>
|
||||
public enum QueueType
|
||||
{
|
||||
None,
|
||||
/// <summary>
|
||||
/// T1, recent cache entries.
|
||||
/// </summary>
|
||||
T1,
|
||||
/// <summary>
|
||||
/// B1, frequent entries.
|
||||
/// </summary>
|
||||
B1,
|
||||
/// <summary>
|
||||
/// T2, ghost entries recently evicted from the T1 cache.
|
||||
/// </summary>
|
||||
T2,
|
||||
/// <summary>
|
||||
/// B2, ghost entries recently evicted from the T2 cache.
|
||||
/// </summary>
|
||||
B2
|
||||
}
|
||||
|
||||
public class QueueNode<TKey, TValue>
|
||||
{
|
||||
public readonly TKey Key;
|
||||
public QueueNode<TKey, TValue> Prev;
|
||||
public QueueNode<TKey, TValue> 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<TKey, TValue> head)
|
||||
{
|
||||
QueueNode<TKey, TValue> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 908fe3016e154893908fb7f3b4050a74
|
||||
timeCreated: 1680857337
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02b781ce209c45098d54e0e0f11dc856
|
||||
timeCreated: 1680833959
|
@@ -0,0 +1,20 @@
|
||||
using YooAsset;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源Lru缓存表。
|
||||
/// </summary>
|
||||
public sealed class AssetLruCacheTable:LruCacheTable<string,OperationHandleBase>
|
||||
{
|
||||
public AssetLruCacheTable(int capacity) : base(capacity)
|
||||
{
|
||||
OnRemoveCallback += OnRelease;
|
||||
}
|
||||
|
||||
private void OnRelease(OperationHandleBase handleBase)
|
||||
{
|
||||
handleBase.ReleaseInternal();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c371cc78061c4ed78a4df5b315314e43
|
||||
timeCreated: 1680835698
|
@@ -0,0 +1,198 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <summary>
|
||||
/// LRU缓存表。
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">LRUKey。</typeparam>
|
||||
/// <typeparam name="TValue">LRUValue</typeparam>
|
||||
/// <remarks>缓存替换策略(Cache Replacement Policy)- LRU(Least Recently Used)。</remarks>
|
||||
public class LruCacheTable<TKey, TValue>
|
||||
{
|
||||
/// <summary>
|
||||
/// LRU缓存表头节点。
|
||||
/// </summary>
|
||||
protected readonly DoubleLinkedListNode<TKey, TValue> Head;
|
||||
|
||||
/// <summary>
|
||||
/// LRU缓存表尾节点。
|
||||
/// </summary>
|
||||
protected readonly DoubleLinkedListNode<TKey, TValue> Tail;
|
||||
|
||||
/// <summary>
|
||||
/// 哈希表来存储键值对。
|
||||
/// <remarks>Dictionary 中存储 key 和 LinkedList 中节点的映射关系。</remarks>
|
||||
/// <remarks>算法优化的关键在于如何降低链表的删除操作的时间复杂度。不管是在插入、删除、查找缓存的时候,都可以通过这种联系来将时间复杂度降低到 O(1)。</remarks>
|
||||
/// </summary>
|
||||
protected readonly Dictionary<TKey, DoubleLinkedListNode<TKey, TValue>> LinkedListNodesMap;
|
||||
|
||||
protected readonly int Capacity;
|
||||
|
||||
public delegate void OnAdd(TValue data);
|
||||
public delegate void OnRemove(TValue data);
|
||||
|
||||
/// <summary>
|
||||
/// LRU缓存表添加成功回调。
|
||||
/// </summary>
|
||||
public OnAdd OnAddCallback;
|
||||
|
||||
/// <summary>
|
||||
/// LRU缓存表移除回调。
|
||||
/// </summary>
|
||||
public OnRemove OnRemoveCallback;
|
||||
|
||||
/// <summary>
|
||||
/// LRU缓存表构造。
|
||||
/// </summary>
|
||||
/// <param name="capacity">容量。</param>
|
||||
/// <param name="onAdd">LRU缓存表添加成功回调。</param>
|
||||
/// <param name="onRemove">LRU缓存表移除回调。</param>
|
||||
public LruCacheTable(int capacity,OnAdd onAdd = null,OnRemove onRemove = null)
|
||||
{
|
||||
Capacity = capacity;
|
||||
OnAddCallback = onAdd;
|
||||
OnRemoveCallback = onRemove;
|
||||
Head = new DoubleLinkedListNode<TKey, TValue>();
|
||||
Tail = new DoubleLinkedListNode<TKey, TValue>();
|
||||
Head.Next = Tail;
|
||||
Tail.Previous = Head;
|
||||
LinkedListNodesMap = new Dictionary<TKey, DoubleLinkedListNode<TKey, TValue>>(Capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从LRU缓存表中获取。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <returns>值。</returns>
|
||||
/// <remarks>在链表中删除 key,然后将 key 添加到链表的尾部,这样就可以保证链表的尾部就是最近访问的数据,链表的头部就是最久没有被访问的数据。</remarks>
|
||||
public virtual TValue Get(TKey key)
|
||||
{
|
||||
if (LinkedListNodesMap.TryGetValue(key, out var node))
|
||||
{
|
||||
RemoveNode(node);
|
||||
AddLastNode(node);
|
||||
return node.Value;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 放入LRU缓存表。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
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<TKey, TValue>(key, value);
|
||||
AddLastNode(newNode);
|
||||
LinkedListNodesMap.Add(key, newNode);
|
||||
OnAddCallback?.Invoke(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从LRU缓存表中移除。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
public virtual void Remove(TKey key)
|
||||
{
|
||||
if (LinkedListNodesMap.TryGetValue(key, out var node))
|
||||
{
|
||||
LinkedListNodesMap.Remove(key);
|
||||
RemoveNode(node);
|
||||
OnRemoveCallback?.Invoke(node.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理LRU缓存表中所有数据。
|
||||
/// </summary>
|
||||
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<TKey, TValue> node)
|
||||
{
|
||||
node.Previous = Tail.Previous;
|
||||
node.Next = Tail;
|
||||
Tail.Previous.Next = node;
|
||||
Tail.Previous = node;
|
||||
}
|
||||
|
||||
protected DoubleLinkedListNode<TKey, TValue> 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<TKey, TValue> node)
|
||||
{
|
||||
node.Previous.Next = node.Next;
|
||||
node.Next.Previous = node.Previous;
|
||||
node.Next = null;
|
||||
node.Previous = null;
|
||||
}
|
||||
|
||||
protected class DoubleLinkedListNode<TNodeKey, TNodeValue>
|
||||
{
|
||||
public DoubleLinkedListNode()
|
||||
{
|
||||
}
|
||||
|
||||
public DoubleLinkedListNode(TNodeKey key, TNodeValue value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public TNodeKey Key { get; set; }
|
||||
|
||||
public TNodeValue Value { get; set; }
|
||||
|
||||
public DoubleLinkedListNode<TNodeKey, TNodeValue> Previous { get; set; }
|
||||
|
||||
public DoubleLinkedListNode<TNodeKey, TNodeValue> Next { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f37f202a857ede4dbe45f6645eb9765
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
public class LruGroupAttribute:Attribute
|
||||
{
|
||||
public int Capacity;
|
||||
|
||||
public LruGroupAttribute(int capacity)
|
||||
{
|
||||
Capacity = capacity;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b20916852da04186917626247af54dae
|
||||
timeCreated: 1680850559
|
@@ -0,0 +1,12 @@
|
||||
namespace TEngine
|
||||
{
|
||||
public enum LruGroupType : byte
|
||||
{
|
||||
[LruGroup(20)]
|
||||
GameActor,
|
||||
[LruGroup(50)]
|
||||
GameObject,
|
||||
[LruGroup(100)]
|
||||
Sprite,
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04c756fa6c5b4d52b421e31ce051ba43
|
||||
timeCreated: 1680850512
|
@@ -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<UnityEngine.Object, OperationHandleBase> ObjectHandlesMap = new Dictionary<UnityEngine.Object, OperationHandleBase>();
|
||||
|
||||
private static readonly Dictionary<GameObject, UnityEngine.Object> GameObjectsMap = new Dictionary<GameObject, UnityEngine.Object>();
|
||||
|
||||
public static async UniTask<GameObject> InstantiateAsync(string location, Transform parent = null, bool stayWorldSpace = false)
|
||||
{
|
||||
var handle = YooAssets.LoadAssetAsync<GameObject>(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<T> LoadAssetAsync<T>(string location) where T : UnityEngine.Object
|
||||
{
|
||||
var handle = YooAssets.LoadAssetAsync<T>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 115780f0702a4a4a824b789e0ec1fe7b
|
||||
timeCreated: 1680838058
|
Reference in New Issue
Block a user