aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorriperiperi <rhy3756547@hotmail.com>2022-11-24 14:50:15 +0000
committerGitHub <noreply@github.com>2022-11-24 15:50:15 +0100
commit65778a6b78ab8bde4090478482227e40c551db4d (patch)
tree3f59d3060b635a0cd9f9d87057ef12c00731f2ee
parentf4e879a1e6ad810aa38c1c020467a2589441871b (diff)
GPU: Don't trigger uploads for redundant buffer updates (#3828)1.1.382
* Initial implementation * Actually do The Thing * Add remark about performance to IVirtualMemoryManager
-rw-r--r--Ryujinx.Cpu/Jit/MemoryManager.cs31
-rw-r--r--Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs28
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs24
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs13
-rw-r--r--Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs5
-rw-r--r--Ryujinx.Memory/AddressSpaceManager.cs8
-rw-r--r--Ryujinx.Memory/IVirtualMemoryManager.cs11
7 files changed, 113 insertions, 7 deletions
diff --git a/Ryujinx.Cpu/Jit/MemoryManager.cs b/Ryujinx.Cpu/Jit/MemoryManager.cs
index 86c69431..21c50d51 100644
--- a/Ryujinx.Cpu/Jit/MemoryManager.cs
+++ b/Ryujinx.Cpu/Jit/MemoryManager.cs
@@ -180,6 +180,37 @@ namespace Ryujinx.Cpu.Jit
WriteImpl(va, data);
}
+ /// <inheritdoc/>
+ public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+ {
+ if (data.Length == 0)
+ {
+ return false;
+ }
+
+ SignalMemoryTracking(va, (ulong)data.Length, false);
+
+ if (IsContiguousAndMapped(va, data.Length))
+ {
+ var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length);
+
+ bool changed = !data.SequenceEqual(target);
+
+ if (changed)
+ {
+ data.CopyTo(target);
+ }
+
+ return changed;
+ }
+ else
+ {
+ WriteImpl(va, data);
+
+ return true;
+ }
+ }
+
/// <summary>
/// Writes data to CPU mapped memory.
/// </summary>
diff --git a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
index 8994e9c0..c4e59db9 100644
--- a/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
+++ b/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
@@ -308,6 +308,34 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
+ public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+ {
+ try
+ {
+ SignalMemoryTracking(va, (ulong)data.Length, false);
+
+ Span<byte> target = _addressSpaceMirror.GetSpan(va, data.Length);
+ bool changed = !data.SequenceEqual(target);
+
+ if (changed)
+ {
+ data.CopyTo(target);
+ }
+
+ return changed;
+ }
+ catch (InvalidMemoryRegionException)
+ {
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
+
+ return true;
+ }
+ }
+
+ /// <inheritdoc/>
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
{
if (tracked)
diff --git a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
index f4006ba9..5c936616 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs
@@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
class ConstantBufferUpdater
{
+ private const int UniformDataCacheSize = 512;
+
private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow<ThreedClassState> _state;
@@ -16,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ulong _ubBeginCpuAddress = 0;
private ulong _ubFollowUpAddress = 0;
private ulong _ubByteCount = 0;
+ private int _ubIndex = 0;
+ private int[] _ubData = new int[UniformDataCacheSize];
/// <summary>
/// Creates a new instance of the constant buffer updater.
@@ -108,9 +112,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_ubFollowUpAddress != 0)
{
var memoryManager = _channel.MemoryManager;
- memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
+
+ Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
+
+ if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
+ {
+ memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
+ }
_ubFollowUpAddress = 0;
+ _ubIndex = 0;
}
}
@@ -124,7 +135,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
- if (_ubFollowUpAddress != address)
+ if (_ubFollowUpAddress != address || _ubIndex == _ubData.Length)
{
FlushUboDirty();
@@ -132,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
}
- var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
- _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
+ _ubData[_ubIndex++] = argument;
_ubFollowUpAddress = address + 4;
_ubByteCount += 4;
@@ -153,7 +163,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ulong size = (ulong)data.Length * 4;
- if (_ubFollowUpAddress != address)
+ if (_ubFollowUpAddress != address || _ubIndex + data.Length > _ubData.Length)
{
FlushUboDirty();
@@ -161,8 +171,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
}
- var byteData = MemoryMarshal.Cast<int, byte>(data);
- _channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
+ data.CopyTo(_ubData.AsSpan(_ubIndex));
+ _ubIndex += data.Length;
_ubFollowUpAddress = address + size;
_ubByteCount += size;
diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
index 155cba0f..051838f1 100644
--- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
@@ -242,6 +242,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
WriteImpl(range, data, _cpuMemory.WriteUntracked);
}
+ /// <summary>
+ /// Writes data to the application process, returning false if the data was not changed.
+ /// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
+ /// </summary>
+ /// <remarks>The memory manager can return that memory has changed when it hasn't to avoid expensive data copies.</remarks>
+ /// <param name="address">Address to write into</param>
+ /// <param name="data">Data to be written</param>
+ /// <returns>True if the data was changed, false otherwise</returns>
+ public bool WriteWithRedundancyCheck(ulong address, ReadOnlySpan<byte> data)
+ {
+ return _cpuMemory.WriteWithRedundancyCheck(address, data);
+ }
+
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
/// <summary>
diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs
index 29922f89..6c442282 100644
--- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs
+++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs
@@ -44,6 +44,11 @@ namespace Ryujinx.Memory.Tests
throw new NotImplementedException();
}
+ public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+ {
+ throw new NotImplementedException();
+ }
+
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
{
throw new NotImplementedException();
diff --git a/Ryujinx.Memory/AddressSpaceManager.cs b/Ryujinx.Memory/AddressSpaceManager.cs
index 45f3225e..ffe880bf 100644
--- a/Ryujinx.Memory/AddressSpaceManager.cs
+++ b/Ryujinx.Memory/AddressSpaceManager.cs
@@ -137,6 +137,14 @@ namespace Ryujinx.Memory
}
/// <inheritdoc/>
+ public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
+ {
+ Write(va, data);
+
+ return true;
+ }
+
+ /// <inheritdoc/>
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
{
if (size == 0)
diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs
index f97cb0b5..c8a74f66 100644
--- a/Ryujinx.Memory/IVirtualMemoryManager.cs
+++ b/Ryujinx.Memory/IVirtualMemoryManager.cs
@@ -58,6 +58,17 @@ namespace Ryujinx.Memory
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
void Write(ulong va, ReadOnlySpan<byte> data);
+ /// <summary>
+ /// Writes data to the application process, returning false if the data was not changed.
+ /// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
+ /// </summary>
+ /// <remarks>The memory manager can return that memory has changed when it hasn't to avoid expensive data copies.</remarks>
+ /// <param name="va">Virtual address to write the data into</param>
+ /// <param name="data">Data to be written</param>
+ /// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
+ /// <returns>True if the data was changed, false otherwise</returns>
+ bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data);
+
void Fill(ulong va, ulong size, byte value)
{
const int MaxChunkSize = 1 << 24;