using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan
{
///
/// 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.
///
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 _ranges;
public readonly IEnumerable 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
{
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 FindOverlaps(int offset, int size)
{
var list = _ranges;
if (list == null)
{
return null;
}
List 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()).Add(list[index++]);
}
while (index < list.Count && list[index].OverlapsWith(offset, size));
}
return result;
}
private static int BinarySearch(List 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 baseData, Span modData, int offset, Span 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;
}
}
}