179 lines
7.8 KiB
C#
179 lines
7.8 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using SharpEmu.Libs.VideoOut;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.VideoOut;
|
|
|
|
public sealed class BufferUploadProfileTests
|
|
{
|
|
[Fact]
|
|
public void CollectionRequiresBothProfileSwitchesAndReportsOnlyOnce()
|
|
{
|
|
BufferUploadProfile.Report(TextWriter.Null);
|
|
var expectedEnabled = (Environment.GetEnvironmentVariable("SHARPEMU_PROFILE_PERFORMANCE") == "1" ||
|
|
Environment.GetEnvironmentVariable("SHARPEMU_PROFILE_RENDER") == "1") &&
|
|
Environment.GetEnvironmentVariable("SHARPEMU_PROFILE_PERFORMANCE_FRAME_TRACE") == "1";
|
|
Assert.Equal(expectedEnabled, BufferUploadProfile.Enabled);
|
|
using (BufferUploadProfile.BeginSweep(11, 22, 0))
|
|
BufferUploadProfile.Record(0x1000, 4096, 1, 4096, 1024, Stopwatch.Frequency);
|
|
using var writer = new StringWriter();
|
|
BufferUploadProfile.Report(writer);
|
|
if (expectedEnabled)
|
|
{
|
|
Assert.Contains("[PERF][BUFFER_SYNC] source=device-address", writer.ToString());
|
|
Assert.Contains("bytes=4096 hot_bytes=1024 sync_ms=1000.00", writer.ToString());
|
|
}
|
|
else
|
|
Assert.Equal(string.Empty, writer.ToString());
|
|
writer.GetStringBuilder().Clear();
|
|
BufferUploadProfile.Report(writer);
|
|
Assert.Equal(string.Empty, writer.ToString());
|
|
}
|
|
|
|
[Fact]
|
|
public void NestedScopesRestoreSourceAfterException()
|
|
{
|
|
BufferUploadProfile.Report(TextWriter.Null);
|
|
using (BufferUploadProfile.BeginSweep(11, 22, 0))
|
|
{
|
|
Assert.Throws<InvalidOperationException>((Action)(() =>
|
|
{
|
|
using var innerScope = BufferUploadProfile.BeginSweep(0, 0, 33);
|
|
BufferUploadProfile.Record(0x1000, 4096, 0, 0, 0, 1);
|
|
throw new InvalidOperationException();
|
|
}));
|
|
BufferUploadProfile.Record(0x2000, 4096, 0, 0, 0, 2);
|
|
}
|
|
BufferUploadProfile.Record(0x3000, 4096, 0, 0, 0, 3);
|
|
using var writer = new StringWriter();
|
|
BufferUploadProfile.Report(writer);
|
|
if (!BufferUploadProfile.Enabled)
|
|
{
|
|
Assert.Equal(string.Empty, writer.ToString());
|
|
return;
|
|
}
|
|
var lines = writer.ToString().Split(Environment.NewLine);
|
|
Assert.Contains(lines, line => line.Contains("compute=0x0000000000000021 address=0x0000000000001000"));
|
|
Assert.Contains(lines, line => line.Contains("vertex=0x000000000000000B pixel=0x0000000000000016 compute=0x0000000000000000 address=0x0000000000002000"));
|
|
Assert.Contains(lines, line => line.Contains("source=other") && line.Contains("address=0x0000000000003000"));
|
|
}
|
|
|
|
[Fact]
|
|
public void ReportUsesInvariantDecimalSeparator()
|
|
{
|
|
BufferUploadProfile.Report(TextWriter.Null);
|
|
var originalCulture = CultureInfo.CurrentCulture;
|
|
try
|
|
{
|
|
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("de-DE");
|
|
BufferUploadProfile.Record(0x1000, 4096, 1, 4096, 0, Stopwatch.Frequency);
|
|
using var writer = new StringWriter();
|
|
BufferUploadProfile.Report(writer);
|
|
if (BufferUploadProfile.Enabled)
|
|
{
|
|
Assert.Contains("sync_ms=1000.00", writer.ToString());
|
|
Assert.DoesNotContain("sync_ms=1000,", writer.ToString());
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
CultureInfo.CurrentCulture = originalCulture;
|
|
BufferUploadProfile.Report(TextWriter.Null);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void SlowSamplesRetainLateCallsAndCleanChecksAfterRangeOverflow()
|
|
{
|
|
var counters = new BufferUploadProfile.Counters();
|
|
var source = new BufferUploadProfile.Source(true, 0, 0, 9);
|
|
for (uint index = 0; index < BufferUploadProfile.Counters.Capacity + 20; index++)
|
|
counters.Record(source, index * 4096, 4096, 1, 4096, 0, index, recordSlowSample: true);
|
|
counters.Record(source, 0xF00000, 4096, 0, 0, 0, 1000, recordSlowSample: true);
|
|
|
|
Assert.Equal(BufferUploadProfile.Counters.Capacity, counters.Uploads.Count);
|
|
Assert.Equal(BufferUploadProfile.Counters.SlowSampleCapacity, counters.SlowSampleCount);
|
|
Assert.Equal(0xF00000ul, counters.SlowSamples[0].Key.Address);
|
|
Assert.Equal(0, counters.SlowSamples[0].Copies);
|
|
Assert.True(counters.SlowSamples[0].Timestamp > 0);
|
|
for (var index = 1; index < counters.SlowSampleCount; index++)
|
|
{
|
|
Assert.Equal(148 - index, counters.SlowSamples[index].Ticks);
|
|
Assert.Equal((ulong)(148 - index) * 4096, counters.SlowSamples[index].Key.Address);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void SlowSamplesRequireDetailedCollectionAndKeepDurationOrder()
|
|
{
|
|
var counters = new BufferUploadProfile.Counters();
|
|
counters.Record(default, 0x1000, 4096, 0, 0, 0, 999);
|
|
Assert.Equal(0, counters.SlowSampleCount);
|
|
foreach (var ticks in new long[] { 2, 5, 1, 3, 4, 5, 0, 9, 8, 7, 6 })
|
|
counters.Record(default, 0x2000, 4096, 0, 0, 0, ticks, recordSlowSample: true);
|
|
Assert.Equal(new long[] { 9, 8, 7, 6, 5, 5, 4, 3 }, counters.SlowSamples.Select(sample => sample.Ticks));
|
|
}
|
|
|
|
[Fact]
|
|
public void SlowSampleUpdatesDoNotAllocateAfterCounterInitialization()
|
|
{
|
|
var counters = new BufferUploadProfile.Counters();
|
|
var index = 0;
|
|
var allocated = AllocationMeasurement.SteadyState(
|
|
() =>
|
|
{
|
|
for (; index < 100; index++)
|
|
counters.Record(default, 0x1000, 4096, 0, 0, 0, index, recordSlowSample: true);
|
|
},
|
|
() =>
|
|
{
|
|
for (var end = index + 1000; index < end; index++)
|
|
counters.Record(default, 0x1000, 4096, 0, 0, 0, index, recordSlowSample: true);
|
|
});
|
|
Assert.Equal(0, allocated);
|
|
}
|
|
|
|
[Fact]
|
|
public void CountersSeparateSweepAndOtherUploads()
|
|
{
|
|
var counters = new BufferUploadProfile.Counters();
|
|
var source = new BufferUploadProfile.Source(true, 11, 22, 0);
|
|
counters.FindSource(source).Sweeps++;
|
|
counters.Record(source, 0x1000, 4096, 0, 0, 0, 10);
|
|
counters.Record(source, 0x1000, 4096, 2, 2048, 1024, 30);
|
|
counters.Record(default, 0x1000, 4096, 1, 4096, 0, 50);
|
|
var statistics = counters.Sources[source];
|
|
Assert.Equal(1, statistics.Sweeps);
|
|
Assert.Equal(2, statistics.Checks);
|
|
Assert.Equal(1, statistics.Uploads);
|
|
Assert.Equal(2, statistics.Copies);
|
|
Assert.Equal(2048ul, statistics.Bytes);
|
|
Assert.Equal(1024ul, statistics.HotBytes);
|
|
Assert.Equal(40, statistics.Ticks);
|
|
Assert.Equal(2, counters.Uploads.Count);
|
|
Assert.Equal(4096ul, counters.Sources[default].Bytes);
|
|
}
|
|
|
|
[Fact]
|
|
public void CapacityOverflowKeepsAllTotals()
|
|
{
|
|
var counters = new BufferUploadProfile.Counters();
|
|
for (uint index = 0; index < BufferUploadProfile.Counters.Capacity + 3; index++)
|
|
counters.Record(new(true, index, 0, 0), index * 4096, 4096, 1, 4096, 1024, 5);
|
|
Assert.Equal(BufferUploadProfile.Counters.Capacity, counters.Sources.Count);
|
|
Assert.Equal(BufferUploadProfile.Counters.Capacity, counters.Uploads.Count);
|
|
Assert.Equal(3, counters.OtherSources.Checks);
|
|
Assert.Equal(3 * 4096ul, counters.OtherSources.Bytes);
|
|
Assert.Equal(3 * 1024ul, counters.OtherUploads.HotBytes);
|
|
var total = new BufferUploadProfile.Statistics();
|
|
foreach (var statistics in counters.Uploads.Values) total.Merge(statistics);
|
|
total.Merge(counters.OtherUploads);
|
|
Assert.Equal(BufferUploadProfile.Counters.Capacity + 3, total.Uploads);
|
|
Assert.Equal((ulong)(BufferUploadProfile.Counters.Capacity + 3) * 4096, total.Bytes);
|
|
}
|
|
}
|