| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- using System;
- 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)
- {
- List<Range> 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;
- Range 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)
- {
- List<Range> 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))
- {
- Range currentOverlap = list[overlapIndex];
- int 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(offset, size)];
- }
- }
- public readonly bool OverlapsWith(int offset, int size)
- {
- List<Range> list = _ranges;
- if (list == null)
- {
- return false;
- }
- return BinarySearch(list, offset, size) >= 0;
- }
- public readonly List<Range> FindOverlaps(int offset, int size)
- {
- List<Range> 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 ??= []).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);
- Range 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;
- List<Range> 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++)
- {
- Range 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;
- }
- }
- }
|