sharpemu/tests/SharpEmu.Libs.Tests/Gpu/Images/MipStatisticsTests.cs

117 lines
4.0 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Buffers.Binary;
using SharpEmu.Libs.Gpu.Images;
using Xunit;
namespace SharpEmu.Libs.Tests.Gpu.Images;
public sealed class MipStatisticsTests
{
private const uint CounterEnabled = 1u << 25;
private static TextureDescriptorWords Texture(uint slot, uint address = 0x1000, uint counter = CounterEnabled) =>
new([address, 0, 0, 0, 0, counter, slot, 0]);
private static (uint ClampedSamples, uint Slot, uint UnclampedSamples, uint FinestMip) Counter(ReadOnlySpan<byte> report, int slot)
{
var counter = report.Slice((int)MipStatistics.FirstCounterOffset + slot * 8, 8);
var first = BinaryPrimitives.ReadUInt32LittleEndian(counter);
var second = BinaryPrimitives.ReadUInt32LittleEndian(counter[4..]);
return (first & 0xFFFFFF, first >> 24, second & 0xFFFFFF, (second >> 24) & 0xF);
}
[Fact]
public void Accessors_DecodeTheCounterFields()
{
var texture = Texture(slot: 0xAB);
Assert.True(texture.MipStatsEnabled);
Assert.Equal(0xABu, texture.MipStatsSlot);
Assert.False(Texture(slot: 0xAB, counter: 0).MipStatsEnabled);
}
[Fact]
public void Report_AsksForTheFinestMipOfASlotWhoseTexturesWereSampled()
{
var statistics = new MipStatistics();
statistics.NoteSampled(Texture(slot: 7));
statistics.NoteSampled(Texture(slot: 7));
var report = new byte[MipStatistics.ReportBytes];
Assert.True(statistics.TryWriteReport(report));
Assert.Equal((0x20000u, 7u, 0x20000u, 0u), Counter(report, 7));
}
[Fact]
public void Report_MarksASlotWithoutSampledTexturesAsNotSampled()
{
var statistics = new MipStatistics();
statistics.NoteSampled(Texture(slot: 7));
statistics.NoteSampled(Texture(slot: 8, counter: 0));
statistics.NoteSampled(Texture(slot: 9, address: 0));
var report = new byte[MipStatistics.ReportBytes];
statistics.TryWriteReport(report);
Assert.Equal((0u, 8u, 0u, MipStatistics.NoMipSampled), Counter(report, 8));
Assert.Equal((0u, 9u, 0u, MipStatistics.NoMipSampled), Counter(report, 9));
Assert.Equal((0u, 255u, 0u, MipStatistics.NoMipSampled), Counter(report, 255));
}
[Fact]
public void Report_SaturatesTheSampleCounts()
{
var statistics = new MipStatistics();
for (var binding = 0; binding < 300; binding++)
{
statistics.NoteSampled(Texture(slot: 1));
}
var report = new byte[MipStatistics.ReportBytes];
statistics.TryWriteReport(report);
Assert.Equal((0xFFFFFFu, 1u, 0xFFFFFFu, 0u), Counter(report, 1));
}
[Fact]
public void Report_StartsANewCountingPeriod()
{
var statistics = new MipStatistics();
statistics.NoteSampled(Texture(slot: 3));
statistics.TryWriteReport(new byte[MipStatistics.ReportBytes]);
var report = new byte[MipStatistics.ReportBytes];
statistics.TryWriteReport(report);
Assert.Equal((0u, 3u, 0u, MipStatistics.NoMipSampled), Counter(report, 3));
}
[Fact]
public void Report_LeavesTheHeaderAndLaterCounterBlocksToTheCaller()
{
var statistics = new MipStatistics();
statistics.NoteSampled(Texture(slot: 0));
var report = new byte[MipStatistics.ReportBytes + 2048];
statistics.TryWriteReport(report);
Assert.All(report[..(int)MipStatistics.FirstCounterOffset], static value => Assert.Equal(0, value));
Assert.All(report[MipStatistics.ReportBytes..], static value => Assert.Equal(0, value));
}
[Fact]
public void Report_IsRefusedWhenTheBufferCannotHoldEveryCounter()
{
var statistics = new MipStatistics();
statistics.NoteSampled(Texture(slot: 3));
var report = new byte[MipStatistics.ReportBytes - 1];
Assert.False(statistics.TryWriteReport(report));
Assert.All(report, static value => Assert.Equal(0, value));
}
}