修复GameObjectPool class未池化的开销
This commit is contained in:
parent
38f731e240
commit
4a0465450a
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using AlicizaX;
|
using AlicizaX;
|
||||||
using AlicizaX.Resource.Runtime;
|
using AlicizaX.Resource.Runtime;
|
||||||
@ -71,7 +70,7 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class PooledObject
|
public class PooledObject : IMemory
|
||||||
{
|
{
|
||||||
public GameObject gameObject;
|
public GameObject gameObject;
|
||||||
public string assetPath;
|
public string assetPath;
|
||||||
@ -79,6 +78,7 @@ namespace AlicizaX
|
|||||||
public bool isActive;
|
public bool isActive;
|
||||||
public string instanceName;
|
public string instanceName;
|
||||||
public bool isRefCountReduced;
|
public bool isRefCountReduced;
|
||||||
|
[NonSerialized] public PoolObjectMonitor monitor;
|
||||||
|
|
||||||
public PooledObject(GameObject go, string path)
|
public PooledObject(GameObject go, string path)
|
||||||
{
|
{
|
||||||
@ -90,6 +90,17 @@ namespace AlicizaX
|
|||||||
isRefCountReduced = false;
|
isRefCountReduced = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
gameObject = null;
|
||||||
|
assetPath = null;
|
||||||
|
lastUsedTime = 0f;
|
||||||
|
isActive = false;
|
||||||
|
instanceName = null;
|
||||||
|
isRefCountReduced = false;
|
||||||
|
monitor = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取过期进度 (0-1),1表示即将过期。
|
/// 获取过期进度 (0-1),1表示即将过期。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -115,7 +126,7 @@ namespace AlicizaX
|
|||||||
/// Inspector显示用的对象信息。
|
/// Inspector显示用的对象信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class PoolObjectInfo
|
public class PoolObjectInfo : IMemory
|
||||||
{
|
{
|
||||||
[SerializeField] public string objectName;
|
[SerializeField] public string objectName;
|
||||||
[SerializeField] public string assetPath;
|
[SerializeField] public string assetPath;
|
||||||
@ -135,13 +146,24 @@ namespace AlicizaX
|
|||||||
expireProgress = pooledObj.GetExpireProgress(expireTime);
|
expireProgress = pooledObj.GetExpireProgress(expireTime);
|
||||||
gameObject = pooledObj.gameObject;
|
gameObject = pooledObj.gameObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
objectName = null;
|
||||||
|
assetPath = null;
|
||||||
|
isActive = false;
|
||||||
|
lastUsedTime = 0f;
|
||||||
|
remainingTime = 0f;
|
||||||
|
expireProgress = 0f;
|
||||||
|
gameObject = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inspector显示用的预制体信息.
|
/// Inspector显示用的预制体信息.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class PrefabRefInfoDisplay
|
public class PrefabRefInfoDisplay : IMemory
|
||||||
{
|
{
|
||||||
[SerializeField] public string assetPath;
|
[SerializeField] public string assetPath;
|
||||||
[SerializeField] public int refCount;
|
[SerializeField] public int refCount;
|
||||||
@ -155,13 +177,21 @@ namespace AlicizaX
|
|||||||
lastAccessTime = info.LastAccessTime;
|
lastAccessTime = info.LastAccessTime;
|
||||||
prefab = info.Prefab;
|
prefab = info.Prefab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
assetPath = null;
|
||||||
|
refCount = 0;
|
||||||
|
lastAccessTime = 0f;
|
||||||
|
prefab = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inspector显示用的池信息。
|
/// Inspector显示用的池信息。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ConfigPoolInfo
|
public class ConfigPoolInfo : IMemory
|
||||||
{
|
{
|
||||||
[SerializeField] public string configAsset;
|
[SerializeField] public string configAsset;
|
||||||
[SerializeField] public int maxCount;
|
[SerializeField] public int maxCount;
|
||||||
@ -198,7 +228,6 @@ namespace AlicizaX
|
|||||||
assetPaths.Clear();
|
assetPaths.Clear();
|
||||||
assetPaths.AddRange(pool.LoadedPrefabs.Keys);
|
assetPaths.AddRange(pool.LoadedPrefabs.Keys);
|
||||||
|
|
||||||
objects.Clear();
|
|
||||||
int objectIndex = 0;
|
int objectIndex = 0;
|
||||||
foreach (var pooledObj in pool.AllObjects)
|
foreach (var pooledObj in pool.AllObjects)
|
||||||
{
|
{
|
||||||
@ -211,7 +240,7 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info = new PoolObjectInfo();
|
info = MemoryPool.Acquire<PoolObjectInfo>();
|
||||||
objects.Add(info);
|
objects.Add(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,8 +248,8 @@ namespace AlicizaX
|
|||||||
objectIndex++;
|
objectIndex++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ReleasePoolObjectInfos(objectIndex);
|
||||||
|
|
||||||
prefabRefs.Clear();
|
|
||||||
int prefabIndex = 0;
|
int prefabIndex = 0;
|
||||||
foreach (var kvp in pool.LoadedPrefabs)
|
foreach (var kvp in pool.LoadedPrefabs)
|
||||||
{
|
{
|
||||||
@ -231,13 +260,48 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
info = new PrefabRefInfoDisplay();
|
info = MemoryPool.Acquire<PrefabRefInfoDisplay>();
|
||||||
prefabRefs.Add(info);
|
prefabRefs.Add(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
info.UpdateFromPrefabRefInfo(kvp.Value);
|
info.UpdateFromPrefabRefInfo(kvp.Value);
|
||||||
prefabIndex++;
|
prefabIndex++;
|
||||||
}
|
}
|
||||||
|
ReleasePrefabRefInfos(prefabIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
configAsset = null;
|
||||||
|
maxCount = 0;
|
||||||
|
expireTime = 0f;
|
||||||
|
totalObjects = 0;
|
||||||
|
activeObjects = 0;
|
||||||
|
availableObjects = 0;
|
||||||
|
loadedPrefabs = 0;
|
||||||
|
assetPaths.Clear();
|
||||||
|
ReleasePoolObjectInfos(0);
|
||||||
|
ReleasePrefabRefInfos(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleasePoolObjectInfos(int keepCount)
|
||||||
|
{
|
||||||
|
while (objects.Count > keepCount)
|
||||||
|
{
|
||||||
|
int lastIndex = objects.Count - 1;
|
||||||
|
MemoryPool.Release(objects[lastIndex]);
|
||||||
|
objects.RemoveAt(lastIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleasePrefabRefInfos(int keepCount)
|
||||||
|
{
|
||||||
|
while (prefabRefs.Count > keepCount)
|
||||||
|
{
|
||||||
|
int lastIndex = prefabRefs.Count - 1;
|
||||||
|
MemoryPool.Release(prefabRefs[lastIndex]);
|
||||||
|
prefabRefs.RemoveAt(lastIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +323,7 @@ namespace AlicizaX
|
|||||||
|
|
||||||
// GameObject到PooledObject的快速查找字典
|
// GameObject到PooledObject的快速查找字典
|
||||||
private readonly Dictionary<GameObject, PooledObject> _gameObjectToPooledObject;
|
private readonly Dictionary<GameObject, PooledObject> _gameObjectToPooledObject;
|
||||||
|
private readonly List<string> _availableObjectPaths;
|
||||||
|
|
||||||
// 重用临时队列,避免重复创建。
|
// 重用临时队列,避免重复创建。
|
||||||
private static Queue<PooledObject> _tempQueue = new Queue<PooledObject>();
|
private static Queue<PooledObject> _tempQueue = new Queue<PooledObject>();
|
||||||
@ -277,6 +342,7 @@ namespace AlicizaX
|
|||||||
PendingRequests = new Dictionary<string, List<UniTaskCompletionSource<GameObject>>>();
|
PendingRequests = new Dictionary<string, List<UniTaskCompletionSource<GameObject>>>();
|
||||||
LoadingAssets = new HashSet<string>();
|
LoadingAssets = new HashSet<string>();
|
||||||
_gameObjectToPooledObject = new Dictionary<GameObject, PooledObject>();
|
_gameObjectToPooledObject = new Dictionary<GameObject, PooledObject>();
|
||||||
|
_availableObjectPaths = new List<string>();
|
||||||
|
|
||||||
// 创建池根节点。
|
// 创建池根节点。
|
||||||
GameObject poolRootGo = new GameObject($"ConfigPool_{config.asset.Replace('/', '_')}");
|
GameObject poolRootGo = new GameObject($"ConfigPool_{config.asset.Replace('/', '_')}");
|
||||||
@ -422,7 +488,15 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
var prefabRefInfo = LoadedPrefabs[assetPath];
|
var prefabRefInfo = LoadedPrefabs[assetPath];
|
||||||
GameObject instantiate = GameObject.Instantiate(prefabRefInfo.Prefab);
|
GameObject instantiate = GameObject.Instantiate(prefabRefInfo.Prefab);
|
||||||
var pooledObj = new PooledObject(instantiate, assetPath);
|
|
||||||
|
var pooledObj = MemoryPool.Acquire<PooledObject>();
|
||||||
|
pooledObj.gameObject = instantiate;
|
||||||
|
pooledObj.assetPath = assetPath;
|
||||||
|
pooledObj.lastUsedTime = Time.time;
|
||||||
|
pooledObj.isActive = false;
|
||||||
|
pooledObj.instanceName = instantiate.name;
|
||||||
|
pooledObj.isRefCountReduced = false;
|
||||||
|
|
||||||
AllObjects.Add(pooledObj);
|
AllObjects.Add(pooledObj);
|
||||||
_gameObjectToPooledObject[instantiate] = pooledObj;
|
_gameObjectToPooledObject[instantiate] = pooledObj;
|
||||||
|
|
||||||
@ -435,6 +509,7 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
|
|
||||||
monitor.Initialize(this, pooledObj);
|
monitor.Initialize(this, pooledObj);
|
||||||
|
pooledObj.monitor = monitor;
|
||||||
return pooledObj;
|
return pooledObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,10 +553,19 @@ namespace AlicizaX
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 使用LINQ找到最旧的未使用对象(更高效)
|
// 使用LINQ找到最旧的未使用对象(更高效)
|
||||||
var oldestObj = AllObjects
|
PooledObject oldestObj = null;
|
||||||
.Where(obj => !obj.isActive)
|
foreach (var obj in AllObjects)
|
||||||
.OrderBy(obj => obj.lastUsedTime)
|
{
|
||||||
.FirstOrDefault();
|
if (obj.isActive)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldestObj == null || obj.lastUsedTime < oldestObj.lastUsedTime)
|
||||||
|
{
|
||||||
|
oldestObj = obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (oldestObj != null)
|
if (oldestObj != null)
|
||||||
{
|
{
|
||||||
@ -543,10 +627,11 @@ namespace AlicizaX
|
|||||||
if (!pooledObj.isRefCountReduced)
|
if (!pooledObj.isRefCountReduced)
|
||||||
{
|
{
|
||||||
OnObjectReallyDestroyed(pooledObj);
|
OnObjectReallyDestroyed(pooledObj);
|
||||||
|
MemoryPool.Release(pooledObj);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 只需要从集合中移除
|
// DestroyPooledObject 已调用 Detach,此分支理论上不可达
|
||||||
AllObjects.Remove(pooledObj);
|
AllObjects.Remove(pooledObj);
|
||||||
CleanAvailableQueue(pooledObj);
|
CleanAvailableQueue(pooledObj);
|
||||||
}
|
}
|
||||||
@ -618,8 +703,11 @@ namespace AlicizaX
|
|||||||
|
|
||||||
if (pooledObj.gameObject != null)
|
if (pooledObj.gameObject != null)
|
||||||
{
|
{
|
||||||
|
pooledObj.monitor?.Detach();
|
||||||
GameObject.Destroy(pooledObj.gameObject);
|
GameObject.Destroy(pooledObj.gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryPool.Release(pooledObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckExpiredObjects()
|
public void CheckExpiredObjects()
|
||||||
@ -645,10 +733,18 @@ namespace AlicizaX
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 重建所有路径的可用队列
|
// 重建所有路径的可用队列
|
||||||
foreach (var kvp in AvailableObjectsByPath.ToList())
|
_availableObjectPaths.Clear();
|
||||||
|
foreach (var assetPath in AvailableObjectsByPath.Keys)
|
||||||
{
|
{
|
||||||
var assetPath = kvp.Key;
|
_availableObjectPaths.Add(assetPath);
|
||||||
var queue = kvp.Value;
|
}
|
||||||
|
|
||||||
|
foreach (var assetPath in _availableObjectPaths)
|
||||||
|
{
|
||||||
|
if (!AvailableObjectsByPath.TryGetValue(assetPath, out var queue))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
_tempQueue.Clear();
|
_tempQueue.Clear();
|
||||||
while (queue.Count > 0)
|
while (queue.Count > 0)
|
||||||
@ -707,8 +803,11 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
if (obj.gameObject != null)
|
if (obj.gameObject != null)
|
||||||
{
|
{
|
||||||
|
obj.monitor?.Detach();
|
||||||
GameObject.Destroy(obj.gameObject);
|
GameObject.Destroy(obj.gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemoryPool.Release(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
AllObjects.Clear();
|
AllObjects.Clear();
|
||||||
@ -759,6 +858,12 @@ namespace AlicizaX
|
|||||||
_pooledObject = pooledObject;
|
_pooledObject = pooledObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Detach()
|
||||||
|
{
|
||||||
|
_pool = null;
|
||||||
|
_pooledObject = null;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
if (_pool != null && _pooledObject != null)
|
if (_pool != null && _pooledObject != null)
|
||||||
@ -806,6 +911,7 @@ namespace AlicizaX
|
|||||||
private List<PoolConfig> _poolConfigs;
|
private List<PoolConfig> _poolConfigs;
|
||||||
private List<ConfigPool> _configPools;
|
private List<ConfigPool> _configPools;
|
||||||
private Dictionary<GameObject, ConfigPool> _gameObjectToPool;
|
private Dictionary<GameObject, ConfigPool> _gameObjectToPool;
|
||||||
|
private Dictionary<string, ConfigPool> _configPoolCache;
|
||||||
|
|
||||||
// 重用预加载对象列表
|
// 重用预加载对象列表
|
||||||
private static readonly List<GameObject> _preloadedObjects = new List<GameObject>();
|
private static readonly List<GameObject> _preloadedObjects = new List<GameObject>();
|
||||||
@ -836,6 +942,7 @@ namespace AlicizaX
|
|||||||
|
|
||||||
_configPools = new List<ConfigPool>();
|
_configPools = new List<ConfigPool>();
|
||||||
_gameObjectToPool = new Dictionary<GameObject, ConfigPool>();
|
_gameObjectToPool = new Dictionary<GameObject, ConfigPool>();
|
||||||
|
_configPoolCache = new Dictionary<string, ConfigPool>();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -906,13 +1013,31 @@ namespace AlicizaX
|
|||||||
// Editor专用的刷新。
|
// Editor专用的刷新。
|
||||||
private void UpdateInspectorInfo()
|
private void UpdateInspectorInfo()
|
||||||
{
|
{
|
||||||
poolInfos.Clear();
|
if (_configPools == null)
|
||||||
|
{
|
||||||
|
ReleaseInspectorInfos(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int poolIndex = 0;
|
||||||
foreach (var pool in _configPools)
|
foreach (var pool in _configPools)
|
||||||
{
|
{
|
||||||
var info = new ConfigPoolInfo();
|
ConfigPoolInfo info;
|
||||||
|
if (poolIndex < poolInfos.Count)
|
||||||
|
{
|
||||||
|
info = poolInfos[poolIndex];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info = MemoryPool.Acquire<ConfigPoolInfo>();
|
||||||
|
poolInfos.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
info.UpdateFromPool(pool);
|
info.UpdateFromPool(pool);
|
||||||
poolInfos.Add(info);
|
poolIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReleaseInspectorInfos(poolIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetResourceLoader(IResourceLoader resourceLoader)
|
public void SetResourceLoader(IResourceLoader resourceLoader)
|
||||||
@ -1037,14 +1162,36 @@ namespace AlicizaX
|
|||||||
|
|
||||||
private ConfigPool FindConfigPool(string assetPath)
|
private ConfigPool FindConfigPool(string assetPath)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(assetPath))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_configPools == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_configPoolCache == null)
|
||||||
|
{
|
||||||
|
_configPoolCache = new Dictionary<string, ConfigPool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_configPoolCache != null && _configPoolCache.TryGetValue(assetPath, out var cachedPool))
|
||||||
|
{
|
||||||
|
return cachedPool;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var pool in _configPools)
|
foreach (var pool in _configPools)
|
||||||
{
|
{
|
||||||
if (pool.MatchesAsset(assetPath))
|
if (pool.MatchesAsset(assetPath))
|
||||||
{
|
{
|
||||||
|
_configPoolCache[assetPath] = pool;
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_configPoolCache[assetPath] = null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,13 +1205,35 @@ namespace AlicizaX
|
|||||||
|
|
||||||
public void ClearAllPools()
|
public void ClearAllPools()
|
||||||
{
|
{
|
||||||
foreach (var pool in _configPools)
|
if (_configPools != null)
|
||||||
{
|
{
|
||||||
pool.Clear();
|
foreach (var pool in _configPools)
|
||||||
|
{
|
||||||
|
pool.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_gameObjectToPool.Clear();
|
if (_gameObjectToPool != null)
|
||||||
poolInfos.Clear();
|
{
|
||||||
|
_gameObjectToPool.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_configPoolCache != null)
|
||||||
|
{
|
||||||
|
_configPoolCache.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReleaseInspectorInfos(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseInspectorInfos(int keepCount)
|
||||||
|
{
|
||||||
|
while (poolInfos.Count > keepCount)
|
||||||
|
{
|
||||||
|
int lastIndex = poolInfos.Count - 1;
|
||||||
|
MemoryPool.Release(poolInfos[lastIndex]);
|
||||||
|
poolInfos.RemoveAt(lastIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
@ -1175,7 +1344,7 @@ namespace AlicizaX
|
|||||||
{
|
{
|
||||||
public static GameObject LoadGameObject(string assetPath, Transform parent = null)
|
public static GameObject LoadGameObject(string assetPath, Transform parent = null)
|
||||||
{
|
{
|
||||||
return GameObjectPool.Instance.GetGameObject(assetPath);
|
return GameObjectPool.Instance.GetGameObject(assetPath, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async UniTask<GameObject> LoadGameObjectAsync(string assetPath, Transform parent = null, CancellationToken cancellationToken = default)
|
public static async UniTask<GameObject> LoadGameObjectAsync(string assetPath, Transform parent = null, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user