Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
518 changed files with 15170 additions and 12486 deletions
99
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
99
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
class Buffer : IRange<Buffer>, IDisposable
|
||||
{
|
||||
private GpuContext _context;
|
||||
|
||||
private IBuffer _buffer;
|
||||
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
|
||||
public ulong EndAddress => Address + Size;
|
||||
|
||||
private int[] _sequenceNumbers;
|
||||
|
||||
public Buffer(GpuContext context, ulong address, ulong size)
|
||||
{
|
||||
_context = context;
|
||||
Address = address;
|
||||
Size = size;
|
||||
|
||||
_buffer = context.Renderer.CreateBuffer((int)size);
|
||||
|
||||
_sequenceNumbers = new int[size / MemoryManager.PageSize];
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public BufferRange GetRange(ulong address, ulong size)
|
||||
{
|
||||
int offset = (int)(address - Address);
|
||||
|
||||
return new BufferRange(_buffer, offset, (int)size);
|
||||
}
|
||||
|
||||
public bool OverlapsWith(ulong address, ulong size)
|
||||
{
|
||||
return Address < address + size && address < EndAddress;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
(ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(address, size);
|
||||
|
||||
for (int index = 0; index < modifiedRanges.Length; index++)
|
||||
{
|
||||
(ulong mAddress, ulong mSize) = modifiedRanges[index];
|
||||
|
||||
int offset = (int)(mAddress - Address);
|
||||
|
||||
_buffer.SetData(offset, _context.PhysicalMemory.Read(mAddress, mSize));
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Buffer destination, int dstOffset)
|
||||
{
|
||||
_buffer.CopyTo(destination._buffer, 0, dstOffset, (int)Size);
|
||||
}
|
||||
|
||||
public void Invalidate()
|
||||
{
|
||||
_buffer.SetData(0, _context.PhysicalMemory.Read(Address, Size));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_buffer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
8
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
struct BufferBounds
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
}
|
||||
}
|
530
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
Normal file
530
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
Normal file
|
@ -0,0 +1,530 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.InputAssembler;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
class BufferManager
|
||||
{
|
||||
private const ulong BufferAlignmentSize = 0x1000;
|
||||
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private RangeList<Buffer> _buffers;
|
||||
|
||||
private IndexBuffer _indexBuffer;
|
||||
|
||||
private VertexBuffer[] _vertexBuffers;
|
||||
|
||||
private class BuffersPerStage
|
||||
{
|
||||
public uint EnableMask { get; set; }
|
||||
|
||||
public BufferBounds[] Buffers { get; }
|
||||
|
||||
public BuffersPerStage(int count)
|
||||
{
|
||||
Buffers = new BufferBounds[count];
|
||||
}
|
||||
|
||||
public void Bind(int index, ulong address, ulong size)
|
||||
{
|
||||
Buffers[index].Address = address;
|
||||
Buffers[index].Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private BuffersPerStage _cpStorageBuffers;
|
||||
private BuffersPerStage _cpUniformBuffers;
|
||||
private BuffersPerStage[] _gpStorageBuffers;
|
||||
private BuffersPerStage[] _gpUniformBuffers;
|
||||
|
||||
private bool _gpStorageBuffersDirty;
|
||||
private bool _gpUniformBuffersDirty;
|
||||
|
||||
private bool _indexBufferDirty;
|
||||
private bool _vertexBuffersDirty;
|
||||
|
||||
private bool _rebind;
|
||||
|
||||
public BufferManager(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_buffers = new RangeList<Buffer>();
|
||||
|
||||
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
||||
|
||||
_cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
|
||||
_cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
|
||||
|
||||
_gpStorageBuffers = new BuffersPerStage[Constants.TotalShaderStages];
|
||||
_gpUniformBuffers = new BuffersPerStage[Constants.TotalShaderStages];
|
||||
|
||||
for (int index = 0; index < Constants.TotalShaderStages; index++)
|
||||
{
|
||||
_gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers);
|
||||
_gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_indexBuffer.Address = address;
|
||||
_indexBuffer.Size = size;
|
||||
_indexBuffer.Type = type;
|
||||
|
||||
_indexBufferDirty = true;
|
||||
}
|
||||
|
||||
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_vertexBuffers[index].Address = address;
|
||||
_vertexBuffers[index].Size = size;
|
||||
_vertexBuffers[index].Stride = stride;
|
||||
_vertexBuffers[index].Divisor = divisor;
|
||||
|
||||
_vertexBuffersDirty = true;
|
||||
}
|
||||
|
||||
public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
// TODO: Improve
|
||||
size += gpuVa & 0x3fUL;
|
||||
|
||||
gpuVa &= ~0x3fUL;
|
||||
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_cpStorageBuffers.Bind(index, address, size);
|
||||
}
|
||||
|
||||
public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
// TODO: Improve
|
||||
size += gpuVa & 0x3fUL;
|
||||
|
||||
gpuVa &= ~0x3fUL;
|
||||
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_gpStorageBuffers[stage].Bind(index, address, size);
|
||||
|
||||
_gpStorageBuffersDirty = true;
|
||||
}
|
||||
|
||||
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_cpUniformBuffers.Bind(index, address, size);
|
||||
}
|
||||
|
||||
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_gpUniformBuffers[stage].Bind(index, address, size);
|
||||
|
||||
_gpUniformBuffersDirty = true;
|
||||
}
|
||||
|
||||
public void SetComputeStorageBufferEnableMask(uint mask)
|
||||
{
|
||||
_cpStorageBuffers.EnableMask = mask;
|
||||
}
|
||||
|
||||
public void SetGraphicsStorageBufferEnableMask(int stage, uint mask)
|
||||
{
|
||||
_gpStorageBuffers[stage].EnableMask = mask;
|
||||
|
||||
_gpStorageBuffersDirty = true;
|
||||
}
|
||||
|
||||
public void SetComputeUniformBufferEnableMask(uint mask)
|
||||
{
|
||||
_cpUniformBuffers.EnableMask = mask;
|
||||
}
|
||||
|
||||
public void SetGraphicsUniformBufferEnableMask(int stage, uint mask)
|
||||
{
|
||||
_gpUniformBuffers[stage].EnableMask = mask;
|
||||
|
||||
_gpUniformBuffersDirty = true;
|
||||
}
|
||||
|
||||
private ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
if (address == MemoryManager.BadAddress)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
ulong alignedAddress = address & ~BufferAlignmentMask;
|
||||
|
||||
ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask;
|
||||
|
||||
// The buffer must have the size of at least one page.
|
||||
if (alignedEndAddress == alignedAddress)
|
||||
{
|
||||
alignedEndAddress += BufferAlignmentSize;
|
||||
}
|
||||
|
||||
CreateBuffer(alignedAddress, alignedEndAddress - alignedAddress);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
private void CreateBuffer(ulong address, ulong size)
|
||||
{
|
||||
Buffer[] overlaps = _buffers.FindOverlaps(address, size);
|
||||
|
||||
if (overlaps.Length != 0)
|
||||
{
|
||||
// The buffer already exists. We can just return the existing buffer
|
||||
// if the buffer we need is fully contained inside the overlapping buffer.
|
||||
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
|
||||
// that fits all the data we need. We also need to copy the contents from the
|
||||
// old buffer(s) to the new buffer.
|
||||
ulong endAddress = address + size;
|
||||
|
||||
if (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress)
|
||||
{
|
||||
foreach (Buffer buffer in overlaps)
|
||||
{
|
||||
address = Math.Min(address, buffer.Address);
|
||||
endAddress = Math.Max(endAddress, buffer.EndAddress);
|
||||
|
||||
buffer.SynchronizeMemory(buffer.Address, buffer.Size);
|
||||
|
||||
_buffers.Remove(buffer);
|
||||
}
|
||||
|
||||
Buffer newBuffer = new Buffer(_context, address, endAddress - address);
|
||||
|
||||
_buffers.Add(newBuffer);
|
||||
|
||||
foreach (Buffer buffer in overlaps)
|
||||
{
|
||||
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
||||
|
||||
buffer.CopyTo(newBuffer, dstOffset);
|
||||
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
_rebind = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No overlap, just create a new buffer.
|
||||
Buffer buffer = new Buffer(_context, address, size);
|
||||
|
||||
_buffers.Add(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong GetComputeUniformBufferAddress(int index)
|
||||
{
|
||||
return _cpUniformBuffers.Buffers[index].Address;
|
||||
}
|
||||
|
||||
public ulong GetGraphicsUniformBufferAddress(int stage, int index)
|
||||
{
|
||||
return _gpUniformBuffers[stage].Buffers[index].Address;
|
||||
}
|
||||
|
||||
public void CommitComputeBindings()
|
||||
{
|
||||
uint enableMask = _cpStorageBuffers.EnableMask;
|
||||
|
||||
for (int index = 0; (enableMask >> index) != 0; index++)
|
||||
{
|
||||
if ((enableMask & (1u << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferBounds bounds = _cpStorageBuffers.Buffers[index];
|
||||
|
||||
if (bounds.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
|
||||
|
||||
_context.Renderer.ComputePipeline.SetStorageBuffer(index, buffer);
|
||||
}
|
||||
|
||||
enableMask = _cpUniformBuffers.EnableMask;
|
||||
|
||||
for (int index = 0; (enableMask >> index) != 0; index++)
|
||||
{
|
||||
if ((enableMask & (1u << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferBounds bounds = _cpUniformBuffers.Buffers[index];
|
||||
|
||||
if (bounds.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
|
||||
|
||||
_context.Renderer.ComputePipeline.SetUniformBuffer(index, buffer);
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
// TODO: Improve
|
||||
Span<byte> data = _context.PhysicalMemory.Read(bounds.Address + 0x310, 0x100);
|
||||
|
||||
Span<int> words = MemoryMarshal.Cast<byte, int>(data);
|
||||
|
||||
for (int offset = 0; offset < 0x40; offset += 4)
|
||||
{
|
||||
words[offset] &= 0x3f;
|
||||
}
|
||||
|
||||
buffer = GetBufferRange(bounds.Address + 0x310, 0x100);
|
||||
|
||||
buffer.Buffer.SetData(buffer.Offset, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CommitBindings()
|
||||
{
|
||||
if (_indexBufferDirty || _rebind)
|
||||
{
|
||||
_indexBufferDirty = false;
|
||||
|
||||
if (_indexBuffer.Address != 0)
|
||||
{
|
||||
BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindIndexBuffer(buffer, _indexBuffer.Type);
|
||||
}
|
||||
}
|
||||
else if (_indexBuffer.Address != 0)
|
||||
{
|
||||
SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
||||
}
|
||||
|
||||
if (_vertexBuffersDirty || _rebind)
|
||||
{
|
||||
_vertexBuffersDirty = false;
|
||||
|
||||
VertexBufferDescriptor[] vertexBuffers = new VertexBufferDescriptor[Constants.TotalVertexBuffers];
|
||||
|
||||
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
||||
{
|
||||
VertexBuffer vb = _vertexBuffers[index];
|
||||
|
||||
if (vb.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(vb.Address, vb.Size);
|
||||
|
||||
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindVertexBuffers(vertexBuffers);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
||||
{
|
||||
VertexBuffer vb = _vertexBuffers[index];
|
||||
|
||||
if (vb.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SynchronizeBufferRange(vb.Address, vb.Size);
|
||||
}
|
||||
}
|
||||
|
||||
if (_gpStorageBuffersDirty || _rebind)
|
||||
{
|
||||
_gpStorageBuffersDirty = false;
|
||||
|
||||
BindBuffers(_gpStorageBuffers, isStorage: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBuffers(_gpStorageBuffers);
|
||||
}
|
||||
|
||||
if (_gpUniformBuffersDirty || _rebind)
|
||||
{
|
||||
_gpUniformBuffersDirty = false;
|
||||
|
||||
BindBuffers(_gpUniformBuffers, isStorage: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBuffers(_gpUniformBuffers);
|
||||
}
|
||||
|
||||
_rebind = false;
|
||||
}
|
||||
|
||||
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
|
||||
{
|
||||
BindOrUpdateBuffers(bindings, bind: true, isStorage);
|
||||
}
|
||||
|
||||
private void UpdateBuffers(BuffersPerStage[] bindings)
|
||||
{
|
||||
BindOrUpdateBuffers(bindings, bind: false);
|
||||
}
|
||||
|
||||
private void BindOrUpdateBuffers(BuffersPerStage[] bindings, bool bind, bool isStorage = false)
|
||||
{
|
||||
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
||||
{
|
||||
uint enableMask = bindings[(int)stage - 1].EnableMask;
|
||||
|
||||
if (enableMask == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int index = 0; (enableMask >> index) != 0; index++)
|
||||
{
|
||||
if ((enableMask & (1u << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferBounds bounds = bindings[(int)stage - 1].Buffers[index];
|
||||
|
||||
if (bounds.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind)
|
||||
{
|
||||
BindBuffer(index, stage, bounds, isStorage);
|
||||
}
|
||||
else
|
||||
{
|
||||
SynchronizeBufferRange(bounds.Address, bounds.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage)
|
||||
{
|
||||
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
|
||||
|
||||
BufferRange[] buffers = new BufferRange[] { buffer };
|
||||
|
||||
if (isStorage)
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.BindStorageBuffers(index, stage, buffers);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.BindUniformBuffers(index, stage, buffers);
|
||||
}
|
||||
|
||||
if (!isStorage && index == 0)
|
||||
{
|
||||
// TODO: Improve
|
||||
Span<byte> data = _context.PhysicalMemory.Read(bounds.Address + 0x110, 0x100);
|
||||
|
||||
Span<int> words = MemoryMarshal.Cast<byte, int>(data);
|
||||
|
||||
for (int offset = 0; offset < 0x40; offset += 4)
|
||||
{
|
||||
words[offset] &= 0x3f;
|
||||
}
|
||||
|
||||
buffer = GetBufferRange(bounds.Address + 0x110, 0x100);
|
||||
|
||||
buffer.Buffer.SetData(buffer.Offset, data);
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
|
||||
{
|
||||
ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
|
||||
ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
|
||||
|
||||
BufferRange srcBuffer = GetBufferRange(srcAddress, size);
|
||||
BufferRange dstBuffer = GetBufferRange(dstAddress, size);
|
||||
|
||||
srcBuffer.Buffer.CopyTo(
|
||||
dstBuffer.Buffer,
|
||||
srcBuffer.Offset,
|
||||
dstBuffer.Offset,
|
||||
(int)size);
|
||||
}
|
||||
|
||||
private BufferRange GetBufferRange(ulong address, ulong size)
|
||||
{
|
||||
Buffer buffer;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
buffer = _buffers.FindFirstOverlap(address, size);
|
||||
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = _buffers.FindFirstOverlap(address, 1);
|
||||
}
|
||||
|
||||
return buffer.GetRange(address, size);
|
||||
}
|
||||
|
||||
private void SynchronizeBufferRange(ulong address, ulong size)
|
||||
{
|
||||
if (size != 0)
|
||||
{
|
||||
Buffer buffer = _buffers.FindFirstOverlap(address, size);
|
||||
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateRange(ulong address, ulong size)
|
||||
{
|
||||
Buffer[] overlappingBuffers = _buffers.FindOverlaps(address, size);
|
||||
|
||||
foreach (Buffer buffer in overlappingBuffers)
|
||||
{
|
||||
buffer.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs
Normal file
15
Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
public interface IPhysicalMemory
|
||||
{
|
||||
int GetPageSize();
|
||||
|
||||
Span<byte> Read(ulong address, ulong size);
|
||||
|
||||
void Write(ulong address, Span<byte> data);
|
||||
|
||||
(ulong, ulong)[] GetModifiedRanges(ulong address, ulong size);
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Gpu/Memory/IRange.cs
Normal file
10
Ryujinx.Graphics.Gpu/Memory/IRange.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
interface IRange<T>
|
||||
{
|
||||
ulong Address { get; }
|
||||
ulong Size { get; }
|
||||
|
||||
bool OverlapsWith(ulong address, ulong size);
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs
Normal file
12
Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
struct IndexBuffer
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
|
||||
public IndexType Type;
|
||||
}
|
||||
}
|
54
Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs
Normal file
54
Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
class MemoryAccessor
|
||||
{
|
||||
private GpuContext _context;
|
||||
|
||||
public MemoryAccessor(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public Span<byte> Read(ulong gpuVa, ulong maxSize)
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
ulong size = Math.Min(_context.MemoryManager.GetSubSize(gpuVa), maxSize);
|
||||
|
||||
return _context.PhysicalMemory.Read(processVa, size);
|
||||
}
|
||||
|
||||
public T Read<T>(ulong gpuVa) where T : struct
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
ulong size = (uint)Marshal.SizeOf<T>();
|
||||
|
||||
return MemoryMarshal.Cast<byte, T>(_context.PhysicalMemory.Read(processVa, size))[0];
|
||||
}
|
||||
|
||||
public int ReadInt32(ulong gpuVa)
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
return BitConverter.ToInt32(_context.PhysicalMemory.Read(processVa, 4));
|
||||
}
|
||||
|
||||
public void Write(ulong gpuVa, int value)
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
_context.PhysicalMemory.Write(processVa, BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
public void Write(ulong gpuVa, Span<byte> data)
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
_context.PhysicalMemory.Write(processVa, data);
|
||||
}
|
||||
}
|
||||
}
|
265
Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
Normal file
265
Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
Normal file
|
@ -0,0 +1,265 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
public class MemoryManager
|
||||
{
|
||||
private const ulong AddressSpaceSize = 1UL << 40;
|
||||
|
||||
public const ulong BadAddress = ulong.MaxValue;
|
||||
|
||||
private const int PtLvl0Bits = 14;
|
||||
private const int PtLvl1Bits = 14;
|
||||
private const int PtPageBits = 12;
|
||||
|
||||
private const ulong PtLvl0Size = 1UL << PtLvl0Bits;
|
||||
private const ulong PtLvl1Size = 1UL << PtLvl1Bits;
|
||||
public const ulong PageSize = 1UL << PtPageBits;
|
||||
|
||||
private const ulong PtLvl0Mask = PtLvl0Size - 1;
|
||||
private const ulong PtLvl1Mask = PtLvl1Size - 1;
|
||||
public const ulong PageMask = PageSize - 1;
|
||||
|
||||
private const int PtLvl0Bit = PtPageBits + PtLvl1Bits;
|
||||
private const int PtLvl1Bit = PtPageBits;
|
||||
|
||||
private const ulong PteUnmapped = 0xffffffff_ffffffff;
|
||||
private const ulong PteReserved = 0xffffffff_fffffffe;
|
||||
|
||||
private ulong[][] _pageTable;
|
||||
|
||||
public MemoryManager()
|
||||
{
|
||||
_pageTable = new ulong[PtLvl0Size][];
|
||||
}
|
||||
|
||||
public ulong Map(ulong pa, ulong va, ulong size)
|
||||
{
|
||||
lock (_pageTable)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
SetPte(va + offset, pa + offset);
|
||||
}
|
||||
}
|
||||
|
||||
return va;
|
||||
}
|
||||
|
||||
public ulong Map(ulong pa, ulong size)
|
||||
{
|
||||
lock (_pageTable)
|
||||
{
|
||||
ulong va = GetFreePosition(size);
|
||||
|
||||
if (va != PteUnmapped)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
SetPte(va + offset, pa + offset);
|
||||
}
|
||||
}
|
||||
|
||||
return va;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong MapLow(ulong pa, ulong size)
|
||||
{
|
||||
lock (_pageTable)
|
||||
{
|
||||
ulong va = GetFreePosition(size, 1, PageSize);
|
||||
|
||||
if (va != PteUnmapped && va <= uint.MaxValue && (va + size) <= uint.MaxValue)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
SetPte(va + offset, pa + offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
va = PteUnmapped;
|
||||
}
|
||||
|
||||
return va;
|
||||
}
|
||||
}
|
||||
|
||||
public ulong ReserveFixed(ulong va, ulong size)
|
||||
{
|
||||
lock (_pageTable)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
if (IsPageInUse(va + offset))
|
||||
{
|
||||
return PteUnmapped;
|
||||
}
|
||||
}
|
||||
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
SetPte(va + offset, PteReserved);
|
||||
}
|
||||
}
|
||||
|
||||
return va;
|
||||
}
|
||||
|
||||
public ulong Reserve(ulong size, ulong alignment)
|
||||
{
|
||||
lock (_pageTable)
|
||||
{
|
||||
ulong address = GetFreePosition(size, alignment);
|
||||
|
||||
if (address != PteUnmapped)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
SetPte(address + offset, PteReserved);
|
||||
}
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
}
|
||||
|
||||
public void Free(ulong va, ulong size)
|
||||
{
|
||||
lock (_pageTable)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
SetPte(va + offset, PteUnmapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ulong GetFreePosition(ulong size, ulong alignment = 1, ulong start = 1UL << 32)
|
||||
{
|
||||
// Note: Address 0 is not considered valid by the driver,
|
||||
// when 0 is returned it's considered a mapping error.
|
||||
ulong address = start;
|
||||
ulong freeSize = 0;
|
||||
|
||||
if (alignment == 0)
|
||||
{
|
||||
alignment = 1;
|
||||
}
|
||||
|
||||
alignment = (alignment + PageMask) & ~PageMask;
|
||||
|
||||
while (address + freeSize < AddressSpaceSize)
|
||||
{
|
||||
if (!IsPageInUse(address + freeSize))
|
||||
{
|
||||
freeSize += PageSize;
|
||||
|
||||
if (freeSize >= size)
|
||||
{
|
||||
return address;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
address += freeSize + PageSize;
|
||||
freeSize = 0;
|
||||
|
||||
ulong remainder = address % alignment;
|
||||
|
||||
if (remainder != 0)
|
||||
{
|
||||
address = (address - remainder) + alignment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PteUnmapped;
|
||||
}
|
||||
|
||||
internal ulong GetSubSize(ulong gpuVa)
|
||||
{
|
||||
ulong size = 0;
|
||||
|
||||
while (GetPte(gpuVa + size) != PteUnmapped)
|
||||
{
|
||||
size += PageSize;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
internal ulong Translate(ulong gpuVa)
|
||||
{
|
||||
ulong baseAddress = GetPte(gpuVa);
|
||||
|
||||
if (baseAddress == PteUnmapped || baseAddress == PteReserved)
|
||||
{
|
||||
return PteUnmapped;
|
||||
}
|
||||
|
||||
return baseAddress + (gpuVa & PageMask);
|
||||
}
|
||||
|
||||
public bool IsRegionFree(ulong va, ulong size)
|
||||
{
|
||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||
{
|
||||
if (IsPageInUse(va + offset))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsPageInUse(ulong va)
|
||||
{
|
||||
if (va >> PtLvl0Bits + PtLvl1Bits + PtPageBits != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong l0 = (va >> PtLvl0Bit) & PtLvl0Mask;
|
||||
ulong l1 = (va >> PtLvl1Bit) & PtLvl1Mask;
|
||||
|
||||
if (_pageTable[l0] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _pageTable[l0][l1] != PteUnmapped;
|
||||
}
|
||||
|
||||
private ulong GetPte(ulong address)
|
||||
{
|
||||
ulong l0 = (address >> PtLvl0Bit) & PtLvl0Mask;
|
||||
ulong l1 = (address >> PtLvl1Bit) & PtLvl1Mask;
|
||||
|
||||
if (_pageTable[l0] == null)
|
||||
{
|
||||
return PteUnmapped;
|
||||
}
|
||||
|
||||
return _pageTable[l0][l1];
|
||||
}
|
||||
|
||||
private void SetPte(ulong address, ulong tgtAddr)
|
||||
{
|
||||
ulong l0 = (address >> PtLvl0Bit) & PtLvl0Mask;
|
||||
ulong l1 = (address >> PtLvl1Bit) & PtLvl1Mask;
|
||||
|
||||
if (_pageTable[l0] == null)
|
||||
{
|
||||
_pageTable[l0] = new ulong[PtLvl1Size];
|
||||
|
||||
for (ulong index = 0; index < PtLvl1Size; index++)
|
||||
{
|
||||
_pageTable[l0][index] = PteUnmapped;
|
||||
}
|
||||
}
|
||||
|
||||
_pageTable[l0][l1] = tgtAddr;
|
||||
}
|
||||
}
|
||||
}
|
208
Ryujinx.Graphics.Gpu/Memory/RangeList.cs
Normal file
208
Ryujinx.Graphics.Gpu/Memory/RangeList.cs
Normal file
|
@ -0,0 +1,208 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
class RangeList<T> where T : IRange<T>
|
||||
{
|
||||
private List<T> _items;
|
||||
|
||||
public RangeList()
|
||||
{
|
||||
_items = new List<T>();
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
lock (_items)
|
||||
{
|
||||
int index = BinarySearch(item.Address);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = ~index;
|
||||
}
|
||||
|
||||
_items.Insert(index, item);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
lock (_items)
|
||||
{
|
||||
int index = BinarySearch(item.Address);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
while (index > 0 && _items[index - 1].Address == item.Address)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
|
||||
while (index < _items.Count)
|
||||
{
|
||||
if (_items[index].Equals(item))
|
||||
{
|
||||
_items.RemoveAt(index);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_items[index].Address > item.Address)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public T FindFirstOverlap(T item)
|
||||
{
|
||||
return FindFirstOverlap(item.Address, item.Size);
|
||||
}
|
||||
|
||||
public T FindFirstOverlap(ulong address, ulong size)
|
||||
{
|
||||
lock (_items)
|
||||
{
|
||||
int index = BinarySearch(address, size);
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
return _items[index];
|
||||
}
|
||||
}
|
||||
|
||||
public T[] FindOverlaps(T item)
|
||||
{
|
||||
return FindOverlaps(item.Address, item.Size);
|
||||
}
|
||||
|
||||
public T[] FindOverlaps(ulong address, ulong size)
|
||||
{
|
||||
List<T> overlapsList = new List<T>();
|
||||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
lock (_items)
|
||||
{
|
||||
foreach (T item in _items)
|
||||
{
|
||||
if (item.Address >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (item.OverlapsWith(address, size))
|
||||
{
|
||||
overlapsList.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return overlapsList.ToArray();
|
||||
}
|
||||
|
||||
public T[] FindOverlaps(ulong address)
|
||||
{
|
||||
List<T> overlapsList = new List<T>();
|
||||
|
||||
lock (_items)
|
||||
{
|
||||
int index = BinarySearch(address);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
while (index > 0 && _items[index - 1].Address == address)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
|
||||
while (index < _items.Count)
|
||||
{
|
||||
T overlap = _items[index++];
|
||||
|
||||
if (overlap.Address != address)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
overlapsList.Add(overlap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return overlapsList.ToArray();
|
||||
}
|
||||
|
||||
private int BinarySearch(ulong address)
|
||||
{
|
||||
int left = 0;
|
||||
int right = _items.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int range = right - left;
|
||||
|
||||
int middle = left + (range >> 1);
|
||||
|
||||
T item = _items[middle];
|
||||
|
||||
if (item.Address == address)
|
||||
{
|
||||
return middle;
|
||||
}
|
||||
|
||||
if (address < item.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ~left;
|
||||
}
|
||||
|
||||
private int BinarySearch(ulong address, ulong size)
|
||||
{
|
||||
int left = 0;
|
||||
int right = _items.Count - 1;
|
||||
|
||||
while (left <= right)
|
||||
{
|
||||
int range = right - left;
|
||||
|
||||
int middle = left + (range >> 1);
|
||||
|
||||
T item = _items[middle];
|
||||
|
||||
if (item.OverlapsWith(address, size))
|
||||
{
|
||||
return middle;
|
||||
}
|
||||
|
||||
if (address < item.Address)
|
||||
{
|
||||
right = middle - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
left = middle + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ~left;
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs
Normal file
10
Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
struct VertexBuffer
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
public int Stride;
|
||||
public int Divisor;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue