199 lines
7.8 KiB
C#
199 lines
7.8 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.Libs.VideoOut;
|
|
using Xunit;
|
|
|
|
namespace SharpEmu.Libs.Tests.VideoOut;
|
|
|
|
public sealed class PerformanceOverlayTests
|
|
{
|
|
[Theory]
|
|
[InlineData(PerformanceOverlayCorner.TopLeft, 12, 12)]
|
|
[InlineData(PerformanceOverlayCorner.TopRight, 1280 - 12 - PerfOverlay.PanelWidth, 12)]
|
|
[InlineData(PerformanceOverlayCorner.BottomRight, 1280 - 12 - PerfOverlay.PanelWidth, 720 - 12 - PerfOverlay.PanelHeight)]
|
|
[InlineData(PerformanceOverlayCorner.BottomLeft, 12, 720 - 12 - PerfOverlay.PanelHeight)]
|
|
public void FullPanelUsesSelectedCorner(PerformanceOverlayCorner corner, int left, int top)
|
|
{
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(new HostVideoOptions { OverlayCorner = corner }, false);
|
|
|
|
Assert.Equal(
|
|
new OverlayRectangle(left, top, PerfOverlay.PanelWidth, PerfOverlay.PanelHeight),
|
|
state.GetRectangle(1280, 720));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(PerformanceOverlayCorner.TopLeft)]
|
|
[InlineData(PerformanceOverlayCorner.TopRight)]
|
|
[InlineData(PerformanceOverlayCorner.BottomRight)]
|
|
[InlineData(PerformanceOverlayCorner.BottomLeft)]
|
|
public void SmallSurfaceClipsPanelInsideItsBounds(PerformanceOverlayCorner corner)
|
|
{
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(new HostVideoOptions { OverlayCorner = corner }, false);
|
|
foreach (var size in new[] { 0, 1, 12, 24, 100, 4096 })
|
|
{
|
|
var rectangle = state.GetRectangle(size, size);
|
|
Assert.InRange(rectangle.Left, 0, size);
|
|
Assert.InRange(rectangle.Top, 0, size);
|
|
Assert.InRange(rectangle.Left + rectangle.Width, 0, size);
|
|
Assert.InRange(rectangle.Top + rectangle.Height, 0, size);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void HotkeysChangeOnlyRuntimeState()
|
|
{
|
|
var options = new HostVideoOptions { OverlayCorner = PerformanceOverlayCorner.TopRight };
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(options, false);
|
|
state.Toggle();
|
|
state.CycleCorner();
|
|
|
|
Assert.False(state.Enabled);
|
|
Assert.Equal(PerformanceOverlayCorner.BottomRight, state.Corner);
|
|
Assert.True(options.OverlayEnabled);
|
|
Assert.Equal(PerformanceOverlayCorner.TopRight, options.OverlayCorner);
|
|
|
|
state.Configure(options, false);
|
|
Assert.True(state.Enabled);
|
|
Assert.Equal(PerformanceOverlayCorner.TopRight, state.Corner);
|
|
for (var index = 0; index < 4; index++)
|
|
{
|
|
state.CycleCorner();
|
|
}
|
|
Assert.Equal(PerformanceOverlayCorner.TopRight, state.Corner);
|
|
}
|
|
|
|
[Fact]
|
|
public void EnvironmentCanStartHiddenWithoutDisablingTheHotkey()
|
|
{
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(HostVideoOptions.Default, true);
|
|
Assert.False(state.DrawOnScreen);
|
|
state.Toggle();
|
|
Assert.True(state.DrawOnScreen);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(PerformanceOverlayMode.Full, true, PerfOverlay.PanelHeight)]
|
|
[InlineData(PerformanceOverlayMode.Minimal, true, PerfOverlay.MinimalPanelHeight)]
|
|
[InlineData(PerformanceOverlayMode.TitleBar, false, PerfOverlay.MinimalPanelHeight)]
|
|
public void ModeSelectsPanelOrTitleBar(PerformanceOverlayMode mode, bool onScreen, int height)
|
|
{
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(new HostVideoOptions { OverlayMode = mode }, false);
|
|
Assert.Equal(onScreen, state.DrawOnScreen);
|
|
Assert.Equal(height, state.DisplayHeight);
|
|
state.Toggle();
|
|
Assert.False(state.DrawOnScreen);
|
|
}
|
|
|
|
[Fact]
|
|
public void MinimalBottomPanelUsesItsOwnDimensions()
|
|
{
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(new HostVideoOptions
|
|
{
|
|
OverlayCorner = PerformanceOverlayCorner.BottomLeft,
|
|
OverlayMode = PerformanceOverlayMode.Minimal,
|
|
}, false);
|
|
Assert.Equal(
|
|
new OverlayRectangle(12, 720 - 12 - PerfOverlay.MinimalPanelHeight,
|
|
PerfOverlay.MinimalPanelWidth, PerfOverlay.MinimalPanelHeight),
|
|
state.GetRectangle(1280, 720));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(PerformanceOverlayCorner.TopLeft, false, false)]
|
|
[InlineData(PerformanceOverlayCorner.TopRight, true, false)]
|
|
[InlineData(PerformanceOverlayCorner.BottomRight, true, true)]
|
|
[InlineData(PerformanceOverlayCorner.BottomLeft, false, true)]
|
|
public void MinimalWidthKeepsThePanelAtItsSelectedCorner(
|
|
PerformanceOverlayCorner corner, bool rightAligned, bool bottomAligned)
|
|
{
|
|
var state = new PerformanceOverlayState();
|
|
state.Configure(new HostVideoOptions
|
|
{
|
|
OverlayCorner = corner,
|
|
OverlayMode = PerformanceOverlayMode.Minimal,
|
|
}, false);
|
|
var rectangle = state.GetRectangle(1280, 720);
|
|
Assert.Equal(PerfOverlay.MinimalPanelWidth, state.DisplayWidth);
|
|
Assert.Equal(PerfOverlay.MinimalPanelWidth, rectangle.Width);
|
|
Assert.Equal(rightAligned ? 1280 - 12 - rectangle.Width : 12, rectangle.Left);
|
|
Assert.Equal(bottomAligned ? 720 - 12 - rectangle.Height : 12, rectangle.Top);
|
|
|
|
var clipped = state.GetRectangle(100, 100);
|
|
Assert.Equal(76, clipped.Width);
|
|
Assert.Equal(12, clipped.Left);
|
|
}
|
|
|
|
[Fact]
|
|
public void SharedPixelBufferCanHoldBothPanelSizes()
|
|
{
|
|
Assert.True(PerfOverlay.PixelBufferWidth >= PerfOverlay.PanelWidth);
|
|
Assert.True(PerfOverlay.PixelBufferWidth >= PerfOverlay.MinimalPanelWidth);
|
|
Assert.True(PerfOverlay.PixelBufferHeight >= PerfOverlay.PanelHeight);
|
|
Assert.True(PerfOverlay.PixelBufferHeight >= PerfOverlay.MinimalPanelHeight);
|
|
}
|
|
|
|
[Fact]
|
|
public void InvalidOptionsUseDisplayDefaults()
|
|
{
|
|
var options = new HostVideoOptions
|
|
{
|
|
OverlayCorner = (PerformanceOverlayCorner)100,
|
|
OverlayMode = (PerformanceOverlayMode)100,
|
|
OverlayEnabled = false,
|
|
}.Normalize();
|
|
Assert.Equal(PerformanceOverlayCorner.TopRight, options.OverlayCorner);
|
|
Assert.Equal(PerformanceOverlayMode.Full, options.OverlayMode);
|
|
Assert.False(options.OverlayEnabled);
|
|
}
|
|
|
|
[Fact]
|
|
public void TitleBarKeepsPerformanceAndCaptureHint()
|
|
{
|
|
const string summary = "FPS 60.0 | CPU 10% | GPU 25% | TIME 00:00:10";
|
|
Assert.Equal($"Application · {summary} · Press F12 for capture",
|
|
SdlHostWindow.FormatWindowTitle("Application", true, summary));
|
|
Assert.Equal($"Application · {summary}", SdlHostWindow.FormatWindowTitle("Application", false, summary));
|
|
Assert.Equal("Application", SdlHostWindow.FormatWindowTitle("Application", false, ""));
|
|
}
|
|
|
|
[Fact]
|
|
public void GpuUsageUsesBusiestValidEngine()
|
|
{
|
|
var usage = WindowsGpuUsage.IncludeEngine(double.NaN, 0, 30);
|
|
usage = WindowsGpuUsage.IncludeEngine(usage, 1, 70);
|
|
usage = WindowsGpuUsage.IncludeEngine(usage, 0, 50);
|
|
usage = WindowsGpuUsage.IncludeEngine(usage, 0xC0000BC6, 100);
|
|
usage = WindowsGpuUsage.IncludeEngine(usage, 0, double.NaN);
|
|
usage = WindowsGpuUsage.IncludeEngine(usage, 0, -1);
|
|
Assert.Equal(70, usage);
|
|
Assert.Equal(100, WindowsGpuUsage.IncludeEngine(usage, 0, 105));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(double.NaN, "N/A")]
|
|
[InlineData(double.PositiveInfinity, "N/A")]
|
|
[InlineData(0, "0%")]
|
|
[InlineData(100, "100%")]
|
|
public void MissingGpuCounterDoesNotReportZero(double value, string label)
|
|
{
|
|
Assert.Equal(label, PerfOverlay.FormatUsage(value));
|
|
}
|
|
|
|
[Fact]
|
|
public void GpuSamplerCanCloseWithAQueuedSample()
|
|
{
|
|
var sampler = new WindowsGpuUsage();
|
|
sampler.RequestSample();
|
|
sampler.Dispose();
|
|
sampler.RequestSample();
|
|
sampler.Dispose();
|
|
}
|
|
}
|