[GPU] Track GPU write ticks per buffer range
This commit is contained in:
parent
6d69163297
commit
fd2afcd8c8
|
|
@ -172,16 +172,39 @@ public unsafe class GpuBuffer : IDisposable
|
|||
// A readback of it only has to wait for that tick, not for all queued work.
|
||||
public ulong LastGpuWriteTick { get; private set; }
|
||||
|
||||
private readonly WriteTickMap _writeTicks = new();
|
||||
private int _writeTickPruneCount = 64;
|
||||
|
||||
// Called by every path that records a GPU write into this buffer.
|
||||
public void NoteGpuWrite()
|
||||
public void NoteGpuWrite() => NoteGpuWrite(0, Size);
|
||||
|
||||
public void NoteGpuWrite(ulong offset, ulong size)
|
||||
{
|
||||
var tick = _scheduler.CurrentTick;
|
||||
if (tick > LastGpuWriteTick)
|
||||
{
|
||||
LastGpuWriteTick = tick;
|
||||
}
|
||||
|
||||
if (size == 0 || offset >= Size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_writeTicks.Assign(offset, Math.Min(size, Size - offset), tick);
|
||||
if (_writeTicks.Count >= _writeTickPruneCount)
|
||||
{
|
||||
if (Volatile.Read(ref _foreignReads) == 0)
|
||||
{
|
||||
_writeTicks.Prune(_scheduler.Timeline.CompletedTick + 1);
|
||||
}
|
||||
|
||||
_writeTickPruneCount = Math.Max(64, _writeTicks.Count * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong LastGpuWriteTickIn(ulong offset, ulong size) => _writeTicks.MaxTick(offset, size);
|
||||
|
||||
public void Write(ulong offset, ReadOnlySpan<byte> source)
|
||||
{
|
||||
if (_mapped == null || offset > Size || (ulong)source.Length > Size - offset)
|
||||
|
|
@ -243,7 +266,7 @@ public unsafe class GpuBuffer : IDisposable
|
|||
}
|
||||
|
||||
command.EndRendering();
|
||||
NoteGpuWrite();
|
||||
NoteGpuWrite(destinationOffset, size);
|
||||
var vk = _device.Vk;
|
||||
var native = new CommandBuffer(command.Handle);
|
||||
var before = stackalloc BufferMemoryBarrier2[2];
|
||||
|
|
@ -271,7 +294,7 @@ public unsafe class GpuBuffer : IDisposable
|
|||
|
||||
var command = _scheduler.Current;
|
||||
command.EndRendering();
|
||||
NoteGpuWrite();
|
||||
NoteGpuWrite(offset, size);
|
||||
var vk = _device.Vk;
|
||||
var native = new CommandBuffer(command.Handle);
|
||||
var before = CreateBarrier(offset, size, MemoryAccess, AccessFlags.TransferWriteBit);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
namespace SharpEmu.Libs.Gpu.Buffers;
|
||||
|
||||
public sealed class WriteTickMap
|
||||
{
|
||||
private readonly List<Entry> _entries = [];
|
||||
private ulong _floor;
|
||||
|
||||
private readonly record struct Entry(ulong Begin, ulong End, ulong Tick);
|
||||
|
||||
public int Count => _entries.Count;
|
||||
|
||||
public ulong Floor => _floor;
|
||||
|
||||
public void Assign(ulong begin, ulong size, ulong tick)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var end = begin + size;
|
||||
var index = FirstEndingAfter(begin);
|
||||
var removed = 0;
|
||||
while (index + removed < _entries.Count && _entries[index + removed].Begin < end)
|
||||
{
|
||||
removed++;
|
||||
}
|
||||
|
||||
Entry? left = null;
|
||||
Entry? right = null;
|
||||
if (removed != 0)
|
||||
{
|
||||
var first = _entries[index];
|
||||
if (first.Begin < begin)
|
||||
{
|
||||
left = first with { End = begin };
|
||||
}
|
||||
|
||||
var last = _entries[index + removed - 1];
|
||||
if (last.End > end)
|
||||
{
|
||||
right = last with { Begin = end };
|
||||
}
|
||||
|
||||
_entries.RemoveRange(index, removed);
|
||||
}
|
||||
|
||||
var insert = index;
|
||||
if (left is { } leftPiece)
|
||||
{
|
||||
_entries.Insert(insert++, leftPiece);
|
||||
}
|
||||
|
||||
_entries.Insert(insert, new Entry(begin, end, tick));
|
||||
var middle = insert++;
|
||||
if (right is { } rightPiece)
|
||||
{
|
||||
_entries.Insert(insert, rightPiece);
|
||||
}
|
||||
|
||||
if (middle + 1 < _entries.Count)
|
||||
{
|
||||
_ = TryMerge(middle, middle + 1);
|
||||
}
|
||||
|
||||
if (middle > 0)
|
||||
{
|
||||
_ = TryMerge(middle - 1, middle);
|
||||
}
|
||||
}
|
||||
|
||||
public ulong MaxTick(ulong begin, ulong size)
|
||||
{
|
||||
var tick = _floor;
|
||||
if (size == 0)
|
||||
{
|
||||
return tick;
|
||||
}
|
||||
|
||||
var end = begin + size;
|
||||
for (var index = FirstEndingAfter(begin); index < _entries.Count && _entries[index].Begin < end; index++)
|
||||
{
|
||||
tick = Math.Max(tick, _entries[index].Tick);
|
||||
}
|
||||
|
||||
return tick;
|
||||
}
|
||||
|
||||
public void Prune(ulong below)
|
||||
{
|
||||
var kept = 0;
|
||||
for (var index = 0; index < _entries.Count; index++)
|
||||
{
|
||||
var entry = _entries[index];
|
||||
if (entry.Tick < below)
|
||||
{
|
||||
_floor = Math.Max(_floor, entry.Tick);
|
||||
continue;
|
||||
}
|
||||
|
||||
_entries[kept++] = entry;
|
||||
}
|
||||
|
||||
_entries.RemoveRange(kept, _entries.Count - kept);
|
||||
}
|
||||
|
||||
private bool TryMerge(int left, int right)
|
||||
{
|
||||
var a = _entries[left];
|
||||
var b = _entries[right];
|
||||
if (a.End != b.Begin || a.Tick != b.Tick)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_entries[left] = a with { End = b.End };
|
||||
_entries.RemoveAt(right);
|
||||
return true;
|
||||
}
|
||||
|
||||
private int FirstEndingAfter(ulong address)
|
||||
{
|
||||
var low = 0;
|
||||
var high = _entries.Count;
|
||||
while (low < high)
|
||||
{
|
||||
var middle = low + ((high - low) >> 1);
|
||||
if (_entries[middle].End <= address)
|
||||
{
|
||||
low = middle + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
high = middle;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue