mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-07 16:45:10 +00:00
资源模块优化-使用资源缓存表以及资源淘汰算法,缓存常用资源,利于获取、淘汰不常用资源。
资源模块优化-使用资源缓存表以及资源淘汰算法,缓存常用资源,利于获取、淘汰不常用资源。
This commit is contained in:
@@ -42,8 +42,7 @@ namespace GameLogic
|
||||
|
||||
public void SetSprite(string sprite)
|
||||
{
|
||||
m_image.sprite = LoadAsset<Sprite>(sprite);
|
||||
m_image.SetNativeSize();
|
||||
m_image.SetSprite(sprite);
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
|
@@ -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();
|
||||
@@ -200,6 +202,19 @@ namespace TEngine.Editor.Inspector
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
|
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,276 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Debug = UnityEngine.Debug;
|
||||
|
||||
namespace TEngine
|
||||
{
|
||||
/// <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(TKey key,TValue data);
|
||||
|
||||
public delegate void OnRemove(TKey key,TValue data);
|
||||
|
||||
/// <summary>
|
||||
/// ARC缓存表添加成功回调。
|
||||
/// </summary>
|
||||
public readonly OnAdd OnAddCallback;
|
||||
|
||||
/// <summary>
|
||||
/// ARC缓存表移除回调。
|
||||
/// </summary>
|
||||
public readonly 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)
|
||||
{
|
||||
Capacity = capacity;
|
||||
OnAddCallback = onAdd;
|
||||
OnRemoveCallback = onRemove;
|
||||
CacheStorageMap = new Dictionary<TKey, QueueNode<TKey, TValue>>();
|
||||
_t1Head = new QueueNode<TKey, TValue>();
|
||||
_t2Head = new QueueNode<TKey, TValue>();
|
||||
_b1Head = new QueueNode<TKey, TValue>();
|
||||
_b2Head = new QueueNode<TKey, TValue>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对象推入ARC缓存表。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <param name="value">值。</param>
|
||||
public void PutCache(TKey key, TValue value)
|
||||
{
|
||||
CacheStorageMap.TryGetValue(key, out QueueNode<TKey, TValue> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从ARC缓存表取出对象。
|
||||
/// </summary>
|
||||
/// <param name="key">键。</param>
|
||||
/// <returns>TValue from cache if exists or null。</returns>
|
||||
public TValue GetCache(TKey key)
|
||||
{
|
||||
CacheStorageMap.TryGetValue(key, out QueueNode<TKey, TValue> queueNode);
|
||||
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2486d1f34ad744b7bb19b1b220952ee9
|
||||
timeCreated: 1697174054
|
@@ -245,7 +245,7 @@ namespace TEngine
|
||||
}
|
||||
}
|
||||
|
||||
AssetOperationHandle handle = GameModule.Resource.LoadAssetGetOperation<T>(assetName);
|
||||
AssetOperationHandle handle = YooAssets.LoadAssetSync<T>(assetName);
|
||||
|
||||
Reference(handle);
|
||||
|
||||
@@ -289,7 +289,7 @@ namespace TEngine
|
||||
}
|
||||
}
|
||||
|
||||
assetOperationHandle = GameModule.Resource.LoadAssetGetOperation<T>(assetName);
|
||||
assetOperationHandle = YooAssets.LoadAssetSync<T>(assetName);
|
||||
|
||||
Reference(assetOperationHandle);
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace TEngine
|
||||
}
|
||||
}
|
||||
|
||||
assetOperationHandle = GameModule.Resource.LoadAssetGetOperation<T>(assetName);
|
||||
assetOperationHandle = YooAssets.LoadAssetSync<T>(assetName);
|
||||
|
||||
Reference(assetOperationHandle);
|
||||
|
||||
@@ -354,11 +354,10 @@ namespace TEngine
|
||||
/// </summary>
|
||||
/// <param name="assetName">要加载的实例名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <param name="assetOperationHandle">资源操作句柄。</param>
|
||||
/// <returns>资源实实例。</returns>
|
||||
// ReSharper disable once UnusedParameter.Global
|
||||
// ReSharper disable once RedundantAssignment
|
||||
public async UniTask<T> LoadAssetAsync<T>(string assetName, CancellationToken cancellationToken,AssetOperationHandle assetOperationHandle = null) where T : Object
|
||||
public async UniTask<T> LoadAssetAsync<T>(string assetName, CancellationToken cancellationToken = default) where T : Object
|
||||
{
|
||||
if (string.IsNullOrEmpty(assetName))
|
||||
{
|
||||
@@ -379,7 +378,7 @@ namespace TEngine
|
||||
}
|
||||
}
|
||||
|
||||
AssetOperationHandle handle = GameModule.Resource.LoadAssetAsyncHandle<T>(assetName);
|
||||
AssetOperationHandle handle = YooAssets.LoadAssetAsync<T>(assetName);
|
||||
|
||||
Reference(handle);
|
||||
|
||||
@@ -392,8 +391,6 @@ namespace TEngine
|
||||
return null;
|
||||
}
|
||||
|
||||
assetOperationHandle = handle;
|
||||
|
||||
if (typeof(T) == typeof(GameObject))
|
||||
{
|
||||
GameObject ret = handle.InstantiateSync();
|
||||
@@ -411,21 +408,9 @@ namespace TEngine
|
||||
/// <param name="assetName">要加载的游戏物体名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <returns>异步游戏物体实例。</returns>
|
||||
public async UniTask<GameObject> LoadGameObjectAsync(string assetName, CancellationToken cancellationToken)
|
||||
public async UniTask<GameObject> LoadGameObjectAsync(string assetName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await LoadAssetAsync<GameObject>(assetName,cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载游戏物体。
|
||||
/// </summary>
|
||||
/// <param name="assetName">要加载的游戏物体名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <param name="assetOperationHandle">资源操作句柄。</param>
|
||||
/// <returns>异步游戏物体实例。</returns>
|
||||
public async UniTask<GameObject> LoadGameObjectAsync(string assetName, CancellationToken cancellationToken,AssetOperationHandle assetOperationHandle)
|
||||
{
|
||||
return await LoadAssetAsync<GameObject>(assetName,cancellationToken,assetOperationHandle);
|
||||
}
|
||||
}
|
||||
}
|
@@ -195,8 +195,7 @@ namespace TEngine
|
||||
/// <param name="assetName">要加载的实例名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <returns>资源实实例。</returns>
|
||||
public async UniTask<T> LoadAssetAsync<T>(string assetName, CancellationToken cancellationToken)
|
||||
where T : Object
|
||||
public async UniTask<T> LoadAssetAsync<T>(string assetName, CancellationToken cancellationToken = default) where T : Object
|
||||
{
|
||||
DirtyInitAssetGroup();
|
||||
return await _assetGroup.LoadAssetAsync<T>(assetName, cancellationToken);
|
||||
@@ -208,7 +207,7 @@ namespace TEngine
|
||||
/// <param name="assetName">要加载的游戏物体名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <returns>异步游戏物体实例。</returns>
|
||||
public async UniTask<GameObject> LoadGameObjectAsync(string assetName, CancellationToken cancellationToken)
|
||||
public async UniTask<GameObject> LoadGameObjectAsync(string assetName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
DirtyInitAssetGroup();
|
||||
return await _assetGroup.LoadGameObjectAsync(assetName, cancellationToken);
|
||||
|
@@ -62,6 +62,11 @@ namespace TEngine
|
||||
/// </summary>
|
||||
long Milliseconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源缓存表容量。
|
||||
/// </summary>
|
||||
int ARCTableCapacity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置资源只读区路径。
|
||||
/// </summary>
|
||||
|
@@ -0,0 +1,82 @@
|
||||
namespace TEngine
|
||||
{
|
||||
/// <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: 9a9c1bf1a1e443eaaf17b46fe6b29277
|
||||
timeCreated: 1697174054
|
@@ -86,6 +86,10 @@ namespace TEngine
|
||||
/// <remarks>优先级较高的模块会优先轮询,并且关闭操作会后进行。</remarks>
|
||||
internal override int Priority => 4;
|
||||
|
||||
/// <summary>
|
||||
/// 资源缓存表容量。
|
||||
/// </summary>
|
||||
public int ARCTableCapacity { get; set; }
|
||||
#endregion
|
||||
|
||||
#region 生命周期
|
||||
@@ -135,6 +139,66 @@ namespace TEngine
|
||||
|
||||
#endregion
|
||||
|
||||
private Dictionary<string, AssetOperationHandle> _releaseMaps;
|
||||
|
||||
private ArcCacheTable<string, AssetOperationHandle> _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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中获取同步资源句柄。
|
||||
/// </summary>
|
||||
/// <param name="location">资源定位地址。</param>
|
||||
/// <typeparam name="T">资源类型。</typeparam>
|
||||
/// <returns>资源句柄。</returns>
|
||||
private AssetOperationHandle GetHandleSync<T>(string location) where T : Object
|
||||
{
|
||||
AssetOperationHandle handle = null;
|
||||
// 尝试从从ARC缓存表取出对象。
|
||||
handle = _arcCacheTable.GetCache(location);
|
||||
|
||||
if (handle == null)
|
||||
{
|
||||
handle = YooAssets.LoadAssetSync<T>(location);
|
||||
}
|
||||
// 对象推入ARC缓存表。
|
||||
_arcCacheTable.PutCache(location, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存中获取异步资源句柄。
|
||||
/// </summary>
|
||||
/// <param name="location">资源定位地址。</param>
|
||||
/// <typeparam name="T">资源类型。</typeparam>
|
||||
/// <returns>资源句柄。</returns>
|
||||
private AssetOperationHandle GetHandleAsync<T>(string location) where T : Object
|
||||
{
|
||||
AssetOperationHandle handle = null;
|
||||
// 尝试从从ARC缓存表取出对象。
|
||||
handle = _arcCacheTable.GetCache(location);
|
||||
|
||||
if (handle == null)
|
||||
{
|
||||
handle = YooAssets.LoadAssetAsync<T>(location);
|
||||
}
|
||||
// 对象推入ARC缓存表。
|
||||
_arcCacheTable.PutCache(location, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化资源模块。
|
||||
/// </summary>
|
||||
@@ -155,6 +219,9 @@ namespace TEngine
|
||||
}
|
||||
|
||||
ResourcePool.Initialize(GameModule.Get<ResourceModule>().gameObject);
|
||||
|
||||
_releaseMaps ??= new Dictionary<string, AssetOperationHandle>(ARCTableCapacity);
|
||||
_arcCacheTable ??= new ArcCacheTable<string, AssetOperationHandle>(ARCTableCapacity, OnAddAsset, OnRemoveAsset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -240,6 +307,19 @@ namespace TEngine
|
||||
/// </summary>
|
||||
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<T>(location);
|
||||
AssetOperationHandle handle = GetHandleSync<T>(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<T>(location);
|
||||
AssetOperationHandle handle = GetHandleSync<T>(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
|
||||
/// <returns>资源实例。</returns>
|
||||
public T LoadAsset<T>(string location, out AssetOperationHandle handle) where T : Object
|
||||
{
|
||||
handle = YooAssets.LoadAssetSync<T>(location);
|
||||
|
||||
handle = GetHandleSync<T>(location);
|
||||
if (string.IsNullOrEmpty(location))
|
||||
{
|
||||
Log.Error("Asset name is invalid.");
|
||||
@@ -466,7 +541,7 @@ namespace TEngine
|
||||
/// <returns>资源实例。</returns>
|
||||
public T LoadAsset<T>(string location, Transform parent, out AssetOperationHandle handle) where T : Object
|
||||
{
|
||||
handle = YooAssets.LoadAssetSync<T>(location);
|
||||
handle = GetHandleSync<T>(location);
|
||||
|
||||
if (string.IsNullOrEmpty(location))
|
||||
{
|
||||
@@ -493,7 +568,7 @@ namespace TEngine
|
||||
/// <returns>同步加载资源句柄。</returns>
|
||||
public AssetOperationHandle LoadAssetGetOperation<T>(string location) where T : Object
|
||||
{
|
||||
return YooAssets.LoadAssetSync<T>(location);
|
||||
return GetHandleSync<T>(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -504,7 +579,7 @@ namespace TEngine
|
||||
/// <returns>异步加载资源句柄。</returns>
|
||||
public AssetOperationHandle LoadAssetAsyncHandle<T>(string location) where T : Object
|
||||
{
|
||||
return YooAssets.LoadAssetAsync<T>(location);
|
||||
return GetHandleAsync<T>(location);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -98,6 +98,11 @@ namespace TEngine
|
||||
public int downloadingMaxNum = 3;
|
||||
public int failedTryAgain = 3;
|
||||
|
||||
/// <summary>
|
||||
/// 资源缓存表容量。
|
||||
/// </summary>
|
||||
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,10 +300,28 @@ namespace TEngine
|
||||
/// </summary>
|
||||
public void ClearSandbox()
|
||||
{
|
||||
// TODO
|
||||
// YooAssets.ClearSandbox();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载资源。
|
||||
/// </summary>
|
||||
/// <param name="asset">要卸载的资源。</param>
|
||||
public void UnloadAsset(object asset)
|
||||
{
|
||||
m_ResourceManager.UnloadAsset(asset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预订执行释放未被使用的资源。
|
||||
/// </summary>
|
||||
/// <param name="performGCCollect">是否使用垃圾回收。</param>
|
||||
public void UnloadUnusedAssets(bool performGCCollect)
|
||||
{
|
||||
m_PreorderUnloadUnusedAssets = true;
|
||||
m_PerformGCCollect = performGCCollect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制执行释放未被使用的资源。
|
||||
/// </summary>
|
||||
@@ -317,7 +341,9 @@ namespace TEngine
|
||||
private void Update()
|
||||
{
|
||||
m_LastUnloadUnusedAssetsOperationElapseSeconds += GameTime.unscaledDeltaTime;
|
||||
if (m_AsyncOperation == null && (m_ForceUnloadUnusedAssets || m_LastUnloadUnusedAssetsOperationElapseSeconds >= maxUnloadUnusedAssetsInterval ||
|
||||
if (m_AsyncOperation == null &&
|
||||
(m_ForceUnloadUnusedAssets ||
|
||||
m_LastUnloadUnusedAssetsOperationElapseSeconds >= maxUnloadUnusedAssetsInterval ||
|
||||
m_PreorderUnloadUnusedAssets && m_LastUnloadUnusedAssetsOperationElapseSeconds >= minUnloadUnusedAssetsInterval))
|
||||
{
|
||||
Log.Info("Unload unused assets...");
|
||||
|
@@ -73,11 +73,6 @@ namespace TEngine
|
||||
/// </summary>
|
||||
public AssetOperationHandle Handle { protected set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源引用数据。
|
||||
/// </summary>
|
||||
public AssetReference AssetReference { protected set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源是否准备完毕。
|
||||
/// </summary>
|
||||
@@ -316,13 +311,7 @@ namespace TEngine
|
||||
/// <returns>UIWidget实例。</returns>
|
||||
public T CreateWidgetByPath<T>(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<GameObject>(assetLocation, parentTrans);
|
||||
GameObject goInst = GameModule.Resource.LoadAsset<GameObject>(assetLocation, parentTrans);
|
||||
return CreateWidget<T>(goInst, visible);
|
||||
}
|
||||
|
||||
@@ -336,13 +325,7 @@ namespace TEngine
|
||||
/// <returns>UIWidget实例。</returns>
|
||||
public async UniTask<T> CreateWidgetByPathAsync<T>(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<GameObject>(assetLocation, gameObject.GetCancellationTokenOnDestroy());
|
||||
GameObject goInst = await GameModule.Resource.LoadAssetAsync<GameObject>(assetLocation, gameObject.GetCancellationTokenOnDestroy());
|
||||
goInst.transform.SetParent(parentTrans);
|
||||
return CreateWidget<T>(goInst, visible);
|
||||
}
|
||||
@@ -531,163 +514,6 @@ namespace TEngine
|
||||
|
||||
#endregion
|
||||
|
||||
#region AssetRefrence Methods
|
||||
|
||||
/// <summary>
|
||||
/// 引用资源数据到资源组内。
|
||||
/// </summary>
|
||||
/// <param name="handle">资源操作句柄。</param>
|
||||
/// <param name="assetTag">资源标识。</param>
|
||||
/// <returns>是否注册成功。</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 引用资源数据到资源组内。
|
||||
/// </summary>
|
||||
/// <param name="address">资源定位地址。</param>
|
||||
/// <param name="handle">资源操作句柄。</param>
|
||||
/// <param name="assetTag">资源标识。</param>
|
||||
/// <returns>是否注册成功。</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从资源组内释放资源数据。
|
||||
/// </summary>
|
||||
/// <param name="assetTag"></param>
|
||||
/// <returns></returns>
|
||||
public bool ReleaseByTag(string assetTag)
|
||||
{
|
||||
if (AssetReference == null)
|
||||
{
|
||||
Log.Fatal($"Release Failed => {this}.AssetReference is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return AssetReference.ReleaseByTag(assetTag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从资源组内释放资源数据。
|
||||
/// </summary>
|
||||
/// <param name="handle"></param>
|
||||
/// <returns></returns>
|
||||
public bool Release(AssetOperationHandle handle)
|
||||
{
|
||||
if (AssetReference == null)
|
||||
{
|
||||
Log.Fatal($"Release Failed => {this}.AssetReference is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return AssetReference.Release(handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从资源组内释放资源数据。
|
||||
/// </summary>
|
||||
/// <param name="address">资源定位地址。</param>
|
||||
/// <returns>是否释放成功。</returns>
|
||||
public bool Release(string address)
|
||||
{
|
||||
if (AssetReference == null)
|
||||
{
|
||||
Log.Fatal($"Release Failed => {this}.AssetReference is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
return AssetReference.Release(address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步加载资源。
|
||||
/// </summary>
|
||||
/// <param name="assetName">要加载资源的名称。</param>
|
||||
/// <typeparam name="T">要加载资源的类型。</typeparam>
|
||||
/// <returns>资源实例。</returns>
|
||||
public T LoadAsset<T>(string assetName) where T : UnityEngine.Object
|
||||
{
|
||||
if (AssetReference == null)
|
||||
{
|
||||
Log.Fatal($"LoadAsset Failed => {this}.AssetReference is null");
|
||||
return default;
|
||||
}
|
||||
|
||||
return AssetReference.LoadAsset<T>(assetName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 同步加载资源。
|
||||
/// </summary>
|
||||
/// <param name="assetName">要加载资源的名称。</param>
|
||||
/// <param name="parentTrans">父节点位置。</param>
|
||||
/// <typeparam name="T">要加载资源的类型。</typeparam>
|
||||
/// <returns>资源实例。</returns>
|
||||
public T LoadAsset<T>(string assetName, Transform parentTrans) where T : UnityEngine.Object
|
||||
{
|
||||
if (AssetReference == null)
|
||||
{
|
||||
Log.Fatal($"LoadAsset Failed => {this}.AssetReference is null");
|
||||
return default;
|
||||
}
|
||||
|
||||
return AssetReference.LoadAsset<T>(assetName, parentTrans);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载资源实例。
|
||||
/// </summary>
|
||||
/// <param name="assetName">要加载的实例名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <returns>资源实实例。</returns>
|
||||
public async UniTask<T> LoadAssetAsync<T>(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<T>(assetName, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载游戏物体。
|
||||
/// </summary>
|
||||
/// <param name="assetName">要加载的游戏物体名称。</param>
|
||||
/// <param name="cancellationToken">取消操作Token。</param>
|
||||
/// <returns>异步游戏物体实例。</returns>
|
||||
public async UniTask<GameObject> 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
|
||||
|
||||
/// <summary>
|
||||
|
@@ -149,7 +149,7 @@ namespace TEngine
|
||||
/// <returns></returns>
|
||||
public bool CreateByPath(string resPath, UIBase parentUI, Transform parentTrans = null, bool visible = true)
|
||||
{
|
||||
GameObject goInst = parentUI.AssetReference.LoadAsset<GameObject>(resPath, parentTrans);
|
||||
GameObject goInst = GameModule.Resource.LoadAsset<GameObject>(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);
|
||||
|
@@ -245,17 +245,11 @@ namespace TEngine
|
||||
|
||||
internal void InternalLoad(string location, System.Action<UIWindow> prepareCallback, System.Object[] userDatas)
|
||||
{
|
||||
if (Handle != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_prepareCallback = prepareCallback;
|
||||
this.userDatas = userDatas;
|
||||
if (!FromResources)
|
||||
{
|
||||
Handle = YooAssets.LoadAssetAsync<GameObject>(location);
|
||||
Handle.Completed += Handle_Completed;
|
||||
GameModule.Resource.LoadAssetAsync<GameObject>(location, Handle_Completed);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -369,13 +363,6 @@ namespace TEngine
|
||||
// 注销回调函数
|
||||
_prepareCallback = null;
|
||||
|
||||
// 卸载面板资源
|
||||
if (Handle != null)
|
||||
{
|
||||
Handle.Release();
|
||||
Handle = null;
|
||||
}
|
||||
|
||||
OnDestroy();
|
||||
|
||||
// 销毁面板对象
|
||||
@@ -393,10 +380,15 @@ namespace TEngine
|
||||
/// <exception cref="Exception"></exception>
|
||||
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);
|
||||
@@ -417,9 +409,6 @@ namespace TEngine
|
||||
_panel = panel;
|
||||
_panel.transform.localPosition = Vector3.zero;
|
||||
|
||||
// 绑定引用
|
||||
AssetReference = AssetReference.BindAssetReference(_panel);
|
||||
|
||||
// 获取组件
|
||||
_canvas = _panel.GetComponent<Canvas>();
|
||||
if (_canvas == null)
|
||||
|
Reference in New Issue
Block a user