2019-10-12 23:02:07 -07:00
|
|
|
using Ryujinx.Graphics.GAL;
|
|
|
|
using System;
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Gpu.Memory
|
|
|
|
{
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
|
|
|
|
/// </summary>
|
2019-12-31 14:37:00 -08:00
|
|
|
class Buffer : IRange, IDisposable
|
2019-10-12 23:02:07 -07:00
|
|
|
{
|
2019-12-31 14:37:00 -08:00
|
|
|
private readonly GpuContext _context;
|
2019-10-12 23:02:07 -07:00
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
2020-05-23 02:46:09 -07:00
|
|
|
/// Host buffer handle.
|
2019-12-30 19:22:58 -08:00
|
|
|
/// </summary>
|
2020-05-23 02:46:09 -07:00
|
|
|
public BufferHandle Handle { get; }
|
2019-10-26 19:41:01 -07:00
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Start address of the buffer in guest memory.
|
|
|
|
/// </summary>
|
2019-10-12 23:02:07 -07:00
|
|
|
public ulong Address { get; }
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Size of the buffer in bytes.
|
|
|
|
/// </summary>
|
|
|
|
public ulong Size { get; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// End address of the buffer in guest memory.
|
|
|
|
/// </summary>
|
2019-10-12 23:02:07 -07:00
|
|
|
public ulong EndAddress => Address + Size;
|
|
|
|
|
2020-05-03 15:54:50 -07:00
|
|
|
private readonly (ulong, ulong)[] _modifiedRanges;
|
|
|
|
|
|
|
|
private readonly int[] _sequenceNumbers;
|
2019-10-12 23:02:07 -07:00
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Creates a new instance of the buffer.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="context">GPU context that the buffer belongs to</param>
|
|
|
|
/// <param name="address">Start address of the buffer</param>
|
|
|
|
/// <param name="size">Size of the buffer in bytes</param>
|
2019-10-12 23:02:07 -07:00
|
|
|
public Buffer(GpuContext context, ulong address, ulong size)
|
|
|
|
{
|
|
|
|
_context = context;
|
|
|
|
Address = address;
|
|
|
|
Size = size;
|
|
|
|
|
2020-05-23 02:46:09 -07:00
|
|
|
Handle = context.Renderer.CreateBuffer((int)size);
|
2019-10-12 23:02:07 -07:00
|
|
|
|
2020-05-03 15:54:50 -07:00
|
|
|
_modifiedRanges = new (ulong, ulong)[size / PhysicalMemory.PageSize];
|
|
|
|
|
2019-10-12 23:02:07 -07:00
|
|
|
_sequenceNumbers = new int[size / MemoryManager.PageSize];
|
|
|
|
}
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Gets a sub-range from the buffer.
|
|
|
|
/// </summary>
|
2020-01-01 07:39:09 -08:00
|
|
|
/// <remarks>
|
|
|
|
/// This can be used to bind and use sub-ranges of the buffer on the host API.
|
|
|
|
/// </remarks>
|
|
|
|
/// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
|
|
|
|
/// <returns>The buffer sub-range</returns>
|
2019-10-12 23:02:07 -07:00
|
|
|
public BufferRange GetRange(ulong address, ulong size)
|
|
|
|
{
|
|
|
|
int offset = (int)(address - Address);
|
|
|
|
|
2020-05-23 02:46:09 -07:00
|
|
|
return new BufferRange(Handle, offset, (int)size);
|
2019-10-12 23:02:07 -07:00
|
|
|
}
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Checks if a given range overlaps with 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 overlaps, false otherwise</returns>
|
2019-10-12 23:02:07 -07:00
|
|
|
public bool OverlapsWith(ulong address, ulong size)
|
|
|
|
{
|
|
|
|
return Address < address + size && address < EndAddress;
|
|
|
|
}
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Performs guest to host memory synchronization of the buffer data.
|
2020-01-01 07:39:09 -08:00
|
|
|
/// </summary>
|
|
|
|
/// <remarks>
|
2019-12-30 19:22:58 -08:00
|
|
|
/// This causes the buffer data to be overwritten if a write was detected from the CPU,
|
|
|
|
/// since the last call to this method.
|
2020-01-01 07:39:09 -08:00
|
|
|
/// </remarks>
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <param name="address">Start address of the range to synchronize</param>
|
|
|
|
/// <param name="size">Size in bytes of the range to synchronize</param>
|
2019-10-12 23:02:07 -07:00
|
|
|
public void SynchronizeMemory(ulong address, ulong size)
|
|
|
|
{
|
|
|
|
int currentSequenceNumber = _context.SequenceNumber;
|
|
|
|
|
|
|
|
bool needsSync = false;
|
|
|
|
|
|
|
|
ulong buffOffset = address - Address;
|
|
|
|
|
|
|
|
ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask;
|
|
|
|
|
|
|
|
int startIndex = (int)(buffOffset / MemoryManager.PageSize);
|
|
|
|
int endIndex = (int)(buffEndOffset / MemoryManager.PageSize);
|
|
|
|
|
|
|
|
for (int index = startIndex; index < endIndex; index++)
|
|
|
|
{
|
|
|
|
if (_sequenceNumbers[index] != currentSequenceNumber)
|
|
|
|
{
|
|
|
|
_sequenceNumbers[index] = currentSequenceNumber;
|
|
|
|
|
|
|
|
needsSync = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!needsSync)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-05-03 15:54:50 -07:00
|
|
|
int count = _context.PhysicalMemory.QueryModified(address, size, ResourceName.Buffer, _modifiedRanges);
|
2019-10-12 23:02:07 -07:00
|
|
|
|
2020-05-03 15:54:50 -07:00
|
|
|
for (int index = 0; index < count; index++)
|
2019-10-12 23:02:07 -07:00
|
|
|
{
|
2020-05-03 15:54:50 -07:00
|
|
|
(ulong mAddress, ulong mSize) = _modifiedRanges[index];
|
2019-10-12 23:02:07 -07:00
|
|
|
|
|
|
|
int offset = (int)(mAddress - Address);
|
|
|
|
|
2020-05-23 02:46:09 -07:00
|
|
|
_context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
|
2019-10-12 23:02:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <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>
|
|
|
|
/// <param name="dstOffset">The offset of the destination buffer to copy into</param>
|
2019-10-12 23:02:07 -07:00
|
|
|
public void CopyTo(Buffer destination, int dstOffset)
|
|
|
|
{
|
2020-05-23 02:46:09 -07:00
|
|
|
_context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
|
2019-10-12 23:02:07 -07:00
|
|
|
}
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <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>
|
2019-10-26 19:41:01 -07:00
|
|
|
public void Flush(ulong address, ulong size)
|
|
|
|
{
|
|
|
|
int offset = (int)(address - Address);
|
|
|
|
|
2020-05-23 02:46:09 -07:00
|
|
|
byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
2019-10-26 19:41:01 -07:00
|
|
|
|
2020-09-10 12:44:04 -07:00
|
|
|
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
|
|
|
_context.PhysicalMemory.WriteUntracked(address, data);
|
2019-10-26 19:41:01 -07:00
|
|
|
}
|
|
|
|
|
2019-12-30 19:22:58 -08:00
|
|
|
/// <summary>
|
|
|
|
/// Disposes the host buffer.
|
|
|
|
/// </summary>
|
2019-10-12 23:02:07 -07:00
|
|
|
public void Dispose()
|
|
|
|
{
|
2020-05-23 02:46:09 -07:00
|
|
|
_context.Renderer.DeleteBuffer(Handle);
|
2019-10-12 23:02:07 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|