aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2023-03-14 20:08:44 +0000
committerGitHub <noreply@github.com>2023-03-14 17:08:44 -0300
commit1fc90e57d2e7f7bb6886a58b81bcd1f4cb25f8cf (patch)
treeb2b2063d9ec830ef380651886a33d1e2dee9b497 /Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
parenteafcc314a96f2dc893291c6268ad00278159762f (diff)
Update range for remapped sparse textures instead of recreating them (#4442)1.1.664
* Update sparsely mapped texture ranges without recreating Important TODO in TexturePool. Smaller TODO: should I look into making textures with views also do this? It needs to be able to detect if the views can be instantly deleted without issue if they're now remapped. * Actually do partial updates * Signal group dirty after mappings changed * Fix various issues (should work now) * Further optimisation Should load a lot less data (16x) when partial updating 3d textures. * Improve stability * Allow granular uploads on large textures, improve rules * Actually avoid updating slices that aren't modified. * Address some feedback, minor optimisation * Small tweak * Refactor DereferenceRequest More specific initialization methods. * Improve code for resetting handles * Explain data loading a bit more * Add some safety for setting null from different threads. All texture sets come from the one thread, but null sets can come from multiple. Only decrement ref count if we succeeded the null set first. * Address feedback 1 * Make a bit safer
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/TextureGroup.cs')
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureGroup.cs152
1 files changed, 136 insertions, 16 deletions
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
index 12a640e1..d9b620aa 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
@@ -39,6 +39,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
class TextureGroup : IDisposable
{
+ /// <summary>
+ /// Threshold of layers to force granular handles (and thus partial loading) on array/3D textures.
+ /// </summary>
+ private const int GranularLayerThreshold = 8;
+
private delegate void HandlesCallbackDelegate(int baseHandle, int regionCount, bool split = false);
/// <summary>
@@ -116,7 +121,29 @@ namespace Ryujinx.Graphics.Gpu.Image
_allOffsets = size.AllOffsets;
_sliceSizes = size.SliceSizes;
- (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews);
+ if (Storage.Target.HasDepthOrLayers() && Storage.Info.GetSlices() > GranularLayerThreshold)
+ {
+ _hasLayerViews = true;
+ _hasMipViews = true;
+ }
+ else
+ {
+ (_hasLayerViews, _hasMipViews) = PropagateGranularity(hasLayerViews, hasMipViews);
+
+ // If the texture is partially mapped, fully subdivide handles immediately.
+
+ MultiRange range = Storage.Range;
+ for (int i = 0; i < range.Count; i++)
+ {
+ if (range.GetSubRange(i).Address == MemoryManager.PteUnmapped)
+ {
+ _hasLayerViews = true;
+ _hasMipViews = true;
+
+ break;
+ }
+ }
+ }
RecalculateHandleRegions();
}
@@ -249,7 +276,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
bool dirty = false;
bool anyModified = false;
- bool anyUnmapped = false;
+ bool anyNotDirty = false;
for (int i = 0; i < regionCount; i++)
{
@@ -294,20 +321,21 @@ namespace Ryujinx.Graphics.Gpu.Image
dirty |= handleDirty;
}
- anyUnmapped |= handleUnmapped;
-
if (group.NeedsCopy)
{
// The texture we copied from is still being written to. Copy from it again the next time this texture is used.
texture.SignalGroupDirty();
}
- _loadNeeded[baseHandle + i] = handleDirty && !handleUnmapped;
+ bool loadNeeded = handleDirty && !handleUnmapped;
+
+ anyNotDirty |= !loadNeeded;
+ _loadNeeded[baseHandle + i] = loadNeeded;
}
if (dirty)
{
- if (anyUnmapped || (_handles.Length > 1 && (anyModified || split)))
+ if (anyNotDirty || (_handles.Length > 1 && (anyModified || split)))
{
// Partial texture invalidation. Only update the layers/levels with dirty flags of the storage.
@@ -331,24 +359,56 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="regionCount">The number of handles to synchronize</param>
private void SynchronizePartial(int baseHandle, int regionCount)
{
+ int spanEndIndex = -1;
+ int spanBase = 0;
+ ReadOnlySpan<byte> dataSpan = ReadOnlySpan<byte>.Empty;
+
for (int i = 0; i < regionCount; i++)
{
if (_loadNeeded[baseHandle + i])
{
var info = GetHandleInformation(baseHandle + i);
+ // Ensure the data for this handle is loaded in the span.
+ if (spanEndIndex <= i - 1)
+ {
+ spanEndIndex = i;
+
+ if (_is3D)
+ {
+ // Look ahead to see how many handles need to be loaded.
+ for (int j = i + 1; j < regionCount; j++)
+ {
+ if (_loadNeeded[baseHandle + j])
+ {
+ spanEndIndex = j;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ var endInfo = spanEndIndex == i ? info : GetHandleInformation(baseHandle + spanEndIndex);
+
+ spanBase = _allOffsets[info.Index];
+ int spanLast = _allOffsets[endInfo.Index + endInfo.Layers * endInfo.Levels - 1];
+ int endOffset = Math.Min(spanLast + _sliceSizes[endInfo.BaseLevel + endInfo.Levels - 1], (int)Storage.Size);
+ int size = endOffset - spanBase;
+
+ dataSpan = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)spanBase, (ulong)size));
+ }
+
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
for (int layer = 0; layer < info.Layers; layer++)
{
for (int level = 0; level < info.Levels; level++)
{
int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
-
int offset = _allOffsets[offsetIndex];
- int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
- int size = endOffset - offset;
- ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
+ ReadOnlySpan<byte> data = dataSpan.Slice(offset - spanBase);
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
@@ -865,8 +925,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A TextureGroupHandle covering the given views</returns>
private TextureGroupHandle GenerateHandles(int viewStart, int views)
{
+ int viewEnd = viewStart + views - 1;
+ (_, int lastLevel) = GetLayerLevelForView(viewEnd);
+
int offset = _allOffsets[viewStart];
- int endOffset = (viewStart + views == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[viewStart + views];
+ int endOffset = _allOffsets[viewEnd] + _sliceSizes[lastLevel];
int size = endOffset - offset;
var result = new List<CpuRegionHandle>();
@@ -1057,7 +1120,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// The dirty flags from the previous handles will be kept.
/// </summary>
/// <param name="handles">The handles to replace the current handles with</param>
- private void ReplaceHandles(TextureGroupHandle[] handles)
+ /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
+ private void ReplaceHandles(TextureGroupHandle[] handles, bool rangeChanged)
{
if (_handles != null)
{
@@ -1065,9 +1129,50 @@ namespace Ryujinx.Graphics.Gpu.Image
foreach (TextureGroupHandle groupHandle in handles)
{
- foreach (CpuRegionHandle handle in groupHandle.Handles)
+ if (rangeChanged)
+ {
+ // When the storage range changes, this becomes a little different.
+ // If a range does not match one in the original, treat it as modified.
+ // It has been newly mapped and its data must be synchronized.
+
+ if (groupHandle.Handles.Length == 0)
+ {
+ continue;
+ }
+
+ foreach (var oldGroup in _handles)
+ {
+ if (!groupHandle.OverlapsWith(oldGroup.Offset, oldGroup.Size))
+ {
+ continue;
+ }
+
+ foreach (CpuRegionHandle handle in groupHandle.Handles)
+ {
+ bool hasMatch = false;
+
+ foreach (var oldHandle in oldGroup.Handles)
+ {
+ if (oldHandle.RangeEquals(handle))
+ {
+ hasMatch = true;
+ break;
+ }
+ }
+
+ if (hasMatch)
+ {
+ handle.Reprotect();
+ }
+ }
+ }
+ }
+ else
{
- handle.Reprotect();
+ foreach (CpuRegionHandle handle in groupHandle.Handles)
+ {
+ handle.Reprotect();
+ }
}
}
@@ -1089,7 +1194,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Recalculate handle regions for this texture group, and inherit existing state into the new handles.
/// </summary>
- private void RecalculateHandleRegions()
+ /// <param name="rangeChanged">True if the storage memory range changed since the last region handle generation</param>
+ private void RecalculateHandleRegions(bool rangeChanged = false)
{
TextureGroupHandle[] handles;
@@ -1171,7 +1277,21 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
- ReplaceHandles(handles);
+ ReplaceHandles(handles, rangeChanged);
+ }
+
+ /// <summary>
+ /// Regenerates handles when the storage range has been remapped.
+ /// This forces the regions to be fully subdivided.
+ /// </summary>
+ public void RangeChanged()
+ {
+ _hasLayerViews = true;
+ _hasMipViews = true;
+
+ RecalculateHandleRegions(true);
+
+ SignalAllDirty();
}
/// <summary>