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.
///