aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Graphics.Gpu
diff options
context:
space:
mode:
Diffstat (limited to 'Ryujinx.Graphics.Gpu')
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Compute.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs56
-rw-r--r--Ryujinx.Graphics.Gpu/Engine/Methods.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/GpuContext.cs4
-rw-r--r--Ryujinx.Graphics.Gpu/Image/Pool.cs2
-rw-r--r--Ryujinx.Graphics.Gpu/Image/TextureGroup.cs3
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/Buffer.cs83
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs43
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/BufferManager.cs29
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs8
-rw-r--r--Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs24
13 files changed, 241 insertions, 19 deletions
diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs
index bcff5953..be317a7f 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs
@@ -16,6 +16,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param>
public void Dispatch(GpuState state, int argument)
{
+ FlushUboDirty();
+
uint qmdAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress);
var qmd = _context.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
index dd16cb2d..a1cf86ec 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
@@ -69,6 +69,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
return;
}
+ FlushUboDirty();
+
if (copy2D)
{
// Buffer to texture copy.
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
index 3fee1fcf..16fb31d6 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
@@ -66,6 +66,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
int index = (argument >> 4) & 0x1f;
+ FlushUboDirty();
+
if (enable)
{
var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
index 61772327..3e1dd151 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
@@ -1,3 +1,4 @@
+using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
using System;
using System.Runtime.InteropServices;
@@ -6,6 +7,25 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
partial class Methods
{
+ // State associated with direct uniform buffer updates.
+ // This state is used to attempt to batch together consecutive updates.
+ private ulong _ubBeginCpuAddress = 0;
+ private ulong _ubFollowUpAddress = 0;
+ private ulong _ubByteCount = 0;
+
+ /// <summary>
+ /// Flushes any queued ubo updates.
+ /// </summary>
+ private void FlushUboDirty()
+ {
+ if (_ubFollowUpAddress != 0)
+ {
+ BufferManager.ForceDirty(_ubFollowUpAddress - _ubByteCount, _ubByteCount);
+
+ _ubFollowUpAddress = 0;
+ }
+ }
+
/// <summary>
/// Updates the uniform buffer data with inline data.
/// </summary>
@@ -15,11 +35,22 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
- _context.MemoryManager.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument);
+ ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
- state.SetUniformBufferOffset(uniformBuffer.Offset + 4);
+ if (_ubFollowUpAddress != address)
+ {
+ FlushUboDirty();
+
+ _ubByteCount = 0;
+ _ubBeginCpuAddress = _context.MemoryManager.Translate(address);
+ }
- _context.AdvanceSequence();
+ _context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1)));
+
+ _ubFollowUpAddress = address + 4;
+ _ubByteCount += 4;
+
+ state.SetUniformBufferOffset(uniformBuffer.Offset + 4);
}
/// <summary>
@@ -31,11 +62,24 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
- _context.MemoryManager.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, MemoryMarshal.Cast<int, byte>(data));
+ ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
- state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4);
+ ulong size = (ulong)data.Length * 4;
+
+ if (_ubFollowUpAddress != address)
+ {
+ FlushUboDirty();
- _context.AdvanceSequence();
+ _ubByteCount = 0;
+ _ubBeginCpuAddress = _context.MemoryManager.Translate(address);
+ }
+
+ _context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(data));
+
+ _ubFollowUpAddress = address + size;
+ _ubByteCount += size;
+
+ state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index ae9bdb0d..431ea449 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -130,6 +130,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
_prevTfEnable = false;
}
+ FlushUboDirty();
+
// Shaders must be the first one to be updated if modified, because
// some of the other state depends on information from the currently
// bound shaders.
diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs
index f131ecc3..a9386ce5 100644
--- a/Ryujinx.Graphics.Gpu/GpuContext.cs
+++ b/Ryujinx.Graphics.Gpu/GpuContext.cs
@@ -137,7 +137,7 @@ namespace Ryujinx.Graphics.Gpu
/// This is required for any GPU memory access.
/// </summary>
/// <param name="cpuMemory">CPU memory manager</param>
- public void SetVmm(Cpu.MemoryManager cpuMemory)
+ public void SetVmm(Cpu.IVirtualMemoryManagerTracked cpuMemory)
{
PhysicalMemory = new PhysicalMemory(cpuMemory);
}
@@ -187,6 +187,8 @@ namespace Ryujinx.Graphics.Gpu
Renderer.Dispose();
GPFifo.Dispose();
HostInitalized.Dispose();
+
+ PhysicalMemory.Dispose();
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs
index 855f6344..0b4c2993 100644
--- a/Ryujinx.Graphics.Gpu/Image/Pool.cs
+++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs
@@ -1,6 +1,4 @@
-using Ryujinx.Common;
using Ryujinx.Cpu.Tracking;
-using Ryujinx.Graphics.Gpu.Memory;
using System;
namespace Ryujinx.Graphics.Gpu.Image
diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
index 52129d64..30ca59d4 100644
--- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
+++ b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
@@ -1,5 +1,4 @@
-using Ryujinx.Common;
-using Ryujinx.Cpu.Tracking;
+using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range;
diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
index cdd61b6d..c567e30c 100644
--- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
@@ -36,6 +36,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
public ulong EndAddress => Address + Size;
/// <summary>
+ /// Increments when the buffer is (partially) unmapped or disposed.
+ /// </summary>
+ public int UnmappedSequence { get; private set; }
+
+ /// <summary>
/// Ranges of the buffer that have been modified on the GPU.
/// Ranges defined here cannot be updated from CPU until a CPU waiting sync point is reached.
/// Then, write tracking will signal, wait for GPU sync (generated at the syncpoint) and flush these regions.
@@ -45,9 +50,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </remarks>
private BufferModifiedRangeList _modifiedRanges = null;
- private CpuMultiRegionHandle _memoryTrackingGranular;
-
- private CpuRegionHandle _memoryTracking;
+ private readonly CpuMultiRegionHandle _memoryTrackingGranular;
+ private readonly CpuRegionHandle _memoryTracking;
private readonly RegionSignal _externalFlushDelegate;
private readonly Action<ulong, ulong> _loadDelegate;
@@ -131,6 +135,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Checks if a given range is fully contained in the buffer.
+ /// </summary>
+ /// <param name="address">Start address of the range</param>
+ /// <param name="size">Size in bytes of the range</param>
+ /// <returns>True if the range is contained, false otherwise</returns>
+ public bool FullyContains(ulong address, ulong size)
+ {
+ return address >= Address && address + size <= EndAddress;
+ }
+
+ /// <summary>
/// Performs guest to host memory synchronization of the buffer data.
/// </summary>
/// <remarks>
@@ -147,7 +162,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
- if (_memoryTracking.Dirty && _context.SequenceNumber != _sequenceNumber)
+ if (_context.SequenceNumber != _sequenceNumber && _memoryTracking.DirtyOrVolatile())
{
_memoryTracking.Reprotect();
@@ -166,6 +181,39 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Performs guest to host memory synchronization of the buffer data, regardless of sequence number.
+ /// </summary>
+ /// <remarks>
+ /// This causes the buffer data to be overwritten if a write was detected from the CPU,
+ /// since the last call to this method.
+ /// </remarks>
+ /// <param name="address">Start address of the range to synchronize</param>
+ /// <param name="size">Size in bytes of the range to synchronize</param>
+ public void ForceSynchronizeMemory(ulong address, ulong size)
+ {
+ if (_useGranular)
+ {
+ _memoryTrackingGranular.QueryModified(address, size, _modifiedDelegate);
+ }
+ else
+ {
+ if (_memoryTracking.DirtyOrVolatile())
+ {
+ _memoryTracking.Reprotect();
+
+ if (_modifiedRanges != null)
+ {
+ _modifiedRanges.ExcludeModifiedRegions(Address, Size, _loadDelegate);
+ }
+ else
+ {
+ _context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
+ }
+ }
+ }
+ }
+
+ /// <summary>
/// Ensure that the modified range list exists.
/// </summary>
private void EnsureRangeList()
@@ -317,6 +365,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Force a region of the buffer to be dirty. Avoids reprotection and nullifies sequence number check.
+ /// </summary>
+ /// <param name="mAddress">Start address of the modified region</param>
+ /// <param name="mSize">Size of the region to force dirty</param>
+ public void ForceDirty(ulong mAddress, ulong mSize)
+ {
+ if (_modifiedRanges != null)
+ {
+ _modifiedRanges.Clear(mAddress, mSize);
+ }
+
+ if (_useGranular)
+ {
+ _memoryTrackingGranular.ForceDirty(mAddress, mSize);
+ }
+ else
+ {
+ _memoryTracking.ForceDirty();
+ _sequenceNumber--;
+ }
+ }
+
+ /// <summary>
/// Performs copy of all the buffer data from one buffer to another.
/// </summary>
/// <param name="destination">The destination buffer to copy the data into</param>
@@ -385,6 +456,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
public void Unmapped(ulong address, ulong size)
{
_modifiedRanges?.Clear(address, size);
+
+ UnmappedSequence++;
}
/// <summary>
@@ -398,6 +471,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_memoryTracking?.Dispose();
_context.Renderer.DeleteBuffer(Handle);
+
+ UnmappedSequence++;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs
new file mode 100644
index 00000000..fa38b54e
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferCacheEntry.cs
@@ -0,0 +1,43 @@
+namespace Ryujinx.Graphics.Gpu.Memory
+{
+ /// <summary>
+ /// A cached entry for easily locating a buffer that is used often internally.
+ /// </summary>
+ class BufferCacheEntry
+ {
+ /// <summary>
+ /// The CPU VA of the buffer destination.
+ /// </summary>
+ public ulong Address;
+
+ /// <summary>
+ /// The end GPU VA of the associated buffer, used to check if new data can fit.
+ /// </summary>
+ public ulong EndGpuAddress;
+
+ /// <summary>
+ /// The buffer associated with this cache entry.
+ /// </summary>
+ public Buffer Buffer;
+
+ /// <summary>
+ /// The UnmappedSequence of the buffer at the time of creation.
+ /// If this differs from the value currently in the buffer, then this cache entry is outdated.
+ /// </summary>
+ public int UnmappedSequence;
+
+ /// <summary>
+ /// Create a new cache entry.
+ /// </summary>
+ /// <param name="address">The CPU VA of the buffer destination</param>
+ /// <param name="gpuVa">The GPU VA of the buffer destination</param>
+ /// <param name="buffer">The buffer object containing the target buffer</param>
+ public BufferCacheEntry(ulong address, ulong gpuVa, Buffer buffer)
+ {
+ Address = address;
+ EndGpuAddress = gpuVa + (buffer.EndAddress - address);
+ Buffer = buffer;
+ UnmappedSequence = buffer.UnmappedSequence;
+ }
+ }
+}
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index b2cd1ced..4a794b19 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -114,6 +114,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private bool _rebind;
+ private Dictionary<ulong, BufferCacheEntry> _dirtyCache;
+
/// <summary>
/// Creates a new instance of the buffer manager.
/// </summary>
@@ -143,6 +145,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
_bufferTextures = new List<BufferTextureBinding>();
+
+ _dirtyCache = new Dictionary<ulong, BufferCacheEntry>();
}
/// <summary>
@@ -467,6 +471,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Performs address translation of the GPU virtual address, and attempts to force
+ /// the buffer in the region as dirty.
+ /// The buffer lookup for this function is cached in a dictionary for quick access, which
+ /// accelerates common UBO updates.
+ /// </summary>
+ /// <param name="gpuVa">Start GPU virtual address of the buffer</param>
+ /// <param name="size">Size in bytes of the buffer</param>
+ public void ForceDirty(ulong gpuVa, ulong size)
+ {
+ BufferCacheEntry result;
+
+ if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence)
+ {
+ ulong address = TranslateAndCreateBuffer(gpuVa, size);
+ result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
+
+ _dirtyCache[gpuVa] = result;
+ }
+
+ result.Buffer.ForceDirty(result.Address, size);
+ }
+
+ /// <summary>
/// Creates a new buffer for the specified range, if needed.
/// If a buffer where this range can be fully contained already exists,
/// then the creation of a new buffer is not necessary.
@@ -520,7 +547,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int dstOffset = (int)(buffer.Address - newBuffer.Address);
- buffer.SynchronizeMemory(buffer.Address, buffer.Size);
+ buffer.ForceSynchronizeMemory(buffer.Address, buffer.Size);
buffer.CopyTo(newBuffer, dstOffset);
newBuffer.InheritModifiedRanges(buffer);
diff --git a/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs b/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs
index 92099b6a..8a9c6767 100644
--- a/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/GpuRegionHandle.cs
@@ -56,5 +56,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
regionHandle.Reprotect(asDirty);
}
}
+
+ public void ForceDirty()
+ {
+ foreach (var regionHandle in _cpuRegionHandles)
+ {
+ regionHandle.ForceDirty();
+ }
+ }
}
}
diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
index 8b2401c7..3d2af532 100644
--- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
@@ -12,19 +12,24 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Represents physical memory, accessible from the GPU.
/// This is actually working CPU virtual addresses, of memory mapped on the application process.
/// </summary>
- class PhysicalMemory
+ class PhysicalMemory : IDisposable
{
public const int PageSize = 0x1000;
- private readonly Cpu.MemoryManager _cpuMemory;
+ private IVirtualMemoryManagerTracked _cpuMemory;
/// <summary>
/// Creates a new instance of the physical memory.
/// </summary>
/// <param name="cpuMemory">CPU memory manager of the application process</param>
- public PhysicalMemory(Cpu.MemoryManager cpuMemory)
+ public PhysicalMemory(IVirtualMemoryManagerTracked cpuMemory)
{
_cpuMemory = cpuMemory;
+
+ if (_cpuMemory is IRefCounted rc)
+ {
+ rc.IncrementReferenceCount();
+ }
}
/// <summary>
@@ -213,5 +218,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
}
+
+ /// <summary>
+ /// Release our reference to the CPU memory manager.
+ /// </summary>
+ public void Dispose()
+ {
+ if (_cpuMemory is IRefCounted rc)
+ {
+ rc.DecrementReferenceCount();
+
+ _cpuMemory = null;
+ }
+ }
}
} \ No newline at end of file