Initial work

This commit is contained in:
gdk 2019-10-13 03:02:07 -03:00 committed by Thog
parent f617fb542a
commit 1876b346fe
518 changed files with 15170 additions and 12486 deletions

View 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();
}
}
}

View file

@ -0,0 +1,8 @@
namespace Ryujinx.Graphics.Gpu.Memory
{
struct BufferBounds
{
public ulong Address;
public ulong Size;
}
}

View 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();
}
}
}
}

View 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);
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View file

@ -0,0 +1,10 @@
namespace Ryujinx.Graphics.Gpu.Memory
{
struct VertexBuffer
{
public ulong Address;
public ulong Size;
public int Stride;
public int Divisor;
}
}