Memory changes 3

General memory improvements to decrease GC pressure and frequency.

Pool big arrays and objects that are created and deleted often.

Skip data copies when they aren't needed.

Inline flag checks to skip unneeded allocations.

From my testing the performance is about the same, but the GC frequency is much lower and collection is faster causing less and smaller spikes.
This commit is contained in:
LotP
2025-10-30 23:25:51 -05:00
committed by KeatonTheBot
parent 2c50fbcc1f
commit e3ee28605d
42 changed files with 703 additions and 331 deletions
+86 -48
View File
@@ -45,7 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
private const int GranularLayerThreshold = 8;
private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
private delegate bool HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false, bool specialData = false);
private readonly HandlesCallbackDelegate _signalModifyingCallback;
private readonly HandlesCallbackDelegate _discardDataCallback;
private readonly HandlesCallbackDelegate _checkDirtyCallback;
/// <summary>
/// The storage texture associated with this group.
@@ -126,6 +130,10 @@ namespace Ryujinx.Graphics.Gpu.Image
_incompatibleOverlaps = incompatibleOverlaps;
_flushIncompatibleOverlaps = TextureCompatibility.IsFormatHostIncompatible(storage.Info, context.Capabilities);
_signalModifyingCallback = SignalModifyingCallback;
_discardDataCallback = DiscardDataCallback;
_checkDirtyCallback = CheckDirtyCallback;
}
/// <summary>
@@ -254,29 +262,33 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="consume">True to consume the dirty flags and reprotect, false to leave them as is</param>
/// <returns>True if a flag was dirty, false otherwise</returns>
public bool CheckDirty(Texture texture, bool consume)
{
EvaluateRelevantHandles(texture, _checkDirtyCallback, out bool dirty, consume);
return dirty;
}
bool CheckDirtyCallback(int baseHandle, int regionCount, bool split, bool consume)
{
bool dirty = false;
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
for (int i = 0; i < regionCount; i++)
{
for (int i = 0; i < regionCount; i++)
TextureGroupHandle group = _handles[baseHandle + i];
foreach (RegionHandle handle in group.Handles)
{
TextureGroupHandle group = _handles[baseHandle + i];
foreach (RegionHandle handle in group.Handles)
if (handle.Dirty)
{
if (handle.Dirty)
if (consume)
{
if (consume)
{
handle.Reprotect();
}
dirty = true;
handle.Reprotect();
}
dirty = true;
}
}
});
}
return dirty;
}
@@ -288,15 +300,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture being discarded</param>
public void DiscardData(Texture texture)
{
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
EvaluateRelevantHandles(texture, _discardDataCallback, out _);
}
bool DiscardDataCallback(int baseHandle, int regionCount, bool split, bool bound)
{
for (int i = 0; i < regionCount; i++)
{
for (int i = 0; i < regionCount; i++)
{
TextureGroupHandle group = _handles[baseHandle + i];
TextureGroupHandle group = _handles[baseHandle + i];
group.DiscardData();
}
});
group.DiscardData();
}
return true;
}
/// <summary>
@@ -308,7 +324,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
FlushIncompatibleOverlapsIfNeeded();
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, _) =>
{
bool dirty = false;
bool anyModified = false;
@@ -384,7 +400,9 @@ namespace Ryujinx.Graphics.Gpu.Image
texture.SynchronizeFull();
}
}
});
return true;
}, out _);
}
/// <summary>
@@ -461,7 +479,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="texture">The texture to synchronize dependents of</param>
public void SynchronizeDependents(Texture texture)
{
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _, _) =>
{
for (int i = 0; i < regionCount; i++)
{
@@ -469,7 +487,9 @@ namespace Ryujinx.Graphics.Gpu.Image
group.SynchronizeDependents();
}
});
return true;
}, out _);
}
/// <summary>
@@ -551,7 +571,7 @@ namespace Ryujinx.Graphics.Gpu.Image
tracked = tracked || ShouldFlushTriggerTracking();
bool flushed = false;
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, split, _) =>
{
int startSlice = 0;
int endSlice = 0;
@@ -605,7 +625,9 @@ namespace Ryujinx.Graphics.Gpu.Image
flushed = true;
}
});
return true;
}, out _);
Storage.SignalModifiedDirty();
@@ -694,7 +716,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _, _) =>
{
for (int i = 0; i < regionCount; i++)
{
@@ -702,7 +724,9 @@ namespace Ryujinx.Graphics.Gpu.Image
group.SignalModified(_context);
}
});
return true;
}, out _);
}
/// <summary>
@@ -715,16 +739,20 @@ namespace Ryujinx.Graphics.Gpu.Image
ModifiedSequence = _context.GetModifiedSequence();
ClearIncompatibleOverlaps(texture);
EvaluateRelevantHandles(texture, (baseHandle, regionCount, _) =>
EvaluateRelevantHandles(texture, _signalModifyingCallback, out _, bound);
}
bool SignalModifyingCallback(int baseHandle, int regionCount, bool split, bool bound)
{
for (int i = 0; i < regionCount; i++)
{
for (int i = 0; i < regionCount; i++)
{
TextureGroupHandle group = _handles[baseHandle + i];
TextureGroupHandle group = _handles[baseHandle + i];
group.SignalModifying(bound, _context);
}
});
group.SignalModifying(bound, _context);
}
return true;
}
/// <summary>
@@ -768,16 +796,16 @@ namespace Ryujinx.Graphics.Gpu.Image
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
/// This can be called for multiple disjoint ranges, if required.
/// </param>
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback)
private void EvaluateRelevantHandles(Texture texture, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
{
if (texture == Storage || !(_hasMipViews || _hasLayerViews))
{
callback(0, _handles.Length);
result = callback(0, _handles.Length, specialData: specialData);
return;
}
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback);
EvaluateRelevantHandles(texture.FirstLayer, texture.FirstLevel, texture.Info.GetSlices(), texture.Info.Levels, callback, out result, specialData);
}
/// <summary>
@@ -792,11 +820,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// A function to be called with the base index of the range of handles for the given texture, and the number of handles it covers.
/// This can be called for multiple disjoint ranges, if required.
/// </param>
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback)
private void EvaluateRelevantHandles(int firstLayer, int firstLevel, int slices, int levels, HandlesCallbackDelegate callback, out bool result, bool specialData = false)
{
int targetLayerHandles = _hasLayerViews ? slices : 1;
int targetLevelHandles = _hasMipViews ? levels : 1;
result = false;
if (_isBuffer)
{
return;
@@ -809,7 +839,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
// When there are no layer views, the mips are at a consistent offset.
callback(firstLevel, targetLevelHandles);
result = callback(firstLevel, targetLevelHandles, specialData: specialData);
}
else
{
@@ -823,7 +853,7 @@ namespace Ryujinx.Graphics.Gpu.Image
while (levels-- > 1)
{
callback(firstLayer + levelIndex, slices);
result = callback(firstLayer + levelIndex, slices, specialData: specialData);
levelIndex += layerCount;
layerCount = Math.Max(layerCount >> 1, 1);
@@ -840,7 +870,7 @@ namespace Ryujinx.Graphics.Gpu.Image
totalSize += layerCount;
}
callback(firstLayer + levelIndex, totalSize);
result = callback(firstLayer + levelIndex, totalSize, specialData: specialData);
}
}
}
@@ -857,12 +887,12 @@ namespace Ryujinx.Graphics.Gpu.Image
for (int i = 0; i < slices; i++)
{
callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true);
result = callback(firstLevel + (firstLayer + i) * levelHandles, targetLevelHandles, true, specialData: specialData);
}
}
else
{
callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles);
result = callback(firstLevel + firstLayer * levelHandles, targetLevelHandles + (targetLayerHandles - 1) * levelHandles, specialData: specialData);
}
}
}
@@ -1441,8 +1471,16 @@ namespace Ryujinx.Graphics.Gpu.Image
var targetRange = new List<(int BaseHandle, int RegionCount)>();
var otherRange = new List<(int BaseHandle, int RegionCount)>();
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, _) => targetRange.Add((baseHandle, regionCount)));
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, _) => otherRange.Add((baseHandle, regionCount)));
EvaluateRelevantHandles(firstLayer, firstLevel, other.Info.GetSlices(), other.Info.Levels, (baseHandle, regionCount, _, _) =>
{
targetRange.Add((baseHandle, regionCount));
return true;
}, out _);
otherGroup.EvaluateRelevantHandles(other, (baseHandle, regionCount, _, _) =>
{
otherRange.Add((baseHandle, regionCount));
return true;
}, out _);
int targetIndex = 0;
int otherIndex = 0;