aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs')
-rw-r--r--src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs174
1 files changed, 168 insertions, 6 deletions
diff --git a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
index d293060b..e060e0b4 100644
--- a/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
+++ b/src/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
@@ -10,6 +10,8 @@ using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory
{
+ delegate void BufferFlushAction(ulong address, ulong size, ulong syncNumber);
+
/// <summary>
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
/// </summary>
@@ -23,7 +25,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Host buffer handle.
/// </summary>
- public BufferHandle Handle { get; }
+ public BufferHandle Handle { get; private set; }
/// <summary>
/// Start address of the buffer in guest memory.
@@ -60,6 +62,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </remarks>
private BufferModifiedRangeList _modifiedRanges = null;
+ /// <summary>
+ /// A structure that is used to flush buffer data back to a host mapped buffer for cached readback.
+ /// Only used if the buffer data is explicitly owned by device local memory.
+ /// </summary>
+ private BufferPreFlush _preFlush = null;
+
+ /// <summary>
+ /// Usage tracking state that determines what type of backing the buffer should use.
+ /// </summary>
+ public BufferBackingState BackingState;
+
private readonly MultiRegionHandle _memoryTrackingGranular;
private readonly RegionHandle _memoryTracking;
@@ -87,6 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="physicalMemory">Physical memory where the buffer is mapped</param>
/// <param name="address">Start address of the buffer</param>
/// <param name="size">Size of the buffer in bytes</param>
+ /// <param name="stage">The type of usage that created the buffer</param>
/// <param name="sparseCompatible">Indicates if the buffer can be used in a sparse buffer mapping</param>
/// <param name="baseBuffers">Buffers which this buffer contains, and will inherit tracking handles from</param>
public Buffer(
@@ -94,6 +108,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
PhysicalMemory physicalMemory,
ulong address,
ulong size,
+ BufferStage stage,
bool sparseCompatible,
IEnumerable<Buffer> baseBuffers = null)
{
@@ -103,9 +118,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
Size = size;
SparseCompatible = sparseCompatible;
- BufferAccess access = sparseCompatible ? BufferAccess.SparseCompatible : BufferAccess.Default;
+ BackingState = new BufferBackingState(_context, this, stage, baseBuffers);
+
+ BufferAccess access = BackingState.SwitchAccess(this);
- Handle = context.Renderer.CreateBuffer((int)size, access, baseBuffers?.MaxBy(x => x.Size).Handle ?? BufferHandle.Null);
+ Handle = context.Renderer.CreateBuffer((int)size, access);
_useGranular = size > GranularBufferThreshold;
@@ -162,6 +179,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Recreates the backing buffer based on the desired access type
+ /// reported by the backing state struct.
+ /// </summary>
+ private void ChangeBacking()
+ {
+ BufferAccess access = BackingState.SwitchAccess(this);
+
+ BufferHandle newHandle = _context.Renderer.CreateBuffer((int)Size, access);
+
+ _context.Renderer.Pipeline.CopyBuffer(Handle, newHandle, 0, 0, (int)Size);
+
+ _modifiedRanges?.SelfMigration();
+
+ // If swtiching from device local to host mapped, pre-flushing data no longer makes sense.
+ // This is set to null and disposed when the migration fully completes.
+ _preFlush = null;
+
+ Handle = newHandle;
+
+ _physicalMemory.BufferCache.BufferBackingChanged(this);
+ }
+
+ /// <summary>
/// Gets a sub-range from the buffer, from a start address til a page boundary after the given size.
/// </summary>
/// <remarks>
@@ -246,6 +286,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
+ BackingState.RecordSet();
_context.Renderer.SetBufferData(Handle, 0, _physicalMemory.GetSpan(Address, (int)Size));
CopyToDependantVirtualBuffers();
}
@@ -284,14 +325,34 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Checks if a backing change is deemed necessary from the given usage.
+ /// If it is, queues a backing change to happen on the next sync action.
+ /// </summary>
+ /// <param name="stage">Buffer stage that can change backing type</param>
+ private void TryQueueBackingChange(BufferStage stage)
+ {
+ if (BackingState.ShouldChangeBacking(stage))
+ {
+ if (!_syncActionRegistered)
+ {
+ _context.RegisterSyncAction(this);
+ _syncActionRegistered = true;
+ }
+ }
+ }
+
+ /// <summary>
/// Signal that the given region of the buffer has been modified.
/// </summary>
/// <param name="address">The start address of the modified region</param>
/// <param name="size">The size of the modified region</param>
- public void SignalModified(ulong address, ulong size)
+ /// <param name="stage">Buffer stage that triggered the modification</param>
+ public void SignalModified(ulong address, ulong size, BufferStage stage)
{
EnsureRangeList();
+ TryQueueBackingChange(stage);
+
_modifiedRanges.SignalModified(address, size);
if (!_syncActionRegistered)
@@ -312,6 +373,37 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
/// <summary>
+ /// Action to be performed immediately before sync is created.
+ /// This will copy any buffer ranges designated for pre-flushing.
+ /// </summary>
+ /// <param name="syncpoint">True if the action is a guest syncpoint</param>
+ public void SyncPreAction(bool syncpoint)
+ {
+ if (_referenceCount == 0)
+ {
+ return;
+ }
+
+ if (BackingState.ShouldChangeBacking())
+ {
+ ChangeBacking();
+ }
+
+ if (BackingState.IsDeviceLocal)
+ {
+ _preFlush ??= new BufferPreFlush(_context, this, FlushImpl);
+
+ if (_preFlush.ShouldCopy)
+ {
+ _modifiedRanges?.GetRangesAtSync(Address, Size, _context.SyncNumber, (address, size) =>
+ {
+ _preFlush.CopyModified(address, size);
+ });
+ }
+ }
+ }
+
+ /// <summary>
/// Action to be performed when a syncpoint is reached after modification.
/// This will register read/write tracking to flush the buffer from GPU when its memory is used.
/// </summary>
@@ -466,6 +558,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="mSize">Size of the modified region</param>
private void LoadRegion(ulong mAddress, ulong mSize)
{
+ BackingState.RecordSet();
+
int offset = (int)(mAddress - Address);
_context.Renderer.SetBufferData(Handle, offset, _physicalMemory.GetSpan(mAddress, (int)mSize));
@@ -539,19 +633,85 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Flushes a range of the buffer.
/// This writes the range data back into guest memory.
/// </summary>
+ /// <param name="handle">Buffer handle to flush data from</param>
/// <param name="address">Start address of the range</param>
/// <param name="size">Size in bytes of the range</param>
- public void Flush(ulong address, ulong size)
+ private void FlushImpl(BufferHandle handle, ulong address, ulong size)
{
int offset = (int)(address - Address);
- using PinnedSpan<byte> data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
+ using PinnedSpan<byte> data = _context.Renderer.GetBufferData(handle, offset, (int)size);
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
_physicalMemory.WriteUntracked(address, CopyFromDependantVirtualBuffers(data.Get(), address, size));
}
/// <summary>
+ /// Flushes a range of the buffer.
+ /// This writes the range data back into guest memory.
+ /// </summary>
+ /// <param name="address">Start address of the range</param>
+ /// <param name="size">Size in bytes of the range</param>
+ private void FlushImpl(ulong address, ulong size)
+ {
+ FlushImpl(Handle, address, size);
+ }
+
+ /// <summary>
+ /// Flushes a range of the buffer from the most optimal source.
+ /// This writes the range data back into guest memory.
+ /// </summary>
+ /// <param name="address">Start address of the range</param>
+ /// <param name="size">Size in bytes of the range</param>
+ /// <param name="syncNumber">Sync number waited for before flushing the data</param>
+ public void Flush(ulong address, ulong size, ulong syncNumber)
+ {
+ BackingState.RecordFlush();
+
+ BufferPreFlush preFlush = _preFlush;
+
+ if (preFlush != null)
+ {
+ preFlush.FlushWithAction(address, size, syncNumber);
+ }
+ else
+ {
+ FlushImpl(address, size);
+ }
+ }
+ /// <summary>
+ /// Gets an action that disposes the backing buffer using its current handle.
+ /// Useful for deleting an old copy of the buffer after the handle changes.
+ /// </summary>
+ /// <returns>An action that flushes data from the specified range, using the buffer handle at the time the method is generated</returns>
+ public Action GetSnapshotDisposeAction()
+ {
+ BufferHandle handle = Handle;
+ BufferPreFlush preFlush = _preFlush;
+
+ return () =>
+ {
+ _context.Renderer.DeleteBuffer(handle);
+ preFlush?.Dispose();
+ };
+ }
+
+ /// <summary>
+ /// Gets an action that flushes a range of the buffer using its current handle.
+ /// Useful for flushing data from old copies of the buffer after the handle changes.
+ /// </summary>
+ /// <returns>An action that flushes data from the specified range, using the buffer handle at the time the method is generated</returns>
+ public BufferFlushAction GetSnapshotFlushAction()
+ {
+ BufferHandle handle = Handle;
+
+ return (ulong address, ulong size, ulong _) =>
+ {
+ FlushImpl(handle, address, size);
+ };
+ }
+
+ /// <summary>
/// Align a given address and size region to page boundaries.
/// </summary>
/// <param name="address">The start address of the region</param>
@@ -857,6 +1017,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
_modifiedRanges?.Clear();
_context.Renderer.DeleteBuffer(Handle);
+ _preFlush?.Dispose();
+ _preFlush = null;
UnmappedSequence++;
}