修复GameObjectPool class未池化的开销

This commit is contained in:
陈思海 2026-03-25 18:29:53 +08:00
parent 38f731e240
commit 4a0465450a

View File

@ -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)