You've already forked ParticleEffectForUGUI
mirror of
https://github.com/mob-sakai/ParticleEffectForUGUI.git
synced 2026-05-14 12:10:08 +00:00
import Coffee.Internal
This commit is contained in:
209
Runtime/Internal/Utilities/ObjectRepository.cs
Normal file
209
Runtime/Internal/Utilities/ObjectRepository.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace Coffee.UIParticleInternal
|
||||
{
|
||||
internal class ObjectRepository<T> where T : Object
|
||||
{
|
||||
private readonly Dictionary<Hash128, Entry> _cache = new Dictionary<Hash128, Entry>(8);
|
||||
private readonly Dictionary<int, Hash128> _objectKey = new Dictionary<int, Hash128>(8);
|
||||
private readonly string _name;
|
||||
private readonly Action<T> _onRelease;
|
||||
private readonly Stack<Entry> _pool = new Stack<Entry>(8);
|
||||
|
||||
public ObjectRepository(Action<T> onRelease = null)
|
||||
{
|
||||
_name = $"{typeof(T).Name}Repository";
|
||||
if (onRelease == null)
|
||||
{
|
||||
_onRelease = x =>
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Object.DestroyImmediate(x, false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Object.Destroy(x);
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
_onRelease = onRelease;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
_pool.Push(new Entry());
|
||||
}
|
||||
}
|
||||
|
||||
public int count => _cache.Count;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var kv in _cache)
|
||||
{
|
||||
var entry = kv.Value;
|
||||
if (entry == null) continue;
|
||||
|
||||
entry.Release(_onRelease);
|
||||
_pool.Push(entry);
|
||||
}
|
||||
|
||||
_cache.Clear();
|
||||
_objectKey.Clear();
|
||||
}
|
||||
|
||||
public bool Valid(Hash128 hash, T obj)
|
||||
{
|
||||
return _cache.TryGetValue(hash, out var entry) && entry.storedObject == obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or retrieves a cached object based on the hash.
|
||||
/// </summary>
|
||||
public void Get(Hash128 hash, ref T obj, Func<T> onCreate)
|
||||
{
|
||||
if (GetFromCache(hash, ref obj)) return;
|
||||
Add(hash, ref obj, onCreate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds or retrieves a cached object based on the hash.
|
||||
/// </summary>
|
||||
public void Get<TS>(Hash128 hash, ref T obj, Func<TS, T> onCreate, TS source)
|
||||
{
|
||||
if (GetFromCache(hash, ref obj)) return;
|
||||
Add(hash, ref obj, onCreate(source));
|
||||
}
|
||||
|
||||
private bool GetFromCache(Hash128 hash, ref T obj)
|
||||
{
|
||||
// Find existing entry.
|
||||
Profiler.BeginSample("(COF)[ObjectRepository] GetFromCache");
|
||||
if (_cache.TryGetValue(hash, out var entry))
|
||||
{
|
||||
if (!entry.storedObject)
|
||||
{
|
||||
Release(ref entry.storedObject);
|
||||
Profiler.EndSample();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry.storedObject != obj)
|
||||
{
|
||||
// if the object is different, release the old one.
|
||||
Release(ref obj);
|
||||
++entry.reference;
|
||||
obj = entry.storedObject;
|
||||
Logging.Log(_name, $"Get(total#{count}): {entry}");
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
return true;
|
||||
}
|
||||
|
||||
Profiler.EndSample();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void Add(Hash128 hash, ref T obj, T newObject)
|
||||
{
|
||||
if (!newObject)
|
||||
{
|
||||
Release(ref obj);
|
||||
obj = newObject;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and add a new entry.
|
||||
Profiler.BeginSample("(COF)[ObjectRepository] Add");
|
||||
var newEntry = 0 < _pool.Count ? _pool.Pop() : new Entry();
|
||||
newEntry.storedObject = newObject;
|
||||
newEntry.hash = hash;
|
||||
newEntry.reference = 1;
|
||||
_cache[hash] = newEntry;
|
||||
_objectKey[newObject.GetInstanceID()] = hash;
|
||||
Logging.Log(_name, $"<color=#03c700>Add</color>(total#{count}): {newEntry}");
|
||||
Release(ref obj);
|
||||
obj = newObject;
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release a object.
|
||||
/// </summary>
|
||||
public void Release(ref T obj)
|
||||
{
|
||||
if (ReferenceEquals(obj, null)) return;
|
||||
|
||||
// Find and release the entry.
|
||||
Profiler.BeginSample("(COF)[ObjectRepository] Release");
|
||||
var id = obj.GetInstanceID();
|
||||
if (_objectKey.TryGetValue(id, out var hash)
|
||||
&& _cache.TryGetValue(hash, out var entry))
|
||||
{
|
||||
entry.reference--;
|
||||
if (entry.reference <= 0 || !entry.storedObject)
|
||||
{
|
||||
Remove(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(_name, $"Release(total#{_cache.Count}): {entry}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Log(_name, $"Release(total#{_cache.Count}): <color=red>Already released: {obj}</color>");
|
||||
}
|
||||
|
||||
obj = null;
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
private void Remove(Entry entry)
|
||||
{
|
||||
if (ReferenceEquals(entry, null)) return;
|
||||
|
||||
Profiler.BeginSample("(COF)[ObjectRepository] Remove");
|
||||
_cache.Remove(entry.hash);
|
||||
_objectKey.Remove(entry.storedObject.GetInstanceID());
|
||||
_pool.Push(entry);
|
||||
entry.reference = 0;
|
||||
Logging.Log(_name, $"<color=#f29e03>Remove</color>(total#{_cache.Count}): {entry}");
|
||||
entry.Release(_onRelease);
|
||||
Profiler.EndSample();
|
||||
}
|
||||
|
||||
private class Entry
|
||||
{
|
||||
public Hash128 hash;
|
||||
public int reference;
|
||||
public T storedObject;
|
||||
|
||||
public void Release(Action<T> onRelease)
|
||||
{
|
||||
reference = 0;
|
||||
if (storedObject)
|
||||
{
|
||||
onRelease?.Invoke(storedObject);
|
||||
}
|
||||
|
||||
storedObject = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"h{(uint)hash.GetHashCode()} (refs#{reference}), {storedObject}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user