diff options
| author | riperiperi <rhy3756547@hotmail.com> | 2021-06-24 00:31:26 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-06-24 01:31:26 +0200 |
| commit | 12a7a2ead812d46deb9d978b6758731157be1cbc (patch) | |
| tree | e2b5ca9c8ce9d7087c0eff4a5de8e2927f4bb9a1 /Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs | |
| parent | e053663f27132baec4a4d7c223894eb0322c6c03 (diff) | |
Inherit buffer tracking handles rather than recreating on resize (#2330)
This greatly speeds up games that constantly resize buffers, and removes stuttering on games that resize large buffers occasionally:
- Large improvement on Super Mario 3D All-Stars (#1663 needed for best performance)
- Improvement to Hyrule Warriors: AoC, and UE4 games. These games can still stutter due to texture creation/loading.
- Small improvement to other games, potential 1-frame stutters avoided.
`ForceSynchronizeMemory`, which was added with POWER, is no longer needed. Some tests have been added for the MultiRegionHandle.
Diffstat (limited to 'Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs')
| -rw-r--r-- | Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs | 122 |
1 files changed, 121 insertions, 1 deletions
diff --git a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs b/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs index 22e198c5..05756503 100644 --- a/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs +++ b/Ryujinx.Memory.Tests/MultiRegionTrackingTests.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Memory.Tests { return smart ? _tracking.BeginSmartGranularTracking(address, size, granularity) : - (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, granularity); + (IMultiRegionHandle)_tracking.BeginGranularTracking(address, size, null, granularity); } private void RandomOrder(Random random, List<int> indices, Action<int> action) @@ -279,5 +279,125 @@ namespace Ryujinx.Memory.Tests Assert.AreEqual(0, _tracking.GetRegionCount()); } + + [Test] + public void InheritHandles() + { + // Test merging the following into a granular region handle: + // - 3x gap (creates new granular handles) + // - 3x from multiregion: not dirty, dirty and with action + // - 2x gap + // - 3x single page: not dirty, dirty and with action + // - 3x two page: not dirty, dirty and with action (handle is not reused, but its state is copied to the granular handles) + // - 1x gap + // For a total of 18 pages. + + bool[] actionsTriggered = new bool[3]; + + MultiRegionHandle granular = _tracking.BeginGranularTracking(PageSize * 3, PageSize * 3, null, PageSize); + PreparePages(granular, 3, PageSize * 3); + + // Write to the second handle in the multiregion. + _tracking.VirtualMemoryEvent(PageSize * 4, PageSize, true); + + // Add an action to the third handle in the multiregion. + granular.RegisterAction(PageSize * 5, PageSize, (_, _) => { actionsTriggered[0] = true; }); + + RegionHandle[] singlePages = new RegionHandle[3]; + + for (int i = 0; i < 3; i++) + { + singlePages[i] = _tracking.BeginTracking(PageSize * (8 + (ulong)i), PageSize); + singlePages[i].Reprotect(); + } + + // Write to the second handle. + _tracking.VirtualMemoryEvent(PageSize * 9, PageSize, true); + + // Add an action to the third handle. + singlePages[2].RegisterAction((_, _) => { actionsTriggered[1] = true; }); + + RegionHandle[] doublePages = new RegionHandle[3]; + + for (int i = 0; i < 3; i++) + { + doublePages[i] = _tracking.BeginTracking(PageSize * (11 + (ulong)i * 2), PageSize * 2); + doublePages[i].Reprotect(); + } + + // Write to the second handle. + _tracking.VirtualMemoryEvent(PageSize * 13, PageSize * 2, true); + + // Add an action to the third handle. + doublePages[2].RegisterAction((_, _) => { actionsTriggered[2] = true; }); + + // Finally, create a granular handle that inherits all these handles. + + IEnumerable<IRegionHandle>[] handleGroups = new IEnumerable<IRegionHandle>[] + { + granular.GetHandles(), + singlePages, + doublePages + }; + + MultiRegionHandle combined = _tracking.BeginGranularTracking(0, PageSize * 18, handleGroups.SelectMany((handles) => handles), PageSize); + + bool[] expectedDirty = new bool[] + { + true, true, true, // Gap. + false, true, false, // Multi-region. + true, true, // Gap. + false, true, false, // Individual handles. + false, false, true, true, false, false, // Double size handles. + true // Gap. + }; + + for (int i = 0; i < 18; i++) + { + bool modified = false; + combined.QueryModified(PageSize * (ulong)i, PageSize, (_, _) => { modified = true; }); + + Assert.AreEqual(expectedDirty[i], modified); + } + + Assert.AreEqual(new bool[3], actionsTriggered); + + _tracking.VirtualMemoryEvent(PageSize * 5, PageSize, false); + Assert.IsTrue(actionsTriggered[0]); + + _tracking.VirtualMemoryEvent(PageSize * 10, PageSize, false); + Assert.IsTrue(actionsTriggered[1]); + + _tracking.VirtualMemoryEvent(PageSize * 15, PageSize, false); + Assert.IsTrue(actionsTriggered[2]); + + // The double page handles should be disposed, as they were split into granular handles. + foreach (RegionHandle doublePage in doublePages) + { + // These should have been disposed. + bool throws = false; + + try + { + doublePage.Dispose(); + } + catch (ObjectDisposedException) + { + throws = true; + } + + Assert.IsTrue(throws); + } + + IEnumerable<IRegionHandle> combinedHandles = combined.GetHandles(); + + Assert.AreEqual(handleGroups[0].ElementAt(0), combinedHandles.ElementAt(3)); + Assert.AreEqual(handleGroups[0].ElementAt(1), combinedHandles.ElementAt(4)); + Assert.AreEqual(handleGroups[0].ElementAt(2), combinedHandles.ElementAt(5)); + + Assert.AreEqual(singlePages[0], combinedHandles.ElementAt(8)); + Assert.AreEqual(singlePages[1], combinedHandles.ElementAt(9)); + Assert.AreEqual(singlePages[2], combinedHandles.ElementAt(10)); + } } } |
