diff options
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Image/TextureGroup.cs')
-rw-r--r-- | Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 152 |
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> |