2023-12-04 14:17:13 +01:00
|
|
|
using System;
|
2023-08-14 19:18:47 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
namespace Ryujinx.Graphics.Vulkan
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// A structure tracking pending upload ranges for buffers.
|
|
|
|
/// Where a range is present, pending data exists that can either be used to build mirrors
|
|
|
|
/// or upload directly to the buffer.
|
|
|
|
/// </summary>
|
|
|
|
struct BufferMirrorRangeList
|
|
|
|
{
|
|
|
|
internal readonly struct Range
|
|
|
|
{
|
|
|
|
public int Offset { get; }
|
|
|
|
public int Size { get; }
|
|
|
|
|
|
|
|
public int End => Offset + Size;
|
|
|
|
|
|
|
|
public Range(int offset, int size)
|
|
|
|
{
|
|
|
|
Offset = offset;
|
|
|
|
Size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool OverlapsWith(int offset, int size)
|
|
|
|
{
|
|
|
|
return Offset < offset + size && offset < Offset + Size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private List<Range> _ranges;
|
|
|
|
|
|
|
|
public readonly IEnumerable<Range> All()
|
|
|
|
{
|
|
|
|
return _ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly bool Remove(int offset, int size)
|
|
|
|
{
|
|
|
|
var list = _ranges;
|
|
|
|
bool removedAny = false;
|
|
|
|
if (list != null)
|
|
|
|
{
|
|
|
|
int overlapIndex = BinarySearch(list, offset, size);
|
|
|
|
|
|
|
|
if (overlapIndex >= 0)
|
|
|
|
{
|
|
|
|
// Overlaps with a range. Search back to find the first one it doesn't overlap with.
|
|
|
|
|
|
|
|
while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size))
|
|
|
|
{
|
|
|
|
overlapIndex--;
|
|
|
|
}
|
|
|
|
|
|
|
|
int endOffset = offset + size;
|
|
|
|
int startIndex = overlapIndex;
|
|
|
|
|
|
|
|
var currentOverlap = list[overlapIndex];
|
|
|
|
|
|
|
|
// Orphan the start of the overlap.
|
|
|
|
if (currentOverlap.Offset < offset)
|
|
|
|
{
|
|
|
|
list[overlapIndex] = new Range(currentOverlap.Offset, offset - currentOverlap.Offset);
|
|
|
|
currentOverlap = new Range(offset, currentOverlap.End - offset);
|
|
|
|
list.Insert(++overlapIndex, currentOverlap);
|
|
|
|
startIndex++;
|
|
|
|
|
|
|
|
removedAny = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove any middle overlaps.
|
|
|
|
while (currentOverlap.Offset < endOffset)
|
|
|
|
{
|
|
|
|
if (currentOverlap.End > endOffset)
|
|
|
|
{
|
|
|
|
// Update the end overlap instead of removing it, if it spans beyond the removed range.
|
|
|
|
list[overlapIndex] = new Range(endOffset, currentOverlap.End - endOffset);
|
|
|
|
|
|
|
|
removedAny = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (++overlapIndex >= list.Count)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentOverlap = list[overlapIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
int count = overlapIndex - startIndex;
|
|
|
|
|
|
|
|
list.RemoveRange(startIndex, count);
|
|
|
|
|
|
|
|
removedAny |= count > 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return removedAny;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Add(int offset, int size)
|
|
|
|
{
|
|
|
|
var list = _ranges;
|
|
|
|
if (list != null)
|
|
|
|
{
|
|
|
|
int overlapIndex = BinarySearch(list, offset, size);
|
|
|
|
if (overlapIndex >= 0)
|
|
|
|
{
|
|
|
|
while (overlapIndex > 0 && list[overlapIndex - 1].OverlapsWith(offset, size))
|
|
|
|
{
|
|
|
|
overlapIndex--;
|
|
|
|
}
|
|
|
|
|
|
|
|
int endOffset = offset + size;
|
|
|
|
int startIndex = overlapIndex;
|
|
|
|
|
|
|
|
while (overlapIndex < list.Count && list[overlapIndex].OverlapsWith(offset, size))
|
|
|
|
{
|
|
|
|
var currentOverlap = list[overlapIndex];
|
|
|
|
var currentOverlapEndOffset = currentOverlap.Offset + currentOverlap.Size;
|
|
|
|
|
|
|
|
if (offset > currentOverlap.Offset)
|
|
|
|
{
|
|
|
|
offset = currentOverlap.Offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (endOffset < currentOverlapEndOffset)
|
|
|
|
{
|
|
|
|
endOffset = currentOverlapEndOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
overlapIndex++;
|
|
|
|
size = endOffset - offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
int count = overlapIndex - startIndex;
|
|
|
|
|
|
|
|
list.RemoveRange(startIndex, count);
|
|
|
|
|
|
|
|
overlapIndex = startIndex;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
overlapIndex = ~overlapIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
list.Insert(overlapIndex, new Range(offset, size));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_ranges = new List<Range>
|
|
|
|
{
|
|
|
|
new Range(offset, size)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly bool OverlapsWith(int offset, int size)
|
|
|
|
{
|
|
|
|
var list = _ranges;
|
|
|
|
if (list == null)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return BinarySearch(list, offset, size) >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly List<Range> FindOverlaps(int offset, int size)
|
|
|
|
{
|
|
|
|
var list = _ranges;
|
|
|
|
if (list == null)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
List<Range> result = null;
|
|
|
|
|
|
|
|
int index = BinarySearch(list, offset, size);
|
|
|
|
|
|
|
|
if (index >= 0)
|
|
|
|
{
|
|
|
|
while (index > 0 && list[index - 1].OverlapsWith(offset, size))
|
|
|
|
{
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
(result ??= new List<Range>()).Add(list[index++]);
|
|
|
|
}
|
|
|
|
while (index < list.Count && list[index].OverlapsWith(offset, size));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static int BinarySearch(List<Range> list, int offset, int size)
|
|
|
|
{
|
|
|
|
int left = 0;
|
|
|
|
int right = list.Count - 1;
|
|
|
|
|
|
|
|
while (left <= right)
|
|
|
|
{
|
|
|
|
int range = right - left;
|
|
|
|
|
|
|
|
int middle = left + (range >> 1);
|
|
|
|
|
|
|
|
var item = list[middle];
|
|
|
|
|
|
|
|
if (item.OverlapsWith(offset, size))
|
|
|
|
{
|
|
|
|
return middle;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset < item.Offset)
|
|
|
|
{
|
|
|
|
right = middle - 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
left = middle + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ~left;
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly void FillData(Span<byte> baseData, Span<byte> modData, int offset, Span<byte> result)
|
|
|
|
{
|
|
|
|
int size = baseData.Length;
|
|
|
|
int endOffset = offset + size;
|
|
|
|
|
|
|
|
var list = _ranges;
|
|
|
|
if (list == null)
|
|
|
|
{
|
|
|
|
baseData.CopyTo(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
int srcOffset = offset;
|
|
|
|
int dstOffset = 0;
|
|
|
|
bool activeRange = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < list.Count; i++)
|
|
|
|
{
|
|
|
|
var range = list[i];
|
|
|
|
|
|
|
|
int rangeEnd = range.Offset + range.Size;
|
|
|
|
|
|
|
|
if (activeRange)
|
|
|
|
{
|
|
|
|
if (range.Offset >= endOffset)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (rangeEnd <= offset)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
activeRange = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int baseSize = range.Offset - srcOffset;
|
|
|
|
|
|
|
|
if (baseSize > 0)
|
|
|
|
{
|
|
|
|
baseData.Slice(dstOffset, baseSize).CopyTo(result.Slice(dstOffset, baseSize));
|
|
|
|
srcOffset += baseSize;
|
|
|
|
dstOffset += baseSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
int modSize = Math.Min(rangeEnd - srcOffset, endOffset - srcOffset);
|
|
|
|
if (modSize != 0)
|
|
|
|
{
|
|
|
|
modData.Slice(dstOffset, modSize).CopyTo(result.Slice(dstOffset, modSize));
|
|
|
|
srcOffset += modSize;
|
|
|
|
dstOffset += modSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int baseSizeEnd = endOffset - srcOffset;
|
|
|
|
|
|
|
|
if (baseSizeEnd > 0)
|
|
|
|
{
|
|
|
|
baseData.Slice(dstOffset, baseSizeEnd).CopyTo(result.Slice(dstOffset, baseSizeEnd));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public readonly int Count()
|
|
|
|
{
|
|
|
|
return _ranges?.Count ?? 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Clear()
|
|
|
|
{
|
|
|
|
_ranges = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|