diff options
| author | gdk <gab.dark.100@gmail.com> | 2019-10-13 03:02:07 -0300 |
|---|---|---|
| committer | Thog <thog@protonmail.com> | 2020-01-09 02:13:00 +0100 |
| commit | 1876b346fea647e8284a66bb6d62c38801035cff (patch) | |
| tree | 6eeff094298cda84d1613dc5ec0691e51d7b35f1 /Ryujinx.Graphics.Gpu/Memory | |
| parent | f617fb542a0e3d36012d77a4b5acbde7b08902f2 (diff) | |
Initial work
Diffstat (limited to 'Ryujinx.Graphics.Gpu/Memory')
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/Buffer.cs | 99 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs | 8 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/BufferManager.cs | 530 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs | 15 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/IRange.cs | 10 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs | 12 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs | 54 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 265 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/RangeList.cs | 208 | ||||
| -rw-r--r-- | Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs | 10 |
10 files changed, 1211 insertions, 0 deletions
diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs new file mode 100644 index 00000000..30bd1ac0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -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(); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs new file mode 100644 index 00000000..2a074bd3 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + struct BufferBounds + { + public ulong Address; + public ulong Size; + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs new file mode 100644 index 00000000..eb2e0ca9 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -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(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs new file mode 100644 index 00000000..5f21704d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs @@ -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); + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/IRange.cs b/Ryujinx.Graphics.Gpu/Memory/IRange.cs new file mode 100644 index 00000000..ee3d5c0b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/IRange.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + interface IRange<T> + { + ulong Address { get; } + ulong Size { get; } + + bool OverlapsWith(ulong address, ulong size); + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs b/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs new file mode 100644 index 00000000..ce2a2c74 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs @@ -0,0 +1,12 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + struct IndexBuffer + { + public ulong Address; + public ulong Size; + + public IndexType Type; + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs new file mode 100644 index 00000000..500c36e5 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs @@ -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); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs new file mode 100644 index 00000000..d1a3e69c --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/RangeList.cs b/Ryujinx.Graphics.Gpu/Memory/RangeList.cs new file mode 100644 index 00000000..6114f15d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/RangeList.cs @@ -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; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs b/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs new file mode 100644 index 00000000..1cb854d6 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + struct VertexBuffer + { + public ulong Address; + public ulong Size; + public int Stride; + public int Divisor; + } +}
\ No newline at end of file |
