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