GPU: Use a bitmap to track buffer modified flags. (#3775)

* Initial implementation

* Some improvements.

* Fix incorrect cast

* Performance improvement and improved correctness

* Add very fast path when all handles are checked.

* Slightly faster

* Add comment

* De-virtualize region handle

All region handles are now bitmap backed.

* Remove non-bitmap tracking

* Remove unused methods

* Add docs, remove unused methods

* Address Feedback

* Rename file
This commit is contained in:
riperiperi
2022-10-29 23:07:37 +01:00
committed by GitHub
parent 141cf61ff7
commit 3d98e1361b
5 changed files with 696 additions and 74 deletions

View File

@ -1,5 +1,4 @@
using Ryujinx.Memory.Range;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@ -10,7 +9,7 @@ namespace Ryujinx.Memory.Tracking
/// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made,
/// and an action can be performed when the region is read to or written from.
/// </summary>
public class RegionHandle : IRegionHandle, IRange
public class RegionHandle : IRegionHandle
{
/// <summary>
/// If more than this number of checks have been performed on a dirty flag since its last reprotect,
@ -23,7 +22,20 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
private static int VolatileThreshold = 5;
public bool Dirty { get; private set; }
public bool Dirty
{
get
{
return Bitmap.IsSet(DirtyBit);
}
protected set
{
Bitmap.Set(DirtyBit, value);
}
}
internal int SequenceNumber { get; set; }
public bool Unmapped { get; private set; }
public ulong Address { get; }
@ -31,7 +43,6 @@ namespace Ryujinx.Memory.Tracking
public ulong EndAddress { get; }
internal IMultiRegionHandle Parent { get; set; }
internal int SequenceNumber { get; set; }
private event Action _onDirty;
@ -68,17 +79,26 @@ namespace Ryujinx.Memory.Tracking
internal RegionSignal PreAction => _preAction;
internal ConcurrentBitmap Bitmap;
internal int DirtyBit;
/// <summary>
/// Create a new region handle. The handle is registered with the given tracking object,
/// Create a new bitmap backed region handle. The handle is registered with the given tracking object,
/// and will be notified of any changes to the specified region.
/// </summary>
/// <param name="tracking">Tracking object for the target memory block</param>
/// <param name="address">Virtual address of the region to track</param>
/// <param name="size">Size of the region to track</param>
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
/// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true)
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ConcurrentBitmap bitmap, int bit, bool mapped = true)
{
Bitmap = bitmap;
DirtyBit = bit;
Dirty = mapped;
Unmapped = !mapped;
Address = address;
Size = size;
@ -92,6 +112,54 @@ namespace Ryujinx.Memory.Tracking
}
}
/// <summary>
/// Create a new region handle. The handle is registered with the given tracking object,
/// and will be notified of any changes to the specified region.
/// </summary>
/// <param name="tracking">Tracking object for the target memory block</param>
/// <param name="address">Virtual address of the region to track</param>
/// <param name="size">Size of the region to track</param>
/// <param name="mapped">True if the region handle starts mapped</param>
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true)
{
Bitmap = new ConcurrentBitmap(1, mapped);
Unmapped = !mapped;
Address = address;
Size = size;
EndAddress = address + size;
_tracking = tracking;
_regions = tracking.GetVirtualRegionsForHandle(address, size);
foreach (var region in _regions)
{
region.Handles.Add(this);
}
}
/// <summary>
/// Replace the bitmap and bit index used to track dirty state.
/// </summary>
/// <remarks>
/// The tracking lock should be held when this is called, to ensure neither bitmap is modified.
/// </remarks>
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
internal void ReplaceBitmap(ConcurrentBitmap bitmap, int bit)
{
// Assumes the tracking lock is held, so nothing else can signal right now.
var oldBitmap = Bitmap;
var oldBit = DirtyBit;
bitmap.Set(bit, Dirty);
Bitmap = bitmap;
DirtyBit = bit;
Dirty |= oldBitmap.IsSet(oldBit);
}
/// <summary>
/// Clear the volatile state of this handle.
/// </summary>
@ -108,7 +176,7 @@ namespace Ryujinx.Memory.Tracking
public bool DirtyOrVolatile()
{
_checkCount++;
return Dirty || _volatile;
return _volatile || Dirty;
}
/// <summary>
@ -195,6 +263,8 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
public void ForceDirty()
{
_checkCount++;
Dirty = true;
}