From efb135b74c9c0ff1de2dfd7d2a431bd23185ca66 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 16 Feb 2023 18:28:49 -0300 Subject: [PATCH] Clear CPU side data on GPU buffer clears (#4125) * Clear CPU side data on GPU buffer clears * Implement tracked fill operation that can signal other resource types except buffer * Fix tests, add missing XML doc * PR feedback --- ARMeilleure/Memory/IMemoryManager.cs | 3 +- ARMeilleure/Signal/NativeSignalHandler.cs | 2 +- Ryujinx.Cpu/AppleHv/HvMemoryManager.cs | 20 +++++----- Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs | 9 +++-- Ryujinx.Cpu/Jit/MemoryManager.cs | 18 ++++----- Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs | 20 +++++----- Ryujinx.Cpu/MemoryEhMeilleure.cs | 2 +- Ryujinx.Graphics.Gpu/Image/Pool.cs | 2 +- Ryujinx.Graphics.Gpu/Image/TextureGroup.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 4 +- Ryujinx.Graphics.Gpu/Memory/BufferCache.cs | 2 +- Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs | 37 ++++++++++++++---- Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs | 13 +++++++ .../MockVirtualMemoryManager.cs | 2 +- .../MultiRegionTrackingTests.cs | 16 ++++---- Ryujinx.Memory.Tests/TrackingTests.cs | 24 ++++++------ Ryujinx.Memory/AddressSpaceManager.cs | 2 +- Ryujinx.Memory/IVirtualMemoryManager.cs | 3 +- Ryujinx.Memory/Tracking/AbstractRegion.cs | 8 ++-- Ryujinx.Memory/Tracking/MemoryTracking.cs | 38 +++++++++++-------- Ryujinx.Memory/Tracking/MultiRegionHandle.cs | 14 +++++-- Ryujinx.Memory/Tracking/RegionHandle.cs | 24 ++++++++++-- .../Tracking/SmartMultiRegionHandle.cs | 12 +++--- Ryujinx.Memory/Tracking/VirtualRegion.cs | 16 ++++++-- Ryujinx.Tests/Memory/MockMemoryManager.cs | 2 +- 25 files changed, 188 insertions(+), 107 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs index c4ea70d17c..5eb1fadd65 100644 --- a/ARMeilleure/Memory/IMemoryManager.cs +++ b/ARMeilleure/Memory/IMemoryManager.cs @@ -71,6 +71,7 @@ namespace ARMeilleure.Memory /// Size of the region /// True if the region was written, false if read /// True if the access is precise, false otherwise - void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); + /// Optional ID of the handles that should not be signalled + void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null); } } \ No newline at end of file diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/ARMeilleure/Signal/NativeSignalHandler.cs index 77eabe2675..cddeb81742 100644 --- a/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/ARMeilleure/Signal/NativeSignalHandler.cs @@ -222,7 +222,7 @@ namespace ARMeilleure.Signal // Tracking action should be non-null to call it, otherwise assume false return. context.BranchIfFalse(skipActionLabel, trackingActionPtr); - Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite, Const(0)); + Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite); context.Copy(inRegionLocal, result); context.MarkLabel(skipActionLabel); diff --git a/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index 222dcae1b8..437e02aea3 100644 --- a/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -634,13 +634,13 @@ namespace Ryujinx.Cpu.AppleHv /// /// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { AssertValidAddressAndSize(va, size); if (precise) { - Tracking.VirtualMemoryEvent(va, size, write, precise: true); + Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId); return; } @@ -663,7 +663,7 @@ namespace Ryujinx.Cpu.AppleHv if (state >= tag) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); return; } else if (state == 0) @@ -706,7 +706,7 @@ namespace Ryujinx.Cpu.AppleHv // Only trigger tracking from reads if both bits are set on any page. if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); break; } } @@ -822,21 +822,21 @@ namespace Ryujinx.Cpu.AppleHv } /// - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); } /// - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); } /// - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); } /// diff --git a/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs b/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs index 8004d39bcf..92d3c76ca1 100644 --- a/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs +++ b/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs @@ -28,8 +28,9 @@ namespace Ryujinx.Cpu /// /// CPU virtual address of the region /// Size of the region + /// Handle ID /// The memory tracking handle - CpuRegionHandle BeginTracking(ulong address, ulong size); + CpuRegionHandle BeginTracking(ulong address, ulong size, int id); /// /// Obtains a memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. @@ -38,8 +39,9 @@ namespace Ryujinx.Cpu /// Size of the region /// Handles to inherit state from or reuse. When none are present, provide null /// Desired granularity of write tracking + /// Handle ID /// The memory tracking handle - CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity); + CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id); /// /// Obtains a smart memory tracking handle for the given virtual region, with a specified granularity. This should be disposed when finished with. @@ -47,7 +49,8 @@ namespace Ryujinx.Cpu /// CPU virtual address of the region /// Size of the region /// Desired granularity of write tracking + /// Handle ID /// The memory tracking handle - CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity); + CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id); } } diff --git a/Ryujinx.Cpu/Jit/MemoryManager.cs b/Ryujinx.Cpu/Jit/MemoryManager.cs index 014d843b5b..8542d53e23 100644 --- a/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -629,31 +629,31 @@ namespace Ryujinx.Cpu.Jit } /// - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); } /// - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); } /// - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); } /// - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { AssertValidAddressAndSize(va, size); if (precise) { - Tracking.VirtualMemoryEvent(va, size, write, precise: true); + Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId); return; } @@ -676,7 +676,7 @@ namespace Ryujinx.Cpu.Jit if ((pte & tag) != 0) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); break; } diff --git a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 856b6b9b09..090740abe1 100644 --- a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -518,13 +518,13 @@ namespace Ryujinx.Cpu.Jit /// /// This function also validates that the given range is both valid and mapped, and will throw if it is not. /// - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { AssertValidAddressAndSize(va, size); if (precise) { - Tracking.VirtualMemoryEvent(va, size, write, precise: true); + Tracking.VirtualMemoryEvent(va, size, write, precise: true, exemptId); return; } @@ -547,7 +547,7 @@ namespace Ryujinx.Cpu.Jit if (state >= tag) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); return; } else if (state == 0) @@ -590,7 +590,7 @@ namespace Ryujinx.Cpu.Jit // Only trigger tracking from reads if both bits are set on any page. if (write || (pte & (pte >> 1) & BlockMappedMask) != 0) { - Tracking.VirtualMemoryEvent(va, size, write); + Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId); break; } } @@ -706,21 +706,21 @@ namespace Ryujinx.Cpu.Jit } /// - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, int id) { - return new CpuRegionHandle(Tracking.BeginTracking(address, size)); + return new CpuRegionHandle(Tracking.BeginTracking(address, size, id)); } /// - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id) { - return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity)); + return new CpuMultiRegionHandle(Tracking.BeginGranularTracking(address, size, handles, granularity, id)); } /// - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { - return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity)); + return new CpuSmartMultiRegionHandle(Tracking.BeginSmartGranularTracking(address, size, granularity, id)); } /// diff --git a/Ryujinx.Cpu/MemoryEhMeilleure.cs b/Ryujinx.Cpu/MemoryEhMeilleure.cs index 806ef81139..0b434ea749 100644 --- a/Ryujinx.Cpu/MemoryEhMeilleure.cs +++ b/Ryujinx.Cpu/MemoryEhMeilleure.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Cpu { public class MemoryEhMeilleure : IDisposable { - private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write, bool precise = false); + private delegate bool TrackingEventDelegate(ulong address, ulong size, bool write); private readonly MemoryTracking _tracking; private readonly TrackingEventDelegate _trackingEvent; diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index ee4c051f4c..3e557c0bd3 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Image Address = address; Size = size; - _memoryTracking = physicalMemory.BeginGranularTracking(address, size); + _memoryTracking = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Pool); _memoryTracking.RegisterPreciseAction(address, size, PreciseAction); _modifiedDelegate = RegionModified; } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 1040b394a0..12a640e15c 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -854,7 +854,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// A CpuRegionHandle covering the given range private CpuRegionHandle GenerateHandle(ulong address, ulong size) { - return _physicalMemory.BeginTracking(address, size); + return _physicalMemory.BeginTracking(address, size, ResourceKind.Texture); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index a624386ed9..3778cd824c 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -105,13 +105,13 @@ namespace Ryujinx.Graphics.Gpu.Memory if (_useGranular) { - _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles); + _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, ResourceKind.Buffer, baseHandles); _memoryTrackingGranular.RegisterPreciseAction(address, size, PreciseAction); } else { - _memoryTracking = physicalMemory.BeginTracking(address, size); + _memoryTracking = physicalMemory.BeginTracking(address, size, ResourceKind.Buffer); if (baseHandles != null) { diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index 00f5908313..a5a9b75e90 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -368,7 +368,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value); - buffer.SignalModified(address, size); + memoryManager.Physical.FillTrackedResource(address, size, value, ResourceKind.Buffer); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index c1fc0c5cdb..bd33383e57 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -7,6 +7,7 @@ using Ryujinx.Memory.Range; using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.Graphics.Gpu.Memory @@ -295,23 +296,41 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Fills the specified memory region with a 32-bit integer value. + /// + /// CPU virtual address of the region + /// Size of the region + /// Value to fill the region with + /// Kind of the resource being filled, which will not be signalled as CPU modified + public void FillTrackedResource(ulong address, ulong size, uint value, ResourceKind kind) + { + _cpuMemory.SignalMemoryTracking(address, size, write: true, precise: true, (int)kind); + + using WritableRegion region = _cpuMemory.GetWritableRegion(address, (int)size); + + MemoryMarshal.Cast(region.Memory.Span).Fill(value); + } + /// /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// /// CPU virtual address of the region /// Size of the region + /// Kind of the resource being tracked /// The memory tracking handle - public CpuRegionHandle BeginTracking(ulong address, ulong size) + public CpuRegionHandle BeginTracking(ulong address, ulong size, ResourceKind kind) { - return _cpuMemory.BeginTracking(address, size); + return _cpuMemory.BeginTracking(address, size, (int)kind); } /// /// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with. /// /// Ranges of physical memory where the data is located + /// Kind of the resource being tracked /// The memory tracking handle - public GpuRegionHandle BeginTracking(MultiRange range) + public GpuRegionHandle BeginTracking(MultiRange range, ResourceKind kind) { var cpuRegionHandles = new CpuRegionHandle[range.Count]; int count = 0; @@ -321,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Memory var currentRange = range.GetSubRange(i); if (currentRange.Address != MemoryManager.PteUnmapped) { - cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size); + cpuRegionHandles[count++] = _cpuMemory.BeginTracking(currentRange.Address, currentRange.Size, (int)kind); } } @@ -338,12 +357,13 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// CPU virtual address of the region /// Size of the region + /// Kind of the resource being tracked /// Handles to inherit state from or reuse /// Desired granularity of write tracking /// The memory tracking handle - public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles = null, ulong granularity = 4096) + public CpuMultiRegionHandle BeginGranularTracking(ulong address, ulong size, ResourceKind kind, IEnumerable handles = null, ulong granularity = 4096) { - return _cpuMemory.BeginGranularTracking(address, size, handles, granularity); + return _cpuMemory.BeginGranularTracking(address, size, handles, granularity, (int)kind); } /// @@ -351,11 +371,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// CPU virtual address of the region /// Size of the region + /// Kind of the resource being tracked /// Desired granularity of write tracking /// The memory tracking handle - public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity = 4096) + public CpuSmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ResourceKind kind, ulong granularity = 4096) { - return _cpuMemory.BeginSmartGranularTracking(address, size, granularity); + return _cpuMemory.BeginSmartGranularTracking(address, size, granularity, (int)kind); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs b/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs new file mode 100644 index 0000000000..55d697b81a --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/ResourceKind.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + /// + /// Kind of a GPU resource. + /// + enum ResourceKind + { + None, + Buffer, + Texture, + Pool + } +} diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 06eb4729e0..6729f4a36f 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -96,7 +96,7 @@ namespace Ryujinx.Memory.Tests throw new NotImplementedException(); } - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { throw new NotImplementedException(); } diff --git a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs b/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs index c607464d21..38cb49216d 100644 --- a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs +++ b/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs @@ -34,8 +34,8 @@ namespace Ryujinx.Memory.Tests private IMultiRegionHandle GetGranular(bool smart, ulong address, ulong size, ulong granularity) { return smart ? - _tracking.BeginSmartGranularTracking(address, size, granularity) : - (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity); + _tracking.BeginSmartGranularTracking(address, size, granularity, 0) : + (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity, 0); } private void RandomOrder(Random random, List indices, Action action) @@ -216,7 +216,7 @@ namespace Ryujinx.Memory.Tests { int region = regionSizes[i]; handle.QueryModified(address, (ulong)(PageSize * region), (address, size) => { }); - + // There should be a gap between regions, // So that they don't combine and we can see the full effects. address += (ulong)(PageSize * (region + 1)); @@ -294,7 +294,7 @@ namespace Ryujinx.Memory.Tests bool[] actionsTriggered = new bool[3]; - MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); + MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0); PreparePages(granular, 3, PageSize * 3); // Write to the second handle in the multiregion. @@ -307,7 +307,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < 3; i++) { - singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize); + singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize, 0); singlePages[i].Reprotect(); } @@ -321,7 +321,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < 3; i++) { - doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2); + doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2, 0); doublePages[i].Reprotect(); } @@ -340,7 +340,7 @@ namespace Ryujinx.Memory.Tests doublePages }; - MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize); + MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize, 0); bool[] expectedDirty = new bool[] { @@ -405,7 +405,7 @@ namespace Ryujinx.Memory.Tests { bool actionTriggered = false; - MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); + MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize, 0); PreparePages(granular, 3, PageSize * 3); // Add a precise action to the second and third handle in the multiregion. diff --git a/Ryujinx.Memory.Tests/TrackingTests.cs b/Ryujinx.Memory.Tests/TrackingTests.cs index b0c39ab04a..eb679804c1 100644 --- a/Ryujinx.Memory.Tests/TrackingTests.cs +++ b/Ryujinx.Memory.Tests/TrackingTests.cs @@ -44,7 +44,7 @@ namespace Ryujinx.Memory.Tests [Test] public void SingleRegion() { - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); (ulong address, ulong size)? readTrackingTriggered = null; handle.RegisterAction((address, size) => { @@ -97,7 +97,7 @@ namespace Ryujinx.Memory.Tests [Test] public void OverlappingRegions() { - RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16); + RegionHandle allHandle = _tracking.BeginTracking(0, PageSize * 16, 0); allHandle.Reprotect(); (ulong address, ulong size)? readTrackingTriggeredAll = null; @@ -116,7 +116,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < 16; i++) { - containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); + containedHandles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0); containedHandles[i].Reprotect(); } @@ -163,7 +163,7 @@ namespace Ryujinx.Memory.Tests ulong alignedEnd = ((address + size + PageSize - 1) / PageSize) * PageSize; ulong alignedSize = alignedEnd - alignedStart; - RegionHandle handle = _tracking.BeginTracking(address, size); + RegionHandle handle = _tracking.BeginTracking(address, size, 0); // Anywhere inside the pages the region is contained on should trigger. @@ -207,7 +207,7 @@ namespace Ryujinx.Memory.Tests for (int i = 0; i < handles.Length; i++) { - handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize); + handles[i] = _tracking.BeginTracking((ulong)i * PageSize, PageSize, 0); handles[i].Reprotect(); } @@ -263,7 +263,7 @@ namespace Ryujinx.Memory.Tests Random random = new Random(randSeed + 512); while (Stopwatch.GetTimestamp() < finishedTime) { - RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536)); + RegionHandle handle = _tracking.BeginTracking((ulong)random.Next(maxAddress), (ulong)random.Next(65536), 0); handle.Dispose(); @@ -295,7 +295,7 @@ namespace Ryujinx.Memory.Tests // Read actions should only be triggered once for each registration. // The implementation should use an interlocked exchange to make sure other threads can't get the action. - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); int triggeredCount = 0; int registeredCount = 0; @@ -359,7 +359,7 @@ namespace Ryujinx.Memory.Tests { // Ensure that disposed handles correctly remove their virtual and physical regions. - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); handle.Reprotect(); Assert.AreEqual(1, _tracking.GetRegionCount()); @@ -372,8 +372,8 @@ namespace Ryujinx.Memory.Tests // We expect there to be three regions after creating both, one for the small region and two covering the big one around it. // Regions are always split to avoid overlapping, which is why there are three instead of two. - RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize); - RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4); + RegionHandle handleSmall = _tracking.BeginTracking(PageSize, PageSize, 0); + RegionHandle handleBig = _tracking.BeginTracking(0, PageSize * 4, 0); Assert.AreEqual(3, _tracking.GetRegionCount()); @@ -398,7 +398,7 @@ namespace Ryujinx.Memory.Tests protection = newProtection; }; - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); // After creating the handle, there is no protection yet. Assert.AreEqual(MemoryPermission.ReadAndWrite, protection); @@ -453,7 +453,7 @@ namespace Ryujinx.Memory.Tests [Test] public void PreciseAction() { - RegionHandle handle = _tracking.BeginTracking(0, PageSize); + RegionHandle handle = _tracking.BeginTracking(0, PageSize, 0); (ulong address, ulong size, bool write)? preciseTriggered = null; handle.RegisterPreciseAction((address, size, write) => diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs index b532ce5e08..ac89fca6d5 100644 --- a/Ryujinx.Memory/AddressSpaceManager.cs +++ b/Ryujinx.Memory/AddressSpaceManager.cs @@ -462,7 +462,7 @@ namespace Ryujinx.Memory } /// - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { // Only the ARM Memory Manager has tracking for now. } diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs index 390371ad23..e1851d48b8 100644 --- a/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -175,7 +175,8 @@ namespace Ryujinx.Memory /// Size of the region /// True if the region was written, false if read /// True if the access is precise, false otherwise - void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false); + /// Optional ID of the handles that should not be signalled + void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null); /// /// Reprotect a region of virtual memory for tracking. diff --git a/Ryujinx.Memory/Tracking/AbstractRegion.cs b/Ryujinx.Memory/Tracking/AbstractRegion.cs index a3c3990ea2..bd4c8ab5cc 100644 --- a/Ryujinx.Memory/Tracking/AbstractRegion.cs +++ b/Ryujinx.Memory/Tracking/AbstractRegion.cs @@ -50,7 +50,8 @@ namespace Ryujinx.Memory.Tracking /// Address accessed /// Size of the region affected in bytes /// Whether the region was written to or read - public abstract void Signal(ulong address, ulong size, bool write); + /// Optional ID of the handles that should not be signalled + public abstract void Signal(ulong address, ulong size, bool write, int? exemptId); /// /// Signals to the handles that a precise memory event has occurred. Assumes that the tracking lock has been obtained. @@ -58,10 +59,11 @@ namespace Ryujinx.Memory.Tracking /// Address accessed /// Size of the region affected in bytes /// Whether the region was written to or read - public abstract void SignalPrecise(ulong address, ulong size, bool write); + /// Optional ID of the handles that should not be signalled + public abstract void SignalPrecise(ulong address, ulong size, bool write, int? exemptId); /// - /// Split this region into two, around the specified address. + /// 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. /// /// Address to split the region around diff --git a/Ryujinx.Memory/Tracking/MemoryTracking.cs b/Ryujinx.Memory/Tracking/MemoryTracking.cs index 9a35cfb6cb..bf1e0ad34a 100644 --- a/Ryujinx.Memory/Tracking/MemoryTracking.cs +++ b/Ryujinx.Memory/Tracking/MemoryTracking.cs @@ -136,10 +136,11 @@ namespace Ryujinx.Memory.Tracking /// Size of the region /// Handles to inherit state from or reuse. When none are present, provide null /// Desired granularity of write tracking + /// Handle ID /// The memory tracking handle - public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity) + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id) { - return new MultiRegionHandle(this, address, size, handles, granularity); + return new MultiRegionHandle(this, address, size, handles, granularity, id); } /// @@ -148,12 +149,13 @@ namespace Ryujinx.Memory.Tracking /// CPU virtual address of the region /// Size of the region /// Desired granularity of write tracking + /// Handle ID /// The memory tracking handle - public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity) + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) { (address, size) = PageAlign(address, size); - return new SmartMultiRegionHandle(this, address, size, granularity); + return new SmartMultiRegionHandle(this, address, size, granularity, id); } /// @@ -161,14 +163,16 @@ namespace Ryujinx.Memory.Tracking /// /// CPU virtual address of the region /// Size of the region + /// Handle ID /// The memory tracking handle - public RegionHandle BeginTracking(ulong address, ulong size) + public RegionHandle BeginTracking(ulong address, ulong size, int id) { var (paAddress, paSize) = PageAlign(address, size); lock (TrackingLock) { - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, _memoryManager.IsRangeMapped(address, size)); + bool mapped = _memoryManager.IsRangeMapped(address, size); + RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, id, mapped); return handle; } @@ -181,28 +185,31 @@ namespace Ryujinx.Memory.Tracking /// Size of the region /// The bitmap owning the dirty flag for this handle /// The bit of this handle within the dirty flag + /// Handle ID /// The memory tracking handle - internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit) + internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit, int id) { var (paAddress, paSize) = PageAlign(address, size); lock (TrackingLock) { - RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size)); + bool mapped = _memoryManager.IsRangeMapped(address, size); + RegionHandle handle = new RegionHandle(this, paAddress, paSize, address, size, bitmap, bit, id, mapped); return handle; } } /// - /// Signal that a virtual memory event happened at the given location (one byte). + /// Signal that a virtual memory event happened at the given location. /// /// Virtual address accessed - /// Whether the address was written to or read + /// Size of the region affected in bytes + /// Whether the region was written to or read /// True if the event triggered any tracking regions, false otherwise - public bool VirtualMemoryEventTracking(ulong address, bool write) + public bool VirtualMemoryEvent(ulong address, ulong size, bool write) { - return VirtualMemoryEvent(address, 1, write); + return VirtualMemoryEvent(address, size, write, precise: false, null); } /// @@ -214,8 +221,9 @@ namespace Ryujinx.Memory.Tracking /// Size of the region affected in bytes /// Whether the region was written to or read /// True if the access is precise, false otherwise + /// Optional ID that of the handles that should not be signalled /// True if the event triggered any tracking regions, false otherwise - public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise = false) + public bool VirtualMemoryEvent(ulong address, ulong size, bool write, bool precise, int? exemptId = null) { // Look up the virtual region using the region list. // Signal up the chain to relevant handles. @@ -250,11 +258,11 @@ namespace Ryujinx.Memory.Tracking if (precise) { - region.SignalPrecise(address, size, write); + region.SignalPrecise(address, size, write, exemptId); } else { - region.Signal(address, size, write); + region.Signal(address, size, write, exemptId); } } } diff --git a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs index 6ea2b7845e..68fc5e7592 100644 --- a/Ryujinx.Memory/Tracking/MultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/MultiRegionHandle.cs @@ -30,7 +30,13 @@ namespace Ryujinx.Memory.Tracking public bool Dirty { get; private set; } = true; - internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable handles, ulong granularity) + internal MultiRegionHandle( + MemoryTracking tracking, + ulong address, + ulong size, + IEnumerable handles, + ulong granularity, + int id) { _handles = new RegionHandle[(size + granularity - 1) / granularity]; Granularity = granularity; @@ -55,7 +61,7 @@ namespace Ryujinx.Memory.Tracking // Fill any gap left before this handle. while (i < startIndex) { - RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); fillHandle.Parent = this; _handles[i++] = fillHandle; } @@ -76,7 +82,7 @@ namespace Ryujinx.Memory.Tracking while (i < endIndex) { - RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); splitHandle.Parent = this; splitHandle.Reprotect(handle.Dirty); @@ -99,7 +105,7 @@ namespace Ryujinx.Memory.Tracking // Fill any remaining space with new handles. while (i < _handles.Length) { - RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i); + RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i, id); handle.Parent = this; _handles[i++] = handle; } diff --git a/Ryujinx.Memory/Tracking/RegionHandle.cs b/Ryujinx.Memory/Tracking/RegionHandle.cs index 580f94a517..7a59f9f25b 100644 --- a/Ryujinx.Memory/Tracking/RegionHandle.cs +++ b/Ryujinx.Memory/Tracking/RegionHandle.cs @@ -15,12 +15,12 @@ namespace Ryujinx.Memory.Tracking /// If more than this number of checks have been performed on a dirty flag since its last reprotect, /// then it is dirtied infrequently. /// - private static int CheckCountForInfrequent = 3; + private const int CheckCountForInfrequent = 3; /// /// Number of frequent dirty/consume in a row to make this handle volatile. /// - private static int VolatileThreshold = 5; + private const int VolatileThreshold = 5; public bool Dirty { @@ -35,6 +35,7 @@ namespace Ryujinx.Memory.Tracking } internal int SequenceNumber { get; set; } + internal int Id { get; } public bool Unmapped { get; private set; } @@ -97,14 +98,26 @@ namespace Ryujinx.Memory.Tracking /// The real, unaligned size of the handle /// The bitmap the dirty flag for this handle is stored in /// The bit index representing the dirty flag for this handle + /// Handle ID /// True if the region handle starts mapped - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, ConcurrentBitmap bitmap, int bit, bool mapped = true) + internal RegionHandle( + MemoryTracking tracking, + ulong address, + ulong size, + ulong realAddress, + ulong realSize, + ConcurrentBitmap bitmap, + int bit, + int id, + bool mapped = true) { Bitmap = bitmap; DirtyBit = bit; Dirty = mapped; + Id = id; + Unmapped = !mapped; Address = address; Size = size; @@ -131,11 +144,14 @@ namespace Ryujinx.Memory.Tracking /// Size of the region to track /// The real, unaligned address of the handle /// The real, unaligned size of the handle + /// Handle ID /// True if the region handle starts mapped - internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, bool mapped = true) + internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong realAddress, ulong realSize, int id, bool mapped = true) { Bitmap = new ConcurrentBitmap(1, mapped); + Id = id; + Unmapped = !mapped; Address = address; diff --git a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs index 47fe72e5bc..4acddefaf7 100644 --- a/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs +++ b/Ryujinx.Memory/Tracking/SmartMultiRegionHandle.cs @@ -18,10 +18,11 @@ namespace Ryujinx.Memory.Tracking private readonly ulong _granularity; private readonly ulong _size; private MemoryTracking _tracking; + private readonly int _id; public bool Dirty { get; private set; } = true; - internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity) + internal SmartMultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, ulong granularity, int id) { // For this multi-region handle, the handle list starts empty. // As regions are queried, they are added to the _handles array at their start index. @@ -34,6 +35,7 @@ namespace Ryujinx.Memory.Tracking _address = address; _size = size; + _id = id; } public void SignalWrite() @@ -102,7 +104,7 @@ namespace Ryujinx.Memory.Tracking RegionSignal signal = handle.PreAction; handle.Dispose(); - RegionHandle splitLow = _tracking.BeginTracking(address, size); + RegionHandle splitLow = _tracking.BeginTracking(address, size, _id); splitLow.Parent = this; if (signal != null) { @@ -110,7 +112,7 @@ namespace Ryujinx.Memory.Tracking } _handles[handleIndex] = splitLow; - RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size); + RegionHandle splitHigh = _tracking.BeginTracking(address + size, handle.Size - size, _id); splitHigh.Parent = this; if (signal != null) { @@ -145,7 +147,7 @@ namespace Ryujinx.Memory.Tracking if (handle != null) { // Fill up to the found handle. - handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle)); + handle = _tracking.BeginTracking(startAddress, HandlesToBytes(i - startHandle), _id); handle.Parent = this; _handles[startHandle] = handle; return; @@ -153,7 +155,7 @@ namespace Ryujinx.Memory.Tracking } // Can fill the whole range. - _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle)); + _handles[startHandle] = _tracking.BeginTracking(startAddress, HandlesToBytes(1 + lastHandle - startHandle), _id); _handles[startHandle].Parent = this; } diff --git a/Ryujinx.Memory/Tracking/VirtualRegion.cs b/Ryujinx.Memory/Tracking/VirtualRegion.cs index 57a0344ac8..9651426b33 100644 --- a/Ryujinx.Memory/Tracking/VirtualRegion.cs +++ b/Ryujinx.Memory/Tracking/VirtualRegion.cs @@ -19,19 +19,24 @@ namespace Ryujinx.Memory.Tracking _tracking = tracking; } - public override void Signal(ulong address, ulong size, bool write) + /// + public override void Signal(ulong address, ulong size, bool write, int? exemptId) { IList handles = Handles; for (int i = 0; i < handles.Count; i++) { - handles[i].Signal(address, size, write, ref handles); + if (exemptId == null || handles[i].Id != exemptId.Value) + { + handles[i].Signal(address, size, write, ref handles); + } } UpdateProtection(); } - public override void SignalPrecise(ulong address, ulong size, bool write) + /// + public override void SignalPrecise(ulong address, ulong size, bool write, int? exemptId) { IList handles = Handles; @@ -39,7 +44,10 @@ namespace Ryujinx.Memory.Tracking for (int i = 0; i < handles.Count; i++) { - allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); + if (exemptId == null || handles[i].Id != exemptId.Value) + { + allPrecise &= handles[i].SignalPrecise(address, size, write, ref handles); + } } // Only update protection if a regular signal handler was called. diff --git a/Ryujinx.Tests/Memory/MockMemoryManager.cs b/Ryujinx.Tests/Memory/MockMemoryManager.cs index 3f7692636a..eeecf419f7 100644 --- a/Ryujinx.Tests/Memory/MockMemoryManager.cs +++ b/Ryujinx.Tests/Memory/MockMemoryManager.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Tests.Memory throw new NotImplementedException(); } - public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false) + public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) { throw new NotImplementedException(); }