using AcDream.App.Rendering.Gpu.Gl;
namespace AcDream.App.Tests.Rendering.Gpu.Gl;
///
/// The texture table's flush is mapped with GL_MAP_UNSYNCHRONIZED_BIT and
/// GL_MAP_INVALIDATE_RANGE_BIT, so a run this tracker reports must contain
/// nothing but slots that were actually written. A run that swallowed a clean
/// slot in between would let the driver discard a live bindless handle while a
/// submitted draw was reading it — invisible in any pixel gate, and exactly the
/// class of fault the ring rewrite exists to remove. These tests pin that.
///
public sealed class GlDirtySlotRunsTests
{
[Fact]
public void NothingMarkedYieldsNoRuns()
{
var runs = new GlDirtySlotRuns(16);
Assert.False(runs.HasDirtySlots);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void OneMarkedSlotIsOneRunOfOne()
{
var runs = new GlDirtySlotRuns(16);
runs.Mark(7);
Assert.True(runs.HasDirtySlots);
Assert.True(runs.TryTakeNextRun(out uint first, out uint count));
Assert.Equal(7u, first);
Assert.Equal(1u, count);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void ConsecutiveSlotsMergeIntoOneRun()
{
var runs = new GlDirtySlotRuns(16);
runs.Mark(4);
runs.Mark(5);
runs.Mark(6);
Assert.True(runs.TryTakeNextRun(out uint first, out uint count));
Assert.Equal(4u, first);
Assert.Equal(3u, count);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void AGapBetweenMarkedSlotsSplitsTheRuns()
{
var runs = new GlDirtySlotRuns(64);
runs.Mark(5);
runs.Mark(50);
Assert.True(runs.TryTakeNextRun(out uint firstStart, out uint firstCount));
Assert.Equal(5u, firstStart);
Assert.Equal(1u, firstCount);
Assert.True(runs.TryTakeNextRun(out uint secondStart, out uint secondCount));
Assert.Equal(50u, secondStart);
Assert.Equal(1u, secondCount);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void RunsComeBackInAscendingSlotOrderRegardlessOfMarkOrder()
{
var runs = new GlDirtySlotRuns(64);
runs.Mark(40);
runs.Mark(1);
runs.Mark(41);
runs.Mark(20);
runs.Mark(0);
Assert.True(runs.TryTakeNextRun(out uint start, out uint count));
Assert.Equal(0u, start);
Assert.Equal(2u, count);
Assert.True(runs.TryTakeNextRun(out start, out count));
Assert.Equal(20u, start);
Assert.Equal(1u, count);
Assert.True(runs.TryTakeNextRun(out start, out count));
Assert.Equal(40u, start);
Assert.Equal(2u, count);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void MarkingTheSameSlotTwiceStillYieldsOneRun()
{
var runs = new GlDirtySlotRuns(16);
runs.Mark(3);
runs.Mark(3);
Assert.True(runs.TryTakeNextRun(out uint start, out uint count));
Assert.Equal(3u, start);
Assert.Equal(1u, count);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void DrainingLeavesTheTrackerCleanForTheNextFlush()
{
var runs = new GlDirtySlotRuns(16);
runs.Mark(2);
runs.Mark(9);
while (runs.TryTakeNextRun(out _, out _))
{
}
Assert.False(runs.HasDirtySlots);
runs.Mark(11);
Assert.True(runs.TryTakeNextRun(out uint start, out uint count));
Assert.Equal(11u, start);
Assert.Equal(1u, count);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void SlotsMarkedAfterAPartialDrainAreStillReported()
{
var runs = new GlDirtySlotRuns(32);
runs.Mark(4);
runs.Mark(20);
Assert.True(runs.TryTakeNextRun(out uint start, out uint count));
Assert.Equal(4u, start);
Assert.Equal(1u, count);
// A draw between two flushes can register another texture.
runs.Mark(21);
Assert.True(runs.TryTakeNextRun(out start, out count));
Assert.Equal(20u, start);
Assert.Equal(2u, count);
Assert.False(runs.TryTakeNextRun(out _, out _));
}
[Fact]
public void TheLastSlotInTheTableIsAddressable()
{
var runs = new GlDirtySlotRuns(8);
runs.Mark(7);
Assert.True(runs.TryTakeNextRun(out uint start, out uint count));
Assert.Equal(7u, start);
Assert.Equal(1u, count);
}
[Fact]
public void MarkingBeyondCapacityThrows()
{
var runs = new GlDirtySlotRuns(8);
Assert.Throws(() => runs.Mark(8));
}
[Fact]
public void ZeroCapacityIsRejected()
{
Assert.Throws(() => new GlDirtySlotRuns(0));
}
///
/// The whole point, stated as one property: every slot a run covers was
/// marked, and every marked slot is covered exactly once.
///
[Fact]
public void EveryReportedSlotWasMarkedAndEveryMarkedSlotIsReportedOnce()
{
const int Capacity = 200;
var random = new Random(20260727);
var expected = new HashSet();
var runs = new GlDirtySlotRuns(Capacity);
for (int i = 0; i < 60; i++)
{
uint slot = (uint)random.Next(Capacity);
expected.Add(slot);
runs.Mark(slot);
}
var reported = new List();
while (runs.TryTakeNextRun(out uint start, out uint count))
{
for (uint slot = start; slot < start + count; slot++)
reported.Add(slot);
}
Assert.Equal(reported.Count, reported.Distinct().Count());
Assert.Equal(expected.OrderBy(slot => slot), reported);
}
}