diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index ef8c80746..8e16b3ae1 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -68,6 +68,9 @@ namespace Ryujinx.Graphics.Gpu.Memory private int _referenceCount = 1; + private ulong _dirtyStart = ulong.MaxValue; + private ulong _dirtyEnd = ulong.MaxValue; + /// /// Creates a new instance of the buffer. /// @@ -221,6 +224,26 @@ namespace Ryujinx.Graphics.Gpu.Memory } _sequenceNumber = _context.SequenceNumber; + _dirtyStart = ulong.MaxValue; + } + } + + if (_dirtyStart != ulong.MaxValue) + { + ulong end = address + size; + + if (end > _dirtyStart && address < _dirtyEnd) + { + if (_modifiedRanges != null) + { + _modifiedRanges.ExcludeModifiedRegions(_dirtyStart, _dirtyEnd - _dirtyStart, _loadDelegate); + } + else + { + LoadRegion(_dirtyStart, _dirtyEnd - _dirtyStart); + } + + _dirtyStart = ulong.MaxValue; } } } @@ -291,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// - /// Inherit modified ranges from another buffer. + /// Inherit modified and dirty ranges from another buffer. /// /// The buffer to inherit from public void InheritModifiedRanges(Buffer from) @@ -320,6 +343,11 @@ namespace Ryujinx.Graphics.Gpu.Memory _modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction); } + + if (from._dirtyStart != ulong.MaxValue) + { + ForceDirty(from._dirtyStart, from._dirtyEnd - from._dirtyStart); + } } /// @@ -338,6 +366,44 @@ namespace Ryujinx.Graphics.Gpu.Memory return false; } + /// + /// Clear the dirty range that overlaps with the given region. + /// + /// Start address of the modified region + /// Size of the modified region + private void ClearDirty(ulong address, ulong size) + { + if (_dirtyStart != ulong.MaxValue) + { + ulong end = address + size; + + if (end > _dirtyStart && address < _dirtyEnd) + { + if (address <= _dirtyStart) + { + // Cut off the start. + + if (end < _dirtyEnd) + { + _dirtyStart = end; + } + else + { + _dirtyStart = ulong.MaxValue; + } + } + else if (end >= _dirtyEnd) + { + // Cut off the end. + + _dirtyEnd = address; + } + + // If fully contained, do nothing. + } + } + } + /// /// Indicate that a region of the buffer was modified, and must be loaded from memory. /// @@ -357,6 +423,8 @@ namespace Ryujinx.Graphics.Gpu.Memory mSize = maxSize; } + ClearDirty(mAddress, mSize); + if (_modifiedRanges != null) { _modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate); @@ -380,14 +448,12 @@ namespace Ryujinx.Graphics.Gpu.Memory } /// - /// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check. + /// Force a region of the buffer to be dirty within the memory tracking. Avoids reprotection and nullifies sequence number check. /// /// Start address of the modified region /// Size of the region to force dirty - public void ForceDirty(ulong mAddress, ulong mSize) + private void ForceTrackingDirty(ulong mAddress, ulong mSize) { - _modifiedRanges?.Clear(mAddress, mSize); - if (_useGranular) { _memoryTrackingGranular.ForceDirty(mAddress, mSize); @@ -399,6 +465,39 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check. + /// + /// Start address of the modified region + /// Size of the region to force dirty + public void ForceDirty(ulong mAddress, ulong mSize) + { + _modifiedRanges?.Clear(mAddress, mSize); + + ulong end = mAddress + mSize; + + if (_dirtyStart == ulong.MaxValue) + { + _dirtyStart = mAddress; + _dirtyEnd = end; + } + else + { + // Is the new range more than a page away from the existing one? + + if ((long)(mAddress - _dirtyEnd) >= (long)MemoryManager.PageSize || + (long)(_dirtyStart - end) >= (long)MemoryManager.PageSize) + { + ForceTrackingDirty(mAddress, mSize); + } + else + { + _dirtyStart = Math.Min(_dirtyStart, mAddress); + _dirtyEnd = Math.Max(_dirtyEnd, end); + } + } + } + /// /// Performs copy of all the buffer data from one buffer to another. ///