aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Memory/Tracking
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Memory/Tracking')
-rw-r--r--Ryujinx.Memory/Tracking/AbstractRegion.cs8
-rw-r--r--Ryujinx.Memory/Tracking/IRegionHandle.cs1
-rw-r--r--Ryujinx.Memory/Tracking/MemoryTracking.cs17
-rw-r--r--Ryujinx.Memory/Tracking/MultiRegionHandle.cs11
-rw-r--r--Ryujinx.Memory/Tracking/PreciseRegionSignal.cs4
-rw-r--r--Ryujinx.Memory/Tracking/RegionHandle.cs35
-rw-r--r--Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs11
-rw-r--r--Ryujinx.Memory/Tracking/VirtualRegion.cs19
8 files changed, 103 insertions, 3 deletions
diff --git a/Ryujinx.Memory/Tracking/AbstractRegion.cs b/Ryujinx.Memory/Tracking/AbstractRegion.cs
index ffac439f..a3c3990e 100644
--- a/Ryujinx.Memory/Tracking/AbstractRegion.cs
+++ b/Ryujinx.Memory/Tracking/AbstractRegion.cs
@@ -53,6 +53,14 @@ namespace Ryujinx.Memory.Tracking
public abstract void Signal(ulong address, ulong size, bool write);
/// <summary>
+ /// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained.
+ /// </summary>
+ /// <param name="address">Address accessed</param>
+ /// <param name="size">Size of the region affected in bytes</param>
+ /// <param name="write">Whether the region was written to or read</param>
+ public abstract void SignalPrecise(ulong address, ulong size, bool write);
+
+ /// <summary>
/// Split this region into two, around the specified address.
/// This region is updated to end at the split address, and a new region is created to represent past that point.
/// </summary>
diff --git a/Ryujinx.Memory/Tracking/IRegionHandle.cs b/Ryujinx.Memory/Tracking/IRegionHandle.cs
index ec802cb3..9d99d90e 100644
--- a/Ryujinx.Memory/Tracking/IRegionHandle.cs
+++ b/Ryujinx.Memory/Tracking/IRegionHandle.cs
@@ -13,5 +13,6 @@ namespace Ryujinx.Memory.Tracking
void ForceDirty();
void Reprotect(bool asDirty = false);
void RegisterAction(RegionSignal action);
+ void RegisterPreciseAction(PreciseRegionSignal action);
}
}
diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/Ryujinx.Memory/Tracking/MemoryTracking.cs
index ed3d7e38..aa7f9a6c 100644
--- a/Ryujinx.Memory/Tracking/MemoryTracking.cs
+++ b/Ryujinx.Memory/Tracking/MemoryTracking.cs
@@ -190,12 +190,15 @@ namespace Ryujinx.Memory.Tracking
/// <summary>
/// Signal that a virtual memory event happened at the given location.
+ /// This can be flagged as a precise event, which will avoid reprotection and call special handlers if possible.
+ /// A precise event has an exact address and size, rather than triggering on page granularity.
/// </summary>
/// <param name="address">Virtual address accessed</param>
/// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param>
+ /// <param name="precise">True if the access is precise, false otherwise</param>
/// <returns>True if the event triggered any tracking regions, false otherwise</returns>
- public bool VirtualMemoryEvent(ulong address, ulong size, bool write)
+ public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise = false)
{
// Look up the virtual region using the region list.
// Signal up the chain to relevant handles.
@@ -206,7 +209,7 @@ namespace Ryujinx.Memory.Tracking
int count = _virtualRegions.FindOverlapsNonOverlapping(address, size, ref overlaps);
- if (count == 0)
+ if (count == 0 && !precise)
{
if (!_memoryManager.IsMapped(address))
{
@@ -224,7 +227,15 @@ namespace Ryujinx.Memory.Tracking
for (int i = 0; i < count; i++)
{
VirtualRegion region = overlaps[i];
- region.Signal(address, size, write);
+
+ if (precise)
+ {
+ region.SignalPrecise(address, size, write);
+ }
+ else
+ {
+ region.Signal(address, size, write);
+ }
}
}
diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs
index 638e7290..cce7ccb4 100644
--- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs
+++ b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs
@@ -206,6 +206,17 @@ namespace Ryujinx.Memory.Tracking
}
}
+ public void RegisterPreciseAction(ulong address, ulong size, PreciseRegionSignal action)
+ {
+ int startHandle = (int)((address - Address) / Granularity);
+ int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
+
+ for (int i = startHandle; i <= lastHandle; i++)
+ {
+ _handles[i].RegisterPreciseAction(action);
+ }
+ }
+
public void Dispose()
{
foreach (var handle in _handles)
diff --git a/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs b/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs
new file mode 100644
index 00000000..038f9595
--- /dev/null
+++ b/Ryujinx.Memory/Tracking/PreciseRegionSignal.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.Memory.Tracking
+{
+ public delegate bool PreciseRegionSignal(ulong address, ulong size, bool write);
+}
diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs
index 40deba98..5ecd53a2 100644
--- a/Ryujinx.Memory/Tracking/RegionHandle.cs
+++ b/Ryujinx.Memory/Tracking/RegionHandle.cs
@@ -37,6 +37,7 @@ namespace Ryujinx.Memory.Tracking
private object _preActionLock = new object();
private RegionSignal _preAction; // Action to perform before a read or write. This will block the memory access.
+ private PreciseRegionSignal _preciseAction; // Action to perform on a precise read or write.
private readonly List<VirtualRegion> _regions;
private readonly MemoryTracking _tracking;
private bool _disposed;
@@ -113,7 +114,10 @@ namespace Ryujinx.Memory.Tracking
/// <summary>
/// Signal that a memory action occurred within this handle's virtual regions.
/// </summary>
+ /// <param name="address">Address accessed</param>
+ /// <param name="size">Size of the region affected in bytes</param>
/// <param name="write">Whether the region was written to or read</param>
+ /// <param name="handleIterable">Reference to the handles being iterated, in case the list needs to be copied</param>
internal void Signal(ulong address, ulong size, bool write, ref IList<RegionHandle> handleIterable)
{
// If this handle was already unmapped (even if just partially),
@@ -164,6 +168,27 @@ namespace Ryujinx.Memory.Tracking
}
/// <summary>
+ /// Signal that a precise memory action occurred within this handle's virtual regions.
+ /// If there is no precise action, or the action returns false, the normal signal handler will be called.
+ /// </summary>
+ /// <param name="address">Address accessed</param>
+ /// <param name="size">Size of the region affected in bytes</param>
+ /// <param name="write">Whether the region was written to or read</param>
+ /// <param name="handleIterable">Reference to the handles being iterated, in case the list needs to be copied</param>
+ /// <returns>True if a precise action was performed and returned true, false otherwise</returns>
+ internal bool SignalPrecise(ulong address, ulong size, bool write, ref IList<RegionHandle> handleIterable)
+ {
+ if (!Unmapped && _preciseAction != null && _preciseAction(address, size, write))
+ {
+ return true;
+ }
+
+ Signal(address, size, write, ref handleIterable);
+
+ return false;
+ }
+
+ /// <summary>
/// Force this handle to be dirty, without reprotecting.
/// </summary>
public void ForceDirty()
@@ -244,6 +269,16 @@ namespace Ryujinx.Memory.Tracking
}
/// <summary>
+ /// Register an action to perform when a precise access occurs (one with exact address and size).
+ /// If the action returns true, read/write tracking are skipped.
+ /// </summary>
+ /// <param name="action">Action to call on read or write</param>
+ public void RegisterPreciseAction(PreciseRegionSignal action)
+ {
+ _preciseAction = action;
+ }
+
+ /// <summary>
/// Register an action to perform when the region is written to.
/// This action will not be removed when it is called - it is called each time the dirty flag is set.
/// </summary>
diff --git a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs
index eabbd723..47fe72e5 100644
--- a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs
+++ b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs
@@ -63,6 +63,17 @@ namespace Ryujinx.Memory.Tracking
}
}
+ public void RegisterPreciseAction(PreciseRegionSignal action)
+ {
+ foreach (var handle in _handles)
+ {
+ if (handle != null)
+ {
+ handle?.RegisterPreciseAction((address, size, write) => action(handle.Address, handle.Size, write));
+ }
+ }
+ }
+
public void QueryModified(Action<ulong, ulong> modifiedAction)
{
if (!Dirty)
diff --git a/Ryujinx.Memory/Tracking/VirtualRegion.cs b/Ryujinx.Memory/Tracking/VirtualRegion.cs
index 40f56351..57a0344a 100644
--- a/Ryujinx.Memory/Tracking/VirtualRegion.cs
+++ b/Ryujinx.Memory/Tracking/VirtualRegion.cs
@@ -31,6 +31,25 @@ namespace Ryujinx.Memory.Tracking
UpdateProtection();
}
+ public override void SignalPrecise(ulong address, ulong size, bool write)
+ {
+ IList<RegionHandle> handles = Handles;
+
+ bool allPrecise = true;
+
+ for (int i = 0; i < handles.Count; i++)
+ {
+ allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles);
+ }
+
+ // Only update protection if a regular signal handler was called.
+ // This allows precise actions to skip reprotection costs if they want (they can still do it manually).
+ if (!allPrecise)
+ {
+ UpdateProtection();
+ }
+ }
+
/// <summary>
/// Signal that this region has been mapped or unmapped.
/// </summary>