GPU: Allow granular buffer updates from the constant buffer updater (#4749)
* GPU: Allow granular buffer updates from the constant buffer updater Sometimes, constant buffer updates can't be avoided, either due to a cb0 access that cannot be eliminated, or the game updating a buffer between draws to the detriment of everyone. To avoid uploading the full 4096 bytes each time, this PR remembers the offset and size containing all constant buffer updates since the last sync. It will then upload that range after sync. * Allow clearing the dirty range * Always use precise Might want to not do this if distance between the existing range and new one is too high. * Use old force dirty mechanism when distance between regions is too great * Update src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Fix inheritance of _dirtyStart and _dirtyEnd --------- Co-authored-by: gdkchan <gab.dark.100@gmail.com>
This commit is contained in:
parent
1f664100bd
commit
1f5d881860
1 changed files with 104 additions and 5 deletions
|
@ -68,6 +68,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
private int _referenceCount = 1;
|
private int _referenceCount = 1;
|
||||||
|
|
||||||
|
private ulong _dirtyStart = ulong.MaxValue;
|
||||||
|
private ulong _dirtyEnd = ulong.MaxValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the buffer.
|
/// Creates a new instance of the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -221,6 +224,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
_sequenceNumber = _context.SequenceNumber;
|
_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
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inherit modified ranges from another buffer.
|
/// Inherit modified and dirty ranges from another buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="from">The buffer to inherit from</param>
|
/// <param name="from">The buffer to inherit from</param>
|
||||||
public void InheritModifiedRanges(Buffer from)
|
public void InheritModifiedRanges(Buffer from)
|
||||||
|
@ -320,6 +343,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
|
||||||
_modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction);
|
_modifiedRanges.InheritRanges(from._modifiedRanges, registerRangeAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (from._dirtyStart != ulong.MaxValue)
|
||||||
|
{
|
||||||
|
ForceDirty(from._dirtyStart, from._dirtyEnd - from._dirtyStart);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -338,6 +366,44 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear the dirty range that overlaps with the given region.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Start address of the modified region</param>
|
||||||
|
/// <param name="size">Size of the modified region</param>
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicate that a region of the buffer was modified, and must be loaded from memory.
|
/// Indicate that a region of the buffer was modified, and must be loaded from memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -357,6 +423,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
mSize = maxSize;
|
mSize = maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClearDirty(mAddress, mSize);
|
||||||
|
|
||||||
if (_modifiedRanges != null)
|
if (_modifiedRanges != null)
|
||||||
{
|
{
|
||||||
_modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate);
|
_modifiedRanges.ExcludeModifiedRegions(mAddress, mSize, _loadDelegate);
|
||||||
|
@ -380,14 +448,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mAddress">Start address of the modified region</param>
|
/// <param name="mAddress">Start address of the modified region</param>
|
||||||
/// <param name="mSize">Size of the region to force dirty</param>
|
/// <param name="mSize">Size of the region to force dirty</param>
|
||||||
public void ForceDirty(ulong mAddress, ulong mSize)
|
private void ForceTrackingDirty(ulong mAddress, ulong mSize)
|
||||||
{
|
{
|
||||||
_modifiedRanges?.Clear(mAddress, mSize);
|
|
||||||
|
|
||||||
if (_useGranular)
|
if (_useGranular)
|
||||||
{
|
{
|
||||||
_memoryTrackingGranular.ForceDirty(mAddress, mSize);
|
_memoryTrackingGranular.ForceDirty(mAddress, mSize);
|
||||||
|
@ -399,6 +465,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mAddress">Start address of the modified region</param>
|
||||||
|
/// <param name="mSize">Size of the region to force dirty</param>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs copy of all the buffer data from one buffer to another.
|
/// Performs copy of all the buffer data from one buffer to another.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Reference in a new issue