[GPU] Track GPU write ticks per buffer range

This commit is contained in:
ParantezTech 2026-10-11 01:36:32 +03:00
parent 6d69163297
commit fd2afcd8c8
2 changed files with 169 additions and 3 deletions

View File

@ -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);

View File

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