[Opt] ObjectPool&&Timer
This commit is contained in:
parent
4c134af495
commit
58fb685792
@ -8,7 +8,7 @@ namespace AlicizaX.Timer.Editor
|
|||||||
[CustomEditor(typeof(TimerComponent))]
|
[CustomEditor(typeof(TimerComponent))]
|
||||||
internal sealed class TimerComponentInspector : GameFrameworkInspector
|
internal sealed class TimerComponentInspector : GameFrameworkInspector
|
||||||
{
|
{
|
||||||
private const double UPDATE_INTERVAL = 0.001d;
|
private const double UPDATE_INTERVAL = 0.25d;
|
||||||
private const int MAX_DISPLAY_COUNT = 20;
|
private const int MAX_DISPLAY_COUNT = 20;
|
||||||
|
|
||||||
private TimerDebugInfo[] _timerBuffer;
|
private TimerDebugInfo[] _timerBuffer;
|
||||||
@ -95,20 +95,23 @@ namespace AlicizaX.Timer.Editor
|
|||||||
int timerCount = debug.GetAllTimers(_timerBuffer);
|
int timerCount = debug.GetAllTimers(_timerBuffer);
|
||||||
int displayCount = Mathf.Min(timerCount, MAX_DISPLAY_COUNT);
|
int displayCount = Mathf.Min(timerCount, MAX_DISPLAY_COUNT);
|
||||||
|
|
||||||
if (displayCount < timerCount)
|
if (displayCount < activeCount)
|
||||||
{
|
{
|
||||||
EditorGUILayout.HelpBox(Utility.Text.Format("Showing first {0} timers of {1}.", displayCount, timerCount), MessageType.Info);
|
EditorGUILayout.HelpBox(Utility.Text.Format("Showing first {0} timers of {1}.", displayCount, activeCount), MessageType.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < displayCount; i++)
|
for (int i = 0; i < displayCount; i++)
|
||||||
{
|
{
|
||||||
TimerDebugInfo timer = _timerBuffer[i];
|
TimerDebugInfo timer = _timerBuffer[i];
|
||||||
|
bool isLoop = (timer.Flags & TimerDebugFlags.Loop) != 0;
|
||||||
|
bool isRunning = (timer.Flags & TimerDebugFlags.Running) != 0;
|
||||||
|
bool isUnscaled = (timer.Flags & TimerDebugFlags.Unscaled) != 0;
|
||||||
string label = Utility.Text.Format(
|
string label = Utility.Text.Format(
|
||||||
"ID {0} | {1} | {2} | {3}",
|
"ID {0} | {1} | {2} | {3}",
|
||||||
timer.TimerId,
|
timer.TimerHandle,
|
||||||
timer.IsLoop ? "Loop" : "Once",
|
isLoop ? "Loop" : "Once",
|
||||||
timer.IsUnscaled ? "Unscaled" : "Scaled",
|
isUnscaled ? "Unscaled" : "Scaled",
|
||||||
timer.IsRunning ? "Running" : "Paused");
|
isRunning ? "Running" : "Paused");
|
||||||
|
|
||||||
string value = Utility.Text.Format(
|
string value = Utility.Text.Format(
|
||||||
"Left {0:F2}s | Duration {1:F2}s",
|
"Left {0:F2}s | Duration {1:F2}s",
|
||||||
@ -142,26 +145,28 @@ namespace AlicizaX.Timer.Editor
|
|||||||
{
|
{
|
||||||
TimerDebugInfo staleTimer = _leakBuffer[i];
|
TimerDebugInfo staleTimer = _leakBuffer[i];
|
||||||
EditorGUILayout.LabelField(
|
EditorGUILayout.LabelField(
|
||||||
Utility.Text.Format("ID {0}", staleTimer.TimerId),
|
Utility.Text.Format("ID {0}", staleTimer.TimerHandle),
|
||||||
Utility.Text.Format("Created {0:F1}s ago", staleTimer.CreationTime));
|
Utility.Text.Format("Created {0:F1}s ago", staleTimer.Age));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private void EnsureTimerBuffer(int count)
|
private void EnsureTimerBuffer(int count)
|
||||||
{
|
{
|
||||||
if (_timerBuffer == null || _timerBuffer.Length < count)
|
int capacity = count > 0 ? count : 1;
|
||||||
|
if (_timerBuffer == null || _timerBuffer.Length < capacity)
|
||||||
{
|
{
|
||||||
_timerBuffer = new TimerDebugInfo[count];
|
_timerBuffer = new TimerDebugInfo[capacity];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
private void EnsureLeakBuffer(int count)
|
private void EnsureLeakBuffer(int count)
|
||||||
{
|
{
|
||||||
if (_leakBuffer == null || _leakBuffer.Length < count)
|
int capacity = count > 0 ? count : 1;
|
||||||
|
if (_leakBuffer == null || _leakBuffer.Length < capacity)
|
||||||
{
|
{
|
||||||
_leakBuffer = new TimerDebugInfo[count];
|
_leakBuffer = new TimerDebugInfo[capacity];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -9,31 +9,53 @@ namespace AlicizaX.Debugger.Runtime
|
|||||||
private sealed class TimerInformationWindow : ScrollableDebuggerWindowBase
|
private sealed class TimerInformationWindow : ScrollableDebuggerWindowBase
|
||||||
{
|
{
|
||||||
private const int MAX_DISPLAY_COUNT = 50;
|
private const int MAX_DISPLAY_COUNT = 50;
|
||||||
|
private const float REFRESH_INTERVAL = 0.25f;
|
||||||
|
private const string OVERFLOW_NOTE = "Showing first 50 active timers.";
|
||||||
|
private const string EMPTY_NOTE = "No active timers.";
|
||||||
|
private const string SAMPLE_NOTE = "Zero-allocation runtime sample view.";
|
||||||
|
|
||||||
private struct RowView
|
private struct RowView
|
||||||
{
|
{
|
||||||
public VisualElement Root;
|
public VisualElement Root;
|
||||||
public Label Title;
|
public VisualElement LoopIndicator;
|
||||||
public Label Value;
|
public VisualElement ScaleIndicator;
|
||||||
|
public VisualElement StateIndicator;
|
||||||
|
public VisualElement Fill;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ITimerServiceDebug _mTimerDebug;
|
private ITimerServiceDebug _mTimerDebug;
|
||||||
private TimerDebugInfo[] m_TimerInfos;
|
private readonly TimerDebugInfo[] m_TimerInfos = new TimerDebugInfo[MAX_DISPLAY_COUNT];
|
||||||
private Label m_SectionTitleLabel;
|
|
||||||
private Label m_ActiveCountLabel;
|
|
||||||
private Label m_PoolCapacityLabel;
|
|
||||||
private Label m_PeakCountLabel;
|
|
||||||
private Label m_FreeCountLabel;
|
|
||||||
private Label m_UsageLabel;
|
|
||||||
private Label m_WarningLabel;
|
|
||||||
private RowView m_EmptyRow;
|
|
||||||
private readonly RowView[] m_TimerRows = new RowView[MAX_DISPLAY_COUNT];
|
private readonly RowView[] m_TimerRows = new RowView[MAX_DISPLAY_COUNT];
|
||||||
|
private VisualElement m_ActiveUsageFill;
|
||||||
|
private VisualElement m_PeakUsageFill;
|
||||||
|
private VisualElement m_FreeUsageFill;
|
||||||
|
private VisualElement m_OverflowNote;
|
||||||
|
private VisualElement m_EmptyNote;
|
||||||
|
private float m_RefreshCountdown;
|
||||||
|
|
||||||
public override void Initialize(params object[] args)
|
public override void Initialize(params object[] args)
|
||||||
{
|
{
|
||||||
_mTimerDebug = AppServices.Require<ITimerService>() as ITimerServiceDebug;
|
_mTimerDebug = AppServices.Require<ITimerService>() as ITimerServiceDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnEnter()
|
||||||
|
{
|
||||||
|
m_RefreshCountdown = 0f;
|
||||||
|
RefreshContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
||||||
|
{
|
||||||
|
m_RefreshCountdown -= realElapseSeconds;
|
||||||
|
if (m_RefreshCountdown > 0f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_RefreshCountdown = REFRESH_INTERVAL;
|
||||||
|
RefreshContent();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void BuildWindow(VisualElement root)
|
protected override void BuildWindow(VisualElement root)
|
||||||
{
|
{
|
||||||
if (_mTimerDebug == null)
|
if (_mTimerDebug == null)
|
||||||
@ -44,91 +66,61 @@ namespace AlicizaX.Debugger.Runtime
|
|||||||
root.Add(CreateActionButton("Refresh", RefreshContent, DebuggerTheme.ButtonSurfaceActive, DebuggerTheme.PrimaryText));
|
root.Add(CreateActionButton("Refresh", RefreshContent, DebuggerTheme.ButtonSurfaceActive, DebuggerTheme.PrimaryText));
|
||||||
|
|
||||||
VisualElement overview = CreateSection("Timer Pool Overview", out VisualElement overviewCard);
|
VisualElement overview = CreateSection("Timer Pool Overview", out VisualElement overviewCard);
|
||||||
m_ActiveCountLabel = AddTextRow(overviewCard, "Active Timer Count").Value;
|
overviewCard.Add(CreateUsageRow("Active Usage", DebuggerTheme.Accent, out m_ActiveUsageFill));
|
||||||
m_PoolCapacityLabel = AddTextRow(overviewCard, "Pool Capacity").Value;
|
overviewCard.Add(CreateUsageRow("Peak Usage", DebuggerTheme.Warning, out m_PeakUsageFill));
|
||||||
m_PeakCountLabel = AddTextRow(overviewCard, "Peak Active Count").Value;
|
overviewCard.Add(CreateUsageRow("Free Capacity", DebuggerTheme.Positive, out m_FreeUsageFill));
|
||||||
m_FreeCountLabel = AddTextRow(overviewCard, "Free Count").Value;
|
|
||||||
m_UsageLabel = AddTextRow(overviewCard, "Pool Usage").Value;
|
|
||||||
root.Add(overview);
|
root.Add(overview);
|
||||||
|
|
||||||
VisualElement section = CreateSection("Active Timers", out VisualElement timerCard);
|
VisualElement sample = CreateSection("Timer Sample", out VisualElement sampleCard);
|
||||||
m_SectionTitleLabel = section.ElementAt(0) as Label;
|
sampleCard.Add(CreateNoteLabel(SAMPLE_NOTE, DebuggerTheme.SecondaryText));
|
||||||
m_WarningLabel = new Label();
|
m_OverflowNote = CreateNoteLabel(OVERFLOW_NOTE, new Color(1f, 0.5f, 0f));
|
||||||
m_WarningLabel.style.color = new Color(1f, 0.5f, 0f);
|
m_OverflowNote.style.display = DisplayStyle.None;
|
||||||
m_WarningLabel.style.display = DisplayStyle.None;
|
sampleCard.Add(m_OverflowNote);
|
||||||
m_WarningLabel.style.marginBottom = 4f;
|
|
||||||
timerCard.Add(m_WarningLabel);
|
|
||||||
|
|
||||||
m_EmptyRow = AddTextRow(timerCard, string.Empty);
|
m_EmptyNote = CreateNoteLabel(EMPTY_NOTE, DebuggerTheme.SecondaryText);
|
||||||
m_EmptyRow.Root.style.display = DisplayStyle.None;
|
m_EmptyNote.style.display = DisplayStyle.None;
|
||||||
|
sampleCard.Add(m_EmptyNote);
|
||||||
|
|
||||||
for (int i = 0; i < MAX_DISPLAY_COUNT; i++)
|
for (int i = 0; i < MAX_DISPLAY_COUNT; i++)
|
||||||
{
|
{
|
||||||
m_TimerRows[i] = AddTextRow(timerCard, string.Empty);
|
m_TimerRows[i] = CreateTimerRow(sampleCard);
|
||||||
m_TimerRows[i].Root.style.display = DisplayStyle.None;
|
m_TimerRows[i].Root.style.display = DisplayStyle.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
root.Add(section);
|
root.Add(sample);
|
||||||
RefreshContent();
|
RefreshContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshContent()
|
private void RefreshContent()
|
||||||
{
|
{
|
||||||
if (_mTimerDebug == null)
|
if (_mTimerDebug == null || m_ActiveUsageFill == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mTimerDebug.GetStatistics(out int activeCount, out int poolCapacity, out int peakActiveCount, out int freeCount);
|
_mTimerDebug.GetStatistics(out int activeCount, out int poolCapacity, out int peakActiveCount, out int freeCount);
|
||||||
float poolUsage = poolCapacity > 0 ? (float)activeCount / poolCapacity : 0f;
|
float capacity = poolCapacity > 0 ? poolCapacity : 1f;
|
||||||
|
SetFillRatio(m_ActiveUsageFill, activeCount / capacity);
|
||||||
m_ActiveCountLabel.text = activeCount.ToString();
|
SetFillRatio(m_PeakUsageFill, peakActiveCount / capacity);
|
||||||
m_PoolCapacityLabel.text = poolCapacity.ToString();
|
SetFillRatio(m_FreeUsageFill, freeCount / capacity);
|
||||||
m_PeakCountLabel.text = peakActiveCount.ToString();
|
|
||||||
m_FreeCountLabel.text = freeCount.ToString();
|
|
||||||
m_UsageLabel.text = Utility.Text.Format("{0:P1}", poolUsage);
|
|
||||||
|
|
||||||
if (activeCount <= 0)
|
if (activeCount <= 0)
|
||||||
{
|
{
|
||||||
m_SectionTitleLabel.text = "Active Timers";
|
m_EmptyNote.style.display = DisplayStyle.Flex;
|
||||||
m_WarningLabel.style.display = DisplayStyle.None;
|
m_OverflowNote.style.display = DisplayStyle.None;
|
||||||
m_EmptyRow.Root.style.display = DisplayStyle.Flex;
|
|
||||||
m_EmptyRow.Title.text = "Status";
|
|
||||||
m_EmptyRow.Value.text = "No active timers";
|
|
||||||
SetTimerRowsVisible(0);
|
SetTimerRowsVisible(0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnsureTimerInfoBuffer(activeCount);
|
m_EmptyNote.style.display = DisplayStyle.None;
|
||||||
|
|
||||||
int timerCount = _mTimerDebug.GetAllTimers(m_TimerInfos);
|
int timerCount = _mTimerDebug.GetAllTimers(m_TimerInfos);
|
||||||
int displayCount = timerCount > MAX_DISPLAY_COUNT ? MAX_DISPLAY_COUNT : timerCount;
|
int displayCount = timerCount > MAX_DISPLAY_COUNT ? MAX_DISPLAY_COUNT : timerCount;
|
||||||
|
m_OverflowNote.style.display = activeCount > MAX_DISPLAY_COUNT ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
m_SectionTitleLabel.text = Utility.Text.Format("Active Timers ({0})", timerCount);
|
|
||||||
m_EmptyRow.Root.style.display = DisplayStyle.None;
|
|
||||||
|
|
||||||
if (displayCount < timerCount)
|
|
||||||
{
|
|
||||||
m_WarningLabel.text = Utility.Text.Format("Showing first {0} timers of {1}.", displayCount, timerCount);
|
|
||||||
m_WarningLabel.style.display = DisplayStyle.Flex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_WarningLabel.style.display = DisplayStyle.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < displayCount; i++)
|
for (int i = 0; i < displayCount; i++)
|
||||||
{
|
{
|
||||||
ref RowView row = ref m_TimerRows[i];
|
UpdateTimerRow(ref m_TimerRows[i], ref m_TimerInfos[i]);
|
||||||
TimerDebugInfo info = m_TimerInfos[i];
|
|
||||||
row.Title.text = Utility.Text.Format("Timer #{0}", info.TimerId);
|
|
||||||
row.Value.text = Utility.Text.Format(
|
|
||||||
"{0} | {1} | {2} | Remaining: {3:F2}s | Duration: {4:F2}s",
|
|
||||||
info.IsLoop ? "Loop" : "Once",
|
|
||||||
info.IsRunning ? "Running" : "Paused",
|
|
||||||
info.IsUnscaled ? "Unscaled" : "Scaled",
|
|
||||||
info.LeftTime,
|
|
||||||
info.Duration);
|
|
||||||
row.Root.style.display = DisplayStyle.Flex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetTimerRowsVisible(displayCount);
|
SetTimerRowsVisible(displayCount);
|
||||||
@ -142,58 +134,146 @@ namespace AlicizaX.Debugger.Runtime
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RowView AddTextRow(VisualElement parent, string title)
|
private static VisualElement CreateUsageRow(string title, Color fillColor, out VisualElement fill)
|
||||||
|
{
|
||||||
|
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
|
||||||
|
VisualElement row = new VisualElement();
|
||||||
|
row.style.flexDirection = FlexDirection.Column;
|
||||||
|
row.style.marginBottom = 8f * scale;
|
||||||
|
|
||||||
|
Label titleLabel = new Label(title);
|
||||||
|
titleLabel.style.color = DebuggerTheme.SecondaryText;
|
||||||
|
titleLabel.style.fontSize = 16f * scale;
|
||||||
|
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||||
|
titleLabel.style.marginBottom = 4f * scale;
|
||||||
|
row.Add(titleLabel);
|
||||||
|
|
||||||
|
VisualElement track = new VisualElement();
|
||||||
|
track.style.height = 14f * scale;
|
||||||
|
track.style.backgroundColor = DebuggerTheme.PanelSurfaceStrong;
|
||||||
|
track.style.borderTopLeftRadius = 4f * scale;
|
||||||
|
track.style.borderTopRightRadius = 4f * scale;
|
||||||
|
track.style.borderBottomLeftRadius = 4f * scale;
|
||||||
|
track.style.borderBottomRightRadius = 4f * scale;
|
||||||
|
track.style.overflow = Overflow.Hidden;
|
||||||
|
|
||||||
|
fill = new VisualElement();
|
||||||
|
fill.style.height = Length.Percent(100f);
|
||||||
|
fill.style.width = Length.Percent(0f);
|
||||||
|
fill.style.backgroundColor = fillColor;
|
||||||
|
track.Add(fill);
|
||||||
|
row.Add(track);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VisualElement CreateNoteLabel(string text, Color color)
|
||||||
|
{
|
||||||
|
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
|
||||||
|
Label label = new Label(text);
|
||||||
|
label.style.color = color;
|
||||||
|
label.style.fontSize = 15f * scale;
|
||||||
|
label.style.marginBottom = 6f * scale;
|
||||||
|
label.style.whiteSpace = WhiteSpace.Normal;
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RowView CreateTimerRow(VisualElement parent)
|
||||||
{
|
{
|
||||||
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
|
float scale = DebuggerComponent.Instance != null ? DebuggerComponent.Instance.GetUiScale() : 1f;
|
||||||
VisualElement row = new VisualElement();
|
VisualElement row = new VisualElement();
|
||||||
row.style.flexDirection = FlexDirection.Row;
|
row.style.flexDirection = FlexDirection.Row;
|
||||||
row.style.alignItems = Align.Center;
|
row.style.alignItems = Align.Center;
|
||||||
row.style.minHeight = 36f * scale;
|
row.style.height = 20f * scale;
|
||||||
row.style.marginBottom = 4f * scale;
|
row.style.marginBottom = 4f * scale;
|
||||||
|
|
||||||
Label titleLabel = new Label(title);
|
VisualElement loopIndicator = CreateIndicator(6f * scale, DebuggerTheme.Accent);
|
||||||
titleLabel.style.minWidth = 280f * scale;
|
VisualElement scaleIndicator = CreateIndicator(6f * scale, DebuggerTheme.Warning);
|
||||||
titleLabel.style.maxWidth = 280f * scale;
|
VisualElement stateIndicator = CreateIndicator(6f * scale, DebuggerTheme.Positive);
|
||||||
titleLabel.style.color = DebuggerTheme.SecondaryText;
|
|
||||||
titleLabel.style.fontSize = 18f * scale;
|
|
||||||
titleLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
|
||||||
titleLabel.style.flexShrink = 0f;
|
|
||||||
titleLabel.style.whiteSpace = WhiteSpace.Normal;
|
|
||||||
|
|
||||||
Label valueLabel = new Label();
|
row.Add(loopIndicator);
|
||||||
valueLabel.style.flexGrow = 1f;
|
row.Add(scaleIndicator);
|
||||||
valueLabel.style.color = DebuggerTheme.PrimaryText;
|
row.Add(stateIndicator);
|
||||||
valueLabel.style.fontSize = 18f * scale;
|
|
||||||
valueLabel.style.whiteSpace = WhiteSpace.Normal;
|
|
||||||
|
|
||||||
row.Add(titleLabel);
|
VisualElement track = new VisualElement();
|
||||||
row.Add(valueLabel);
|
track.style.flexGrow = 1f;
|
||||||
|
track.style.height = 14f * scale;
|
||||||
|
track.style.backgroundColor = DebuggerTheme.PanelSurfaceStrong;
|
||||||
|
track.style.borderTopLeftRadius = 4f * scale;
|
||||||
|
track.style.borderTopRightRadius = 4f * scale;
|
||||||
|
track.style.borderBottomLeftRadius = 4f * scale;
|
||||||
|
track.style.borderBottomRightRadius = 4f * scale;
|
||||||
|
track.style.overflow = Overflow.Hidden;
|
||||||
|
|
||||||
|
VisualElement fill = new VisualElement();
|
||||||
|
fill.style.height = Length.Percent(100f);
|
||||||
|
fill.style.width = Length.Percent(0f);
|
||||||
|
fill.style.backgroundColor = DebuggerTheme.Positive;
|
||||||
|
track.Add(fill);
|
||||||
|
row.Add(track);
|
||||||
parent.Add(row);
|
parent.Add(row);
|
||||||
|
|
||||||
RowView view;
|
RowView view;
|
||||||
view.Root = row;
|
view.Root = row;
|
||||||
view.Title = titleLabel;
|
view.LoopIndicator = loopIndicator;
|
||||||
view.Value = valueLabel;
|
view.ScaleIndicator = scaleIndicator;
|
||||||
|
view.StateIndicator = stateIndicator;
|
||||||
|
view.Fill = fill;
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int EnsureTimerInfoBuffer(int count)
|
private static VisualElement CreateIndicator(float size, Color color)
|
||||||
{
|
{
|
||||||
if (count <= 0)
|
VisualElement indicator = new VisualElement();
|
||||||
|
indicator.style.width = size;
|
||||||
|
indicator.style.height = size;
|
||||||
|
indicator.style.marginRight = size;
|
||||||
|
indicator.style.backgroundColor = color;
|
||||||
|
indicator.style.opacity = 0.2f;
|
||||||
|
return indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateTimerRow(ref RowView row, ref TimerDebugInfo info)
|
||||||
|
{
|
||||||
|
byte flags = info.Flags;
|
||||||
|
bool isRunning = (flags & TimerDebugFlags.Running) != 0;
|
||||||
|
bool isLoop = (flags & TimerDebugFlags.Loop) != 0;
|
||||||
|
bool isUnscaled = (flags & TimerDebugFlags.Unscaled) != 0;
|
||||||
|
float duration = info.Duration;
|
||||||
|
float ratio = duration > 0f ? info.LeftTime / duration : 0f;
|
||||||
|
if (ratio < 0f)
|
||||||
{
|
{
|
||||||
if (m_TimerInfos == null || m_TimerInfos.Length == 0)
|
ratio = 0f;
|
||||||
{
|
}
|
||||||
m_TimerInfos = new TimerDebugInfo[1];
|
else if (ratio > 1f)
|
||||||
}
|
{
|
||||||
return 0;
|
ratio = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_TimerInfos == null || m_TimerInfos.Length < count)
|
row.Root.style.display = DisplayStyle.Flex;
|
||||||
|
row.LoopIndicator.style.opacity = isLoop ? 1f : 0.2f;
|
||||||
|
row.ScaleIndicator.style.opacity = isUnscaled ? 1f : 0.35f;
|
||||||
|
row.ScaleIndicator.style.backgroundColor = isUnscaled ? DebuggerTheme.Warning : DebuggerTheme.Accent;
|
||||||
|
row.StateIndicator.style.opacity = isRunning ? 1f : 0.45f;
|
||||||
|
row.StateIndicator.style.backgroundColor = isRunning ? DebuggerTheme.Positive : DebuggerTheme.Warning;
|
||||||
|
row.Fill.style.width = Length.Percent(ratio * 100f);
|
||||||
|
row.Fill.style.backgroundColor = isRunning
|
||||||
|
? ratio <= 0.2f ? DebuggerTheme.Warning : DebuggerTheme.Positive
|
||||||
|
: DebuggerTheme.SecondaryText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetFillRatio(VisualElement fill, float ratio)
|
||||||
|
{
|
||||||
|
if (ratio < 0f)
|
||||||
{
|
{
|
||||||
m_TimerInfos = new TimerDebugInfo[count];
|
ratio = 0f;
|
||||||
|
}
|
||||||
|
else if (ratio > 1f)
|
||||||
|
{
|
||||||
|
ratio = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
fill.style.width = Length.Percent(ratio * 100f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
namespace AlicizaX.ObjectPool
|
namespace AlicizaX.ObjectPool
|
||||||
{
|
{
|
||||||
public readonly struct ObjectPoolCreateOptions
|
public readonly struct ObjectPoolCreateOptions
|
||||||
@ -9,7 +8,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
public readonly int? Capacity;
|
public readonly int? Capacity;
|
||||||
public readonly float? ExpireTime;
|
public readonly float? ExpireTime;
|
||||||
public readonly int Priority;
|
public readonly int Priority;
|
||||||
public readonly ReleaseStrategy ReleaseStrategy;
|
|
||||||
|
|
||||||
public ObjectPoolCreateOptions(
|
public ObjectPoolCreateOptions(
|
||||||
string name = "",
|
string name = "",
|
||||||
@ -17,8 +15,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
float? autoReleaseInterval = null,
|
float? autoReleaseInterval = null,
|
||||||
int? capacity = null,
|
int? capacity = null,
|
||||||
float? expireTime = null,
|
float? expireTime = null,
|
||||||
int priority = 0,
|
int priority = 0)
|
||||||
ReleaseStrategy releaseStrategy = ReleaseStrategy.LRU)
|
|
||||||
{
|
{
|
||||||
Name = name ?? string.Empty;
|
Name = name ?? string.Empty;
|
||||||
AllowMultiSpawn = allowMultiSpawn;
|
AllowMultiSpawn = allowMultiSpawn;
|
||||||
@ -26,11 +23,10 @@ namespace AlicizaX.ObjectPool
|
|||||||
Capacity = capacity;
|
Capacity = capacity;
|
||||||
ExpireTime = expireTime;
|
ExpireTime = expireTime;
|
||||||
Priority = priority;
|
Priority = priority;
|
||||||
ReleaseStrategy = releaseStrategy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectPoolCreateOptions WithName(string name)
|
public ObjectPoolCreateOptions WithName(string name)
|
||||||
=> new ObjectPoolCreateOptions(name, AllowMultiSpawn, AutoReleaseInterval, Capacity, ExpireTime, Priority, ReleaseStrategy);
|
=> new ObjectPoolCreateOptions(name, AllowMultiSpawn, AutoReleaseInterval, Capacity, ExpireTime, Priority);
|
||||||
|
|
||||||
public static ObjectPoolCreateOptions Single(string name = "")
|
public static ObjectPoolCreateOptions Single(string name = "")
|
||||||
=> new ObjectPoolCreateOptions(name: name);
|
=> new ObjectPoolCreateOptions(name: name);
|
||||||
@ -46,23 +42,15 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
bool HasObjectPool<T>() where T : ObjectBase;
|
bool HasObjectPool<T>() where T : ObjectBase;
|
||||||
bool HasObjectPool<T>(string name) where T : ObjectBase;
|
bool HasObjectPool<T>(string name) where T : ObjectBase;
|
||||||
bool HasObjectPool(Type objectType);
|
|
||||||
bool HasObjectPool(Type objectType, string name);
|
|
||||||
|
|
||||||
IObjectPool<T> GetObjectPool<T>() where T : ObjectBase;
|
IObjectPool<T> GetObjectPool<T>() where T : ObjectBase;
|
||||||
IObjectPool<T> GetObjectPool<T>(string name) where T : ObjectBase;
|
IObjectPool<T> GetObjectPool<T>(string name) where T : ObjectBase;
|
||||||
ObjectPoolBase GetObjectPool(Type objectType);
|
|
||||||
ObjectPoolBase GetObjectPool(Type objectType, string name);
|
|
||||||
|
|
||||||
IObjectPool<T> CreatePool<T>(ObjectPoolCreateOptions options = default) where T : ObjectBase;
|
IObjectPool<T> CreatePool<T>(ObjectPoolCreateOptions options = default) where T : ObjectBase;
|
||||||
ObjectPoolBase CreatePool(Type objectType, ObjectPoolCreateOptions options = default);
|
|
||||||
|
|
||||||
bool DestroyObjectPool<T>() where T : ObjectBase;
|
bool DestroyObjectPool<T>() where T : ObjectBase;
|
||||||
bool DestroyObjectPool<T>(string name) where T : ObjectBase;
|
bool DestroyObjectPool<T>(string name) where T : ObjectBase;
|
||||||
bool DestroyObjectPool(Type objectType);
|
|
||||||
bool DestroyObjectPool(Type objectType, string name);
|
|
||||||
bool DestroyObjectPool<T>(IObjectPool<T> objectPool) where T : ObjectBase;
|
bool DestroyObjectPool<T>(IObjectPool<T> objectPool) where T : ObjectBase;
|
||||||
bool DestroyObjectPool(ObjectPoolBase objectPool);
|
|
||||||
|
|
||||||
void Release();
|
void Release();
|
||||||
void ReleaseAllUnused();
|
void ReleaseAllUnused();
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
namespace AlicizaX.ObjectPool
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 可池化对象接口,支持自定义回收和重用逻辑
|
|
||||||
/// </summary>
|
|
||||||
public interface IPoolableObject
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 对象被回收到池中时调用(重置状态)
|
|
||||||
/// </summary>
|
|
||||||
void OnRecycle();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 对象从池中取出时调用(初始化状态)
|
|
||||||
/// </summary>
|
|
||||||
void OnReuse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 0c4c7354bf5820e408151d8ecbd1bab1
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 6e20a3f64bc47ae4dbf407342897a015
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@ -54,71 +54,4 @@ namespace AlicizaX.ObjectPool
|
|||||||
m_LastUseTime = 0f;
|
m_LastUseTime = 0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 泛型对象池基类,消除装箱开销
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ObjectBase<T> : IMemory where T : class
|
|
||||||
{
|
|
||||||
private string m_Name;
|
|
||||||
private T m_Target;
|
|
||||||
private bool m_Locked;
|
|
||||||
private float m_LastUseTime;
|
|
||||||
|
|
||||||
public string Name => m_Name;
|
|
||||||
public T Target => m_Target;
|
|
||||||
|
|
||||||
public bool Locked
|
|
||||||
{
|
|
||||||
get => m_Locked;
|
|
||||||
set => m_Locked = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float LastUseTime
|
|
||||||
{
|
|
||||||
get => m_LastUseTime;
|
|
||||||
internal set => m_LastUseTime = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool CustomCanReleaseFlag => true;
|
|
||||||
|
|
||||||
protected void Initialize(T target)
|
|
||||||
{
|
|
||||||
Initialize(string.Empty, target, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void Initialize(string name, T target)
|
|
||||||
{
|
|
||||||
Initialize(name, target, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void Initialize(string name, T target, bool locked)
|
|
||||||
{
|
|
||||||
m_Name = name ?? string.Empty;
|
|
||||||
m_Target = target;
|
|
||||||
m_Locked = locked;
|
|
||||||
m_LastUseTime = 0f;
|
|
||||||
|
|
||||||
if (target is IPoolableObject poolable)
|
|
||||||
poolable.OnReuse();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal virtual void OnSpawn() { }
|
|
||||||
|
|
||||||
protected internal virtual void OnUnspawn()
|
|
||||||
{
|
|
||||||
if (m_Target is IPoolableObject poolable)
|
|
||||||
poolable.OnRecycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal abstract void Release(bool isShutdown);
|
|
||||||
|
|
||||||
public virtual void Clear()
|
|
||||||
{
|
|
||||||
m_Name = null;
|
|
||||||
m_Target = null;
|
|
||||||
m_Locked = false;
|
|
||||||
m_LastUseTime = 0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,9 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace AlicizaX
|
namespace AlicizaX
|
||||||
{
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
[AddComponentMenu("Game Framework/ObjectPool")]
|
||||||
|
[UnityEngine.Scripting.Preserve]
|
||||||
public sealed class ObjectPoolComponent : MonoBehaviour
|
public sealed class ObjectPoolComponent : MonoBehaviour
|
||||||
{
|
{
|
||||||
private IObjectPoolService _mObjectPoolService;
|
private IObjectPoolService _mObjectPoolService;
|
||||||
|
|||||||
@ -36,11 +36,10 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
private ObjectSlot[] m_Slots;
|
private ObjectSlot[] m_Slots;
|
||||||
private int m_SlotCount;
|
private int m_SlotCount;
|
||||||
private int m_SlotCapacity;
|
|
||||||
private int[] m_FreeStack;
|
private int[] m_FreeStack;
|
||||||
private int m_FreeTop;
|
private int m_FreeTop;
|
||||||
|
|
||||||
private IntOpenHashMap m_TargetMap;
|
private ReferenceOpenHashMap m_TargetMap;
|
||||||
private StringOpenHashMap m_AllNameHeadMap;
|
private StringOpenHashMap m_AllNameHeadMap;
|
||||||
private StringOpenHashMap m_AvailableNameHeadMap;
|
private StringOpenHashMap m_AvailableNameHeadMap;
|
||||||
private StringOpenHashMap m_AvailableNameTailMap;
|
private StringOpenHashMap m_AvailableNameTailMap;
|
||||||
@ -50,7 +49,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
private int m_Capacity;
|
private int m_Capacity;
|
||||||
private float m_ExpireTime;
|
private float m_ExpireTime;
|
||||||
private int m_Priority;
|
private int m_Priority;
|
||||||
private ReleaseStrategy m_ReleaseStrategy;
|
|
||||||
private float m_AutoReleaseTime;
|
private float m_AutoReleaseTime;
|
||||||
|
|
||||||
private int m_PendingReleaseCount;
|
private int m_PendingReleaseCount;
|
||||||
@ -66,14 +64,13 @@ namespace AlicizaX.ObjectPool
|
|||||||
private const int InitSlotCapacity = 16;
|
private const int InitSlotCapacity = 16;
|
||||||
|
|
||||||
public ObjectPool(string name, bool allowMultiSpawn,
|
public ObjectPool(string name, bool allowMultiSpawn,
|
||||||
float autoReleaseInterval, int capacity, float expireTime, int priority, ReleaseStrategy releaseStrategy)
|
float autoReleaseInterval, int capacity, float expireTime, int priority)
|
||||||
: base(name)
|
: base(name)
|
||||||
{
|
{
|
||||||
int initCap = Math.Min(Math.Max(capacity, 1), InitSlotCapacity);
|
int initCap = Math.Min(Math.Max(capacity, 1), InitSlotCapacity);
|
||||||
m_SlotCapacity = initCap;
|
|
||||||
m_Slots = SlotArrayPool<ObjectSlot>.Rent(initCap);
|
m_Slots = SlotArrayPool<ObjectSlot>.Rent(initCap);
|
||||||
m_FreeStack = SlotArrayPool<int>.Rent(initCap);
|
m_FreeStack = SlotArrayPool<int>.Rent(initCap);
|
||||||
m_TargetMap = new IntOpenHashMap(initCap);
|
m_TargetMap = new ReferenceOpenHashMap(initCap);
|
||||||
m_AllNameHeadMap = new StringOpenHashMap(initCap);
|
m_AllNameHeadMap = new StringOpenHashMap(initCap);
|
||||||
m_AvailableNameHeadMap = new StringOpenHashMap(initCap);
|
m_AvailableNameHeadMap = new StringOpenHashMap(initCap);
|
||||||
m_AvailableNameTailMap = new StringOpenHashMap(initCap);
|
m_AvailableNameTailMap = new StringOpenHashMap(initCap);
|
||||||
@ -82,7 +79,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
m_Capacity = capacity;
|
m_Capacity = capacity;
|
||||||
m_ExpireTime = expireTime;
|
m_ExpireTime = expireTime;
|
||||||
m_Priority = priority;
|
m_Priority = priority;
|
||||||
m_ReleaseStrategy = releaseStrategy;
|
|
||||||
m_AutoReleaseTime = 0f;
|
m_AutoReleaseTime = 0f;
|
||||||
m_PendingReleaseCount = 0;
|
m_PendingReleaseCount = 0;
|
||||||
m_ReleasePerFrameBudget = DefaultReleasePerFrame;
|
m_ReleasePerFrameBudget = DefaultReleasePerFrame;
|
||||||
@ -163,8 +159,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
if (obj == null) return;
|
if (obj == null) return;
|
||||||
if (obj.Target == null) return;
|
if (obj.Target == null) return;
|
||||||
|
|
||||||
int targetHash = obj.Target.GetHashCode();
|
if (m_TargetMap.TryGetValue(obj.Target, out int existingIdx) && m_Slots[existingIdx].IsAlive())
|
||||||
if (m_TargetMap.TryGetValue(targetHash, out int existingIdx) && m_Slots[existingIdx].IsAlive())
|
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
UnityEngine.Debug.LogError($"Target '{obj.Target.GetType().FullName}' is already registered in pool '{FullName}'.");
|
UnityEngine.Debug.LogError($"Target '{obj.Target.GetType().FullName}' is already registered in pool '{FullName}'.");
|
||||||
@ -185,7 +180,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
slot.NextUnused = -1;
|
slot.NextUnused = -1;
|
||||||
slot.SetAlive(true);
|
slot.SetAlive(true);
|
||||||
|
|
||||||
m_TargetMap.AddOrUpdate(targetHash, idx);
|
m_TargetMap.AddOrUpdate(obj.Target, idx);
|
||||||
|
|
||||||
string objectName = obj.Name ?? string.Empty;
|
string objectName = obj.Name ?? string.Empty;
|
||||||
if (m_AllNameHeadMap.TryGetValue(objectName, out int existingHead))
|
if (m_AllNameHeadMap.TryGetValue(objectName, out int existingHead))
|
||||||
@ -251,8 +246,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
public void Unspawn(object target)
|
public void Unspawn(object target)
|
||||||
{
|
{
|
||||||
if (target == null) return;
|
if (target == null) return;
|
||||||
int targetHash = target.GetHashCode();
|
if (!m_TargetMap.TryGetValue(target, out int idx))
|
||||||
if (!m_TargetMap.TryGetValue(targetHash, out int idx))
|
|
||||||
{
|
{
|
||||||
if (m_IsShuttingDown) return;
|
if (m_IsShuttingDown) return;
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
@ -339,27 +333,34 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
m_ShrinkCounter = 0;
|
m_ShrinkCounter = 0;
|
||||||
|
|
||||||
if (m_TargetMap.Count == 0 || m_SlotCapacity <= InitSlotCapacity)
|
int slotArrayLen = m_Slots.Length;
|
||||||
|
if (m_TargetMap.Count == 0 || slotArrayLen <= InitSlotCapacity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float usageRatio = (float)m_TargetMap.Count / m_SlotCapacity;
|
float usageRatio = (float)m_TargetMap.Count / slotArrayLen;
|
||||||
if (usageRatio < 0.25f)
|
if (usageRatio < 0.25f)
|
||||||
{
|
{
|
||||||
int targetCapacity = Math.Max(m_SlotCapacity / 2, InitSlotCapacity);
|
int targetCapacity = Math.Max(slotArrayLen / 2, InitSlotCapacity);
|
||||||
if (targetCapacity < m_SlotCapacity)
|
if (targetCapacity < slotArrayLen && m_SlotCount <= targetCapacity)
|
||||||
{
|
{
|
||||||
var newSlots = SlotArrayPool<ObjectSlot>.Rent(targetCapacity);
|
var newSlots = SlotArrayPool<ObjectSlot>.Rent(targetCapacity);
|
||||||
var newFreeStack = SlotArrayPool<int>.Rent(targetCapacity);
|
var newFreeStack = SlotArrayPool<int>.Rent(targetCapacity);
|
||||||
|
|
||||||
Array.Copy(m_Slots, 0, newSlots, 0, Math.Min(m_SlotCount, targetCapacity));
|
Array.Copy(m_Slots, 0, newSlots, 0, m_SlotCount);
|
||||||
Array.Copy(m_FreeStack, 0, newFreeStack, 0, Math.Min(m_FreeTop, targetCapacity));
|
|
||||||
|
int newFreeTop = 0;
|
||||||
|
for (int i = 0; i < m_FreeTop; i++)
|
||||||
|
{
|
||||||
|
if (m_FreeStack[i] < targetCapacity)
|
||||||
|
newFreeStack[newFreeTop++] = m_FreeStack[i];
|
||||||
|
}
|
||||||
|
|
||||||
SlotArrayPool<ObjectSlot>.Return(m_Slots, true);
|
SlotArrayPool<ObjectSlot>.Return(m_Slots, true);
|
||||||
SlotArrayPool<int>.Return(m_FreeStack, true);
|
SlotArrayPool<int>.Return(m_FreeStack, true);
|
||||||
|
|
||||||
m_Slots = newSlots;
|
m_Slots = newSlots;
|
||||||
m_FreeStack = newFreeStack;
|
m_FreeStack = newFreeStack;
|
||||||
m_SlotCapacity = targetCapacity;
|
m_FreeTop = newFreeTop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,7 +389,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
m_FreeStack = null;
|
m_FreeStack = null;
|
||||||
|
|
||||||
m_SlotCount = 0;
|
m_SlotCount = 0;
|
||||||
m_SlotCapacity = 0;
|
|
||||||
m_FreeTop = 0;
|
m_FreeTop = 0;
|
||||||
m_PendingReleaseCount = 0;
|
m_PendingReleaseCount = 0;
|
||||||
m_UnusedHead = -1;
|
m_UnusedHead = -1;
|
||||||
@ -489,7 +489,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
private void GrowSlots()
|
private void GrowSlots()
|
||||||
{
|
{
|
||||||
int newCap = Math.Max(m_SlotCapacity * 2, InitSlotCapacity);
|
int newCap = Math.Max(m_Slots.Length * 2, InitSlotCapacity);
|
||||||
var newSlots = SlotArrayPool<ObjectSlot>.Rent(newCap);
|
var newSlots = SlotArrayPool<ObjectSlot>.Rent(newCap);
|
||||||
var newFreeStack = SlotArrayPool<int>.Rent(newCap);
|
var newFreeStack = SlotArrayPool<int>.Rent(newCap);
|
||||||
|
|
||||||
@ -501,7 +501,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
m_Slots = newSlots;
|
m_Slots = newSlots;
|
||||||
m_FreeStack = newFreeStack;
|
m_FreeStack = newFreeStack;
|
||||||
m_SlotCapacity = newCap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReleaseSlot(int idx)
|
private void ReleaseSlot(int idx)
|
||||||
@ -514,8 +513,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
MarkSlotUnavailable(idx);
|
MarkSlotUnavailable(idx);
|
||||||
|
|
||||||
RemoveFromAllNameChain(idx);
|
RemoveFromAllNameChain(idx);
|
||||||
int targetHash = obj.Target.GetHashCode();
|
m_TargetMap.Remove(obj.Target);
|
||||||
m_TargetMap.Remove(targetHash);
|
|
||||||
|
|
||||||
obj.Release(false);
|
obj.Release(false);
|
||||||
MemoryPool.Release(obj);
|
MemoryPool.Release(obj);
|
||||||
@ -530,14 +528,13 @@ namespace AlicizaX.ObjectPool
|
|||||||
slot.PrevUnused = -1;
|
slot.PrevUnused = -1;
|
||||||
slot.NextUnused = -1;
|
slot.NextUnused = -1;
|
||||||
|
|
||||||
if (m_FreeTop >= m_SlotCapacity)
|
if (m_FreeTop >= m_FreeStack.Length)
|
||||||
{
|
{
|
||||||
int newCap = m_SlotCapacity * 2;
|
int newCap = m_FreeStack.Length * 2;
|
||||||
var newFreeStack = SlotArrayPool<int>.Rent(newCap);
|
var newFreeStack = SlotArrayPool<int>.Rent(newCap);
|
||||||
Array.Copy(m_FreeStack, 0, newFreeStack, 0, m_FreeTop);
|
Array.Copy(m_FreeStack, 0, newFreeStack, 0, m_FreeTop);
|
||||||
SlotArrayPool<int>.Return(m_FreeStack, true);
|
SlotArrayPool<int>.Return(m_FreeStack, true);
|
||||||
m_FreeStack = newFreeStack;
|
m_FreeStack = newFreeStack;
|
||||||
m_SlotCapacity = newCap;
|
|
||||||
}
|
}
|
||||||
m_FreeStack[m_FreeTop++] = idx;
|
m_FreeStack[m_FreeTop++] = idx;
|
||||||
|
|
||||||
@ -643,13 +640,12 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
private void ShrinkStorageIfEmpty()
|
private void ShrinkStorageIfEmpty()
|
||||||
{
|
{
|
||||||
if (m_TargetMap.Count > 0 || m_SlotCapacity <= InitSlotCapacity)
|
if (m_TargetMap.Count > 0 || m_Slots.Length <= InitSlotCapacity)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SlotArrayPool<ObjectSlot>.Return(m_Slots, true);
|
SlotArrayPool<ObjectSlot>.Return(m_Slots, true);
|
||||||
SlotArrayPool<int>.Return(m_FreeStack, true);
|
SlotArrayPool<int>.Return(m_FreeStack, true);
|
||||||
|
|
||||||
m_SlotCapacity = InitSlotCapacity;
|
|
||||||
m_Slots = SlotArrayPool<ObjectSlot>.Rent(InitSlotCapacity);
|
m_Slots = SlotArrayPool<ObjectSlot>.Rent(InitSlotCapacity);
|
||||||
m_FreeStack = SlotArrayPool<int>.Rent(InitSlotCapacity);
|
m_FreeStack = SlotArrayPool<int>.Rent(InitSlotCapacity);
|
||||||
m_AllNameHeadMap.Clear();
|
m_AllNameHeadMap.Clear();
|
||||||
@ -679,8 +675,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
aliveCount++;
|
aliveCount++;
|
||||||
|
|
||||||
object target = slot.Obj.Target;
|
object target = slot.Obj.Target;
|
||||||
int targetHash = target.GetHashCode();
|
if (!m_TargetMap.TryGetValue(target, out int mappedIdx) || mappedIdx != i)
|
||||||
if (!m_TargetMap.TryGetValue(targetHash, out int mappedIdx) || mappedIdx != i)
|
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.LogError($"Object pool '{FullName}' target index map is inconsistent.");
|
UnityEngine.Debug.LogError($"Object pool '{FullName}' target index map is inconsistent.");
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -56,18 +56,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
public bool HasObjectPool<T>(string name) where T : ObjectBase
|
public bool HasObjectPool<T>(string name) where T : ObjectBase
|
||||||
=> m_ObjectPools.ContainsKey(new TypeNamePair(typeof(T), name));
|
=> m_ObjectPools.ContainsKey(new TypeNamePair(typeof(T), name));
|
||||||
|
|
||||||
public bool HasObjectPool(Type objectType)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
return m_ObjectPools.ContainsKey(new TypeNamePair(objectType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasObjectPool(Type objectType, string name)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
return m_ObjectPools.ContainsKey(new TypeNamePair(objectType, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== Get ==========
|
// ========== Get ==========
|
||||||
|
|
||||||
public IObjectPool<T> GetObjectPool<T>() where T : ObjectBase
|
public IObjectPool<T> GetObjectPool<T>() where T : ObjectBase
|
||||||
@ -76,18 +64,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
public IObjectPool<T> GetObjectPool<T>(string name) where T : ObjectBase
|
public IObjectPool<T> GetObjectPool<T>(string name) where T : ObjectBase
|
||||||
=> (IObjectPool<T>)InternalGet(new TypeNamePair(typeof(T), name));
|
=> (IObjectPool<T>)InternalGet(new TypeNamePair(typeof(T), name));
|
||||||
|
|
||||||
public ObjectPoolBase GetObjectPool(Type objectType)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
return InternalGet(new TypeNamePair(objectType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectPoolBase GetObjectPool(Type objectType, string name)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
return InternalGet(new TypeNamePair(objectType, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== GetAll ==========
|
// ========== GetAll ==========
|
||||||
|
|
||||||
int IObjectPoolServiceDebugView.GetAllObjectPools(bool sort, ObjectPoolBase[] results)
|
int IObjectPoolServiceDebugView.GetAllObjectPools(bool sort, ObjectPoolBase[] results)
|
||||||
@ -134,36 +110,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
options.AutoReleaseInterval ?? DefaultAutoReleaseInterval,
|
options.AutoReleaseInterval ?? DefaultAutoReleaseInterval,
|
||||||
options.Capacity ?? DefaultCapacity,
|
options.Capacity ?? DefaultCapacity,
|
||||||
options.ExpireTime ?? DefaultExpireTime,
|
options.ExpireTime ?? DefaultExpireTime,
|
||||||
options.Priority,
|
options.Priority);
|
||||||
options.ReleaseStrategy);
|
|
||||||
|
|
||||||
m_ObjectPools.Add(key, pool);
|
|
||||||
m_ObjectPoolIndexMap.Add(pool, m_ObjectPoolList.Count);
|
|
||||||
m_ObjectPoolList.Add(pool);
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectPoolBase CreatePool(Type objectType, ObjectPoolCreateOptions options = default)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
var key = new TypeNamePair(objectType, options.Name);
|
|
||||||
if (m_ObjectPools.ContainsKey(key))
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
UnityEngine.Debug.LogError($"Already exist object pool '{key}'.");
|
|
||||||
#endif
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var poolType = typeof(ObjectPool<>).MakeGenericType(objectType);
|
|
||||||
var pool = (ObjectPoolBase)Activator.CreateInstance(poolType,
|
|
||||||
options.Name ?? string.Empty,
|
|
||||||
options.AllowMultiSpawn,
|
|
||||||
options.AutoReleaseInterval ?? DefaultAutoReleaseInterval,
|
|
||||||
options.Capacity ?? DefaultCapacity,
|
|
||||||
options.ExpireTime ?? DefaultExpireTime,
|
|
||||||
options.Priority,
|
|
||||||
options.ReleaseStrategy);
|
|
||||||
|
|
||||||
m_ObjectPools.Add(key, pool);
|
m_ObjectPools.Add(key, pool);
|
||||||
m_ObjectPoolIndexMap.Add(pool, m_ObjectPoolList.Count);
|
m_ObjectPoolIndexMap.Add(pool, m_ObjectPoolList.Count);
|
||||||
@ -179,18 +126,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
public bool DestroyObjectPool<T>(string name) where T : ObjectBase
|
public bool DestroyObjectPool<T>(string name) where T : ObjectBase
|
||||||
=> InternalDestroy(new TypeNamePair(typeof(T), name));
|
=> InternalDestroy(new TypeNamePair(typeof(T), name));
|
||||||
|
|
||||||
public bool DestroyObjectPool(Type objectType)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
return InternalDestroy(new TypeNamePair(objectType));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DestroyObjectPool(Type objectType, string name)
|
|
||||||
{
|
|
||||||
ValidateObjectType(objectType);
|
|
||||||
return InternalDestroy(new TypeNamePair(objectType, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DestroyObjectPool<T>(IObjectPool<T> objectPool) where T : ObjectBase
|
public bool DestroyObjectPool<T>(IObjectPool<T> objectPool) where T : ObjectBase
|
||||||
{
|
{
|
||||||
if (objectPool == null)
|
if (objectPool == null)
|
||||||
@ -203,18 +138,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
return InternalDestroy(new TypeNamePair(typeof(T), objectPool.Name));
|
return InternalDestroy(new TypeNamePair(typeof(T), objectPool.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DestroyObjectPool(ObjectPoolBase objectPool)
|
|
||||||
{
|
|
||||||
if (objectPool == null)
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
UnityEngine.Debug.LogError("Object pool is invalid.");
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return InternalDestroy(new TypeNamePair(objectPool.ObjectType, objectPool.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== Release ==========
|
// ========== Release ==========
|
||||||
|
|
||||||
public void Release()
|
public void Release()
|
||||||
@ -280,23 +203,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
m_ObjectPoolIndexMap[lastPool] = index;
|
m_ObjectPoolIndexMap[lastPool] = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateObjectType(Type objectType)
|
|
||||||
{
|
|
||||||
if (objectType == null)
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
UnityEngine.Debug.LogError("Object type is invalid.");
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!typeof(ObjectBase).IsAssignableFrom(objectType))
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
UnityEngine.Debug.LogError($"Object type '{objectType.FullName}' is invalid.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ObjectPoolComparer(ObjectPoolBase a, ObjectPoolBase b)
|
private static int ObjectPoolComparer(ObjectPoolBase a, ObjectPoolBase b)
|
||||||
=> a.Priority.CompareTo(b.Priority);
|
=> a.Priority.CompareTo(b.Priority);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,13 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace AlicizaX.ObjectPool
|
namespace AlicizaX.ObjectPool
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
internal struct IntOpenHashMap
|
/// 引用类型键的开放寻址哈希表,使用身份相等性(ReferenceEquals)
|
||||||
|
/// </summary>
|
||||||
|
internal struct ReferenceOpenHashMap
|
||||||
{
|
{
|
||||||
private int[] m_Buckets;
|
private int[] m_Buckets;
|
||||||
private int[] m_Keys;
|
private object[] m_Keys;
|
||||||
private int[] m_Values;
|
private int[] m_Values;
|
||||||
private int[] m_Next;
|
private int[] m_Next;
|
||||||
private int m_Count;
|
private int m_Count;
|
||||||
@ -19,46 +21,52 @@ namespace AlicizaX.ObjectPool
|
|||||||
|
|
||||||
public int Count => m_Count;
|
public int Count => m_Count;
|
||||||
|
|
||||||
public IntOpenHashMap(int capacity)
|
public ReferenceOpenHashMap(int capacity)
|
||||||
{
|
{
|
||||||
int cap = NextPowerOf2(Math.Max(capacity, MinCapacity));
|
int cap = NextPowerOf2(Math.Max(capacity, MinCapacity));
|
||||||
m_Mask = cap - 1;
|
m_Mask = cap - 1;
|
||||||
m_Buckets = new int[cap];
|
m_Buckets = SlotArrayPool<int>.Rent(cap);
|
||||||
m_Keys = new int[cap];
|
m_Keys = SlotArrayPool<object>.Rent(cap);
|
||||||
m_Values = new int[cap];
|
m_Values = SlotArrayPool<int>.Rent(cap);
|
||||||
m_Next = new int[cap];
|
m_Next = SlotArrayPool<int>.Rent(cap);
|
||||||
|
Array.Clear(m_Buckets, 0, m_Buckets.Length);
|
||||||
|
Array.Clear(m_Keys, 0, m_Keys.Length);
|
||||||
|
Array.Clear(m_Values, 0, m_Values.Length);
|
||||||
|
Array.Clear(m_Next, 0, m_Next.Length);
|
||||||
m_Count = 0;
|
m_Count = 0;
|
||||||
m_FreeList = 0;
|
m_FreeList = 0;
|
||||||
m_AllocCount = 0;
|
m_AllocCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool TryGetValue(int key, out int value)
|
public bool TryGetValue(object key, out int value)
|
||||||
{
|
{
|
||||||
if (m_Buckets == null) { value = -1; return false; }
|
if (m_Buckets == null || key == null) { value = -1; return false; }
|
||||||
int i = m_Buckets[(key & 0x7FFFFFFF) & m_Mask];
|
int hash = RuntimeHelpers.GetHashCode(key) & 0x7FFFFFFF;
|
||||||
|
int i = m_Buckets[hash & m_Mask];
|
||||||
while (i > 0)
|
while (i > 0)
|
||||||
{
|
{
|
||||||
int idx = i - 1;
|
int idx = i - 1;
|
||||||
if (m_Keys[idx] == key) { value = m_Values[idx]; return true; }
|
if (ReferenceEquals(m_Keys[idx], key)) { value = m_Values[idx]; return true; }
|
||||||
i = m_Next[idx];
|
i = m_Next[idx];
|
||||||
}
|
}
|
||||||
value = -1;
|
value = -1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AddOrUpdate(int key, int value)
|
public void AddOrUpdate(object key, int value)
|
||||||
{
|
{
|
||||||
|
if (key == null) return;
|
||||||
if (m_Count >= ((m_Mask + 1) * 3 >> 2))
|
if (m_Count >= ((m_Mask + 1) * 3 >> 2))
|
||||||
Grow();
|
Grow();
|
||||||
|
|
||||||
int bucket = (key & 0x7FFFFFFF) & m_Mask;
|
int hash = RuntimeHelpers.GetHashCode(key) & 0x7FFFFFFF;
|
||||||
|
int bucket = hash & m_Mask;
|
||||||
int i = m_Buckets[bucket];
|
int i = m_Buckets[bucket];
|
||||||
while (i > 0)
|
while (i > 0)
|
||||||
{
|
{
|
||||||
int ei = i - 1;
|
int ei = i - 1;
|
||||||
if (m_Keys[ei] == key) { m_Values[ei] = value; return; }
|
if (ReferenceEquals(m_Keys[ei], key)) { m_Values[ei] = value; return; }
|
||||||
i = m_Next[ei];
|
i = m_Next[ei];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +78,7 @@ namespace AlicizaX.ObjectPool
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_AllocCount > m_Mask) { Grow(); bucket = (key & 0x7FFFFFFF) & m_Mask; }
|
if (m_AllocCount > m_Mask) { Grow(); bucket = hash & m_Mask; }
|
||||||
idx = m_AllocCount++;
|
idx = m_AllocCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,20 +90,21 @@ namespace AlicizaX.ObjectPool
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public bool Remove(int key)
|
public bool Remove(object key)
|
||||||
{
|
{
|
||||||
if (m_Buckets == null) return false;
|
if (m_Buckets == null || key == null) return false;
|
||||||
int bucket = (key & 0x7FFFFFFF) & m_Mask;
|
int hash = RuntimeHelpers.GetHashCode(key) & 0x7FFFFFFF;
|
||||||
|
int bucket = hash & m_Mask;
|
||||||
int prev = 0;
|
int prev = 0;
|
||||||
int i = m_Buckets[bucket];
|
int i = m_Buckets[bucket];
|
||||||
while (i > 0)
|
while (i > 0)
|
||||||
{
|
{
|
||||||
int idx = i - 1;
|
int idx = i - 1;
|
||||||
if (m_Keys[idx] == key)
|
if (ReferenceEquals(m_Keys[idx], key))
|
||||||
{
|
{
|
||||||
if (prev == 0) m_Buckets[bucket] = m_Next[idx];
|
if (prev == 0) m_Buckets[bucket] = m_Next[idx];
|
||||||
else m_Next[prev - 1] = m_Next[idx];
|
else m_Next[prev - 1] = m_Next[idx];
|
||||||
m_Keys[idx] = 0;
|
m_Keys[idx] = null;
|
||||||
m_Values[idx] = -1;
|
m_Values[idx] = -1;
|
||||||
m_Next[idx] = m_FreeList;
|
m_Next[idx] = m_FreeList;
|
||||||
m_FreeList = idx + 1;
|
m_FreeList = idx + 1;
|
||||||
@ -107,7 +116,6 @@ namespace AlicizaX.ObjectPool
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
if (m_Buckets == null) return;
|
if (m_Buckets == null) return;
|
||||||
@ -126,10 +134,12 @@ namespace AlicizaX.ObjectPool
|
|||||||
int newCap = (m_Mask + 1) << 1;
|
int newCap = (m_Mask + 1) << 1;
|
||||||
if (newCap < MinCapacity) newCap = MinCapacity;
|
if (newCap < MinCapacity) newCap = MinCapacity;
|
||||||
int newMask = newCap - 1;
|
int newMask = newCap - 1;
|
||||||
var newBuckets = new int[newCap];
|
var newBuckets = SlotArrayPool<int>.Rent(newCap);
|
||||||
var newKeys = new int[newCap];
|
var newKeys = SlotArrayPool<object>.Rent(newCap);
|
||||||
var newValues = new int[newCap];
|
var newValues = SlotArrayPool<int>.Rent(newCap);
|
||||||
var newNext = new int[newCap];
|
var newNext = SlotArrayPool<int>.Rent(newCap);
|
||||||
|
Array.Clear(newBuckets, 0, newBuckets.Length);
|
||||||
|
Array.Clear(newNext, 0, newNext.Length);
|
||||||
|
|
||||||
int newAlloc = 0;
|
int newAlloc = 0;
|
||||||
int oldCap = m_Mask + 1;
|
int oldCap = m_Mask + 1;
|
||||||
@ -142,13 +152,19 @@ namespace AlicizaX.ObjectPool
|
|||||||
int ni = newAlloc++;
|
int ni = newAlloc++;
|
||||||
newKeys[ni] = m_Keys[old];
|
newKeys[ni] = m_Keys[old];
|
||||||
newValues[ni] = m_Values[old];
|
newValues[ni] = m_Values[old];
|
||||||
int nb = (newKeys[ni] & 0x7FFFFFFF) & newMask;
|
int hash = RuntimeHelpers.GetHashCode(newKeys[ni]) & 0x7FFFFFFF;
|
||||||
|
int nb = hash & newMask;
|
||||||
newNext[ni] = newBuckets[nb];
|
newNext[ni] = newBuckets[nb];
|
||||||
newBuckets[nb] = ni + 1;
|
newBuckets[nb] = ni + 1;
|
||||||
i = m_Next[old];
|
i = m_Next[old];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SlotArrayPool<int>.Return(m_Buckets, true);
|
||||||
|
SlotArrayPool<object>.Return(m_Keys, true);
|
||||||
|
SlotArrayPool<int>.Return(m_Values, true);
|
||||||
|
SlotArrayPool<int>.Return(m_Next, true);
|
||||||
|
|
||||||
m_Buckets = newBuckets;
|
m_Buckets = newBuckets;
|
||||||
m_Keys = newKeys;
|
m_Keys = newKeys;
|
||||||
m_Values = newValues;
|
m_Values = newValues;
|
||||||
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 311d5e5b578ed15428d9565ab237becc
|
guid: 0c2c880135959a54f95efaa6ef962867
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@ -1,28 +0,0 @@
|
|||||||
namespace AlicizaX.ObjectPool
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 对象池释放策略
|
|
||||||
/// </summary>
|
|
||||||
public enum ReleaseStrategy
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// LRU (Least Recently Used) - 最近最少使用
|
|
||||||
/// </summary>
|
|
||||||
LRU = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// LFU (Least Frequently Used) - 最不经常使用
|
|
||||||
/// </summary>
|
|
||||||
LFU = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Priority - 基于优先级
|
|
||||||
/// </summary>
|
|
||||||
Priority = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Hybrid - 混合策略 (LRU + Priority)
|
|
||||||
/// </summary>
|
|
||||||
Hybrid = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -25,10 +25,14 @@ namespace AlicizaX.ObjectPool
|
|||||||
{
|
{
|
||||||
int cap = NextPowerOf2(Math.Max(capacity, MinCapacity));
|
int cap = NextPowerOf2(Math.Max(capacity, MinCapacity));
|
||||||
m_Mask = cap - 1;
|
m_Mask = cap - 1;
|
||||||
m_Buckets = new int[cap];
|
m_Buckets = SlotArrayPool<int>.Rent(cap);
|
||||||
m_Keys = new string[cap];
|
m_Keys = SlotArrayPool<string>.Rent(cap);
|
||||||
m_Values = new int[cap];
|
m_Values = SlotArrayPool<int>.Rent(cap);
|
||||||
m_Next = new int[cap];
|
m_Next = SlotArrayPool<int>.Rent(cap);
|
||||||
|
Array.Clear(m_Buckets, 0, m_Buckets.Length);
|
||||||
|
Array.Clear(m_Keys, 0, m_Keys.Length);
|
||||||
|
Array.Clear(m_Values, 0, m_Values.Length);
|
||||||
|
Array.Clear(m_Next, 0, m_Next.Length);
|
||||||
m_Count = 0;
|
m_Count = 0;
|
||||||
m_FreeList = 0;
|
m_FreeList = 0;
|
||||||
m_AllocCount = 0;
|
m_AllocCount = 0;
|
||||||
@ -138,10 +142,12 @@ namespace AlicizaX.ObjectPool
|
|||||||
int newCap = (m_Mask + 1) << 1;
|
int newCap = (m_Mask + 1) << 1;
|
||||||
if (newCap < MinCapacity) newCap = MinCapacity;
|
if (newCap < MinCapacity) newCap = MinCapacity;
|
||||||
int newMask = newCap - 1;
|
int newMask = newCap - 1;
|
||||||
var newBuckets = new int[newCap];
|
var newBuckets = SlotArrayPool<int>.Rent(newCap);
|
||||||
var newKeys = new string[newCap];
|
var newKeys = SlotArrayPool<string>.Rent(newCap);
|
||||||
var newValues = new int[newCap];
|
var newValues = SlotArrayPool<int>.Rent(newCap);
|
||||||
var newNext = new int[newCap];
|
var newNext = SlotArrayPool<int>.Rent(newCap);
|
||||||
|
Array.Clear(newBuckets, 0, newBuckets.Length);
|
||||||
|
Array.Clear(newNext, 0, newNext.Length);
|
||||||
|
|
||||||
int newAlloc = 0;
|
int newAlloc = 0;
|
||||||
int oldCap = m_Mask + 1;
|
int oldCap = m_Mask + 1;
|
||||||
@ -162,6 +168,11 @@ namespace AlicizaX.ObjectPool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SlotArrayPool<int>.Return(m_Buckets, true);
|
||||||
|
SlotArrayPool<string>.Return(m_Keys, true);
|
||||||
|
SlotArrayPool<int>.Return(m_Values, true);
|
||||||
|
SlotArrayPool<int>.Return(m_Next, true);
|
||||||
|
|
||||||
m_Buckets = newBuckets;
|
m_Buckets = newBuckets;
|
||||||
m_Keys = newKeys;
|
m_Keys = newKeys;
|
||||||
m_Values = newValues;
|
m_Values = newValues;
|
||||||
|
|||||||
@ -5,13 +5,13 @@ namespace AlicizaX.Timer.Runtime
|
|||||||
[UnityEngine.Scripting.Preserve]
|
[UnityEngine.Scripting.Preserve]
|
||||||
public interface ITimerService : IService
|
public interface ITimerService : IService
|
||||||
{
|
{
|
||||||
int AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false);
|
ulong AddTimer(TimerHandlerNoArgs callback, float time, bool isLoop = false, bool isUnscaled = false);
|
||||||
int AddTimer<T>(Action<T> callback, T arg, float time, bool isLoop = false, bool isUnscaled = false) where T : class;
|
ulong AddTimer<T>(Action<T> callback, T arg, float time, bool isLoop = false, bool isUnscaled = false) where T : class;
|
||||||
void Stop(int timerId);
|
void Stop(ulong timerHandle);
|
||||||
void Resume(int timerId);
|
void Resume(ulong timerHandle);
|
||||||
bool IsRunning(int timerId);
|
bool IsRunning(ulong timerHandle);
|
||||||
float GetLeftTime(int timerId);
|
float GetLeftTime(ulong timerHandle);
|
||||||
void Restart(int timerId);
|
void Restart(ulong timerHandle);
|
||||||
void RemoveTimer(int timerId);
|
void RemoveTimer(ulong timerHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,19 @@
|
|||||||
namespace AlicizaX.Timer.Runtime
|
namespace AlicizaX.Timer.Runtime
|
||||||
{
|
{
|
||||||
|
internal static class TimerDebugFlags
|
||||||
|
{
|
||||||
|
public const byte Running = 1 << 0;
|
||||||
|
public const byte Loop = 1 << 1;
|
||||||
|
public const byte Unscaled = 1 << 2;
|
||||||
|
}
|
||||||
|
|
||||||
internal struct TimerDebugInfo
|
internal struct TimerDebugInfo
|
||||||
{
|
{
|
||||||
public int TimerId;
|
public ulong TimerHandle;
|
||||||
public float LeftTime;
|
public float LeftTime;
|
||||||
public float Duration;
|
public float Duration;
|
||||||
public bool IsLoop;
|
public float Age;
|
||||||
public bool IsRunning;
|
public byte Flags;
|
||||||
public bool IsUnscaled;
|
|
||||||
public float CreationTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface ITimerServiceDebug
|
internal interface ITimerServiceDebug
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ namespace AlicizaX.UI.Runtime
|
|||||||
internal sealed partial class UIService
|
internal sealed partial class UIService
|
||||||
{
|
{
|
||||||
private GameObject m_LayerBlock;
|
private GameObject m_LayerBlock;
|
||||||
private int m_LastCountDownGuid;
|
private ulong m_LastCountDownHandle;
|
||||||
|
|
||||||
private void InitUIBlock()
|
private void InitUIBlock()
|
||||||
{
|
{
|
||||||
@ -23,21 +23,21 @@ namespace AlicizaX.UI.Runtime
|
|||||||
public void SetUIBlock(float timeDuration)
|
public void SetUIBlock(float timeDuration)
|
||||||
{
|
{
|
||||||
ITimerService timerService = GetTimerService();
|
ITimerService timerService = GetTimerService();
|
||||||
if (m_LastCountDownGuid != 0)
|
if (m_LastCountDownHandle != 0UL)
|
||||||
{
|
{
|
||||||
timerService.RemoveTimer(m_LastCountDownGuid);
|
timerService.RemoveTimer(m_LastCountDownHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetLayerBlockOption(true);
|
SetLayerBlockOption(true);
|
||||||
m_LastCountDownGuid = timerService.AddTimer(OnBlockCountDown, timeDuration);
|
m_LastCountDownHandle = timerService.AddTimer(OnBlockCountDown, timeDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceExitBlock()
|
public void ForceExitBlock()
|
||||||
{
|
{
|
||||||
ITimerService timerService = GetTimerService();
|
ITimerService timerService = GetTimerService();
|
||||||
if (m_LastCountDownGuid != 0)
|
if (m_LastCountDownHandle != 0UL)
|
||||||
{
|
{
|
||||||
timerService.RemoveTimer(m_LastCountDownGuid);
|
timerService.RemoveTimer(m_LastCountDownHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
RecoverLayerOptionAll();
|
RecoverLayerOptionAll();
|
||||||
@ -56,7 +56,7 @@ namespace AlicizaX.UI.Runtime
|
|||||||
public void RecoverLayerOptionAll()
|
public void RecoverLayerOptionAll()
|
||||||
{
|
{
|
||||||
SetLayerBlockOption(false);
|
SetLayerBlockOption(false);
|
||||||
m_LastCountDownGuid = 0;
|
m_LastCountDownHandle = 0UL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,12 +10,12 @@ namespace AlicizaX.UI.Runtime
|
|||||||
private readonly struct CacheEntry
|
private readonly struct CacheEntry
|
||||||
{
|
{
|
||||||
public readonly UIMetadata Metadata;
|
public readonly UIMetadata Metadata;
|
||||||
public readonly int TimerId;
|
public readonly ulong TimerHandle;
|
||||||
|
|
||||||
public CacheEntry(UIMetadata metadata, int timerId)
|
public CacheEntry(UIMetadata metadata, ulong timerHandle)
|
||||||
{
|
{
|
||||||
Metadata = metadata;
|
Metadata = metadata;
|
||||||
TimerId = timerId;
|
TimerHandle = timerHandle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,27 +36,27 @@ namespace AlicizaX.UI.Runtime
|
|||||||
}
|
}
|
||||||
|
|
||||||
RemoveFromCache(uiMetadata.MetaInfo.RuntimeTypeHandle);
|
RemoveFromCache(uiMetadata.MetaInfo.RuntimeTypeHandle);
|
||||||
int timerId = -1;
|
ulong timerHandle = 0UL;
|
||||||
|
|
||||||
uiMetadata.View.Holder.transform.SetParent(UICacheLayer);
|
uiMetadata.View.Holder.transform.SetParent(UICacheLayer);
|
||||||
if (uiMetadata.MetaInfo.CacheTime > 0)
|
if (uiMetadata.MetaInfo.CacheTime > 0)
|
||||||
{
|
{
|
||||||
ITimerService timerService = GetTimerService();
|
ITimerService timerService = GetTimerService();
|
||||||
timerId = timerService.AddTimer(
|
timerHandle = timerService.AddTimer(
|
||||||
OnTimerDisposeWindow,
|
OnTimerDisposeWindow,
|
||||||
uiMetadata,
|
uiMetadata,
|
||||||
uiMetadata.MetaInfo.CacheTime,
|
uiMetadata.MetaInfo.CacheTime,
|
||||||
isLoop: false,
|
isLoop: false,
|
||||||
isUnscaled: true);
|
isUnscaled: true);
|
||||||
|
|
||||||
if (timerId <= 0)
|
if (timerHandle == 0UL)
|
||||||
{
|
{
|
||||||
Log.Warning($"Failed to create cache timer for {uiMetadata.UILogicType.Name}");
|
Log.Warning($"Failed to create cache timer for {uiMetadata.UILogicType.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uiMetadata.InCache = true;
|
uiMetadata.InCache = true;
|
||||||
m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, new CacheEntry(uiMetadata, timerId));
|
m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, new CacheEntry(uiMetadata, timerHandle));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTimerDisposeWindow(UIMetadata meta)
|
private void OnTimerDisposeWindow(UIMetadata meta)
|
||||||
@ -74,9 +74,9 @@ namespace AlicizaX.UI.Runtime
|
|||||||
{
|
{
|
||||||
m_CacheWindow.Remove(typeHandle);
|
m_CacheWindow.Remove(typeHandle);
|
||||||
entry.Metadata.InCache = false;
|
entry.Metadata.InCache = false;
|
||||||
if (entry.TimerId > 0 && _timerService != null)
|
if (entry.TimerHandle != 0UL && _timerService != null)
|
||||||
{
|
{
|
||||||
_timerService.RemoveTimer(entry.TimerId);
|
_timerService.RemoveTimer(entry.TimerHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -144,9 +144,9 @@ namespace AlicizaX.UI.Runtime
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry.TimerId > 0 && _timerService != null)
|
if (entry.TimerHandle != 0UL && _timerService != null)
|
||||||
{
|
{
|
||||||
_timerService.RemoveTimer(entry.TimerId);
|
_timerService.RemoveTimer(entry.TimerHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Metadata.InCache = false;
|
entry.Metadata.InCache = false;
|
||||||
@ -157,10 +157,10 @@ namespace AlicizaX.UI.Runtime
|
|||||||
m_CacheWindow.Clear();
|
m_CacheWindow.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LastCountDownGuid != 0 && _timerService != null)
|
if (m_LastCountDownHandle != 0UL && _timerService != null)
|
||||||
{
|
{
|
||||||
_timerService.RemoveTimer(m_LastCountDownGuid);
|
_timerService.RemoveTimer(m_LastCountDownHandle);
|
||||||
m_LastCountDownGuid = 0;
|
m_LastCountDownHandle = 0UL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_LayerBlock != null)
|
if (m_LayerBlock != null)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user