sharpemu/tests/SharpEmu.Libs.Tests/Gpu/Vulkan/VulkanCommandProfileTests.cs

124 lines
5.2 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using SharpEmu.Libs.Gpu.Scheduling;
using SharpEmu.Libs.Gpu.Vulkan;
using SharpEmu.Libs.Tests.Gpu.Images;
using SharpEmu.Libs.Tests.Gpu.Scheduling;
using Silk.NET.Vulkan;
using Xunit;
namespace SharpEmu.Libs.Tests.Gpu.Vulkan;
[Collection(SchedulingStateCollection.Name)]
public sealed class VulkanCommandProfileTests(HeadlessVulkanFixture fixture) : IClassFixture<HeadlessVulkanFixture>
{
[Theory]
[InlineData(100UL, 150UL, 64U, 50UL)]
[InlineData(ulong.MaxValue - 3, 2UL, 64U, 6UL)]
[InlineData(254UL, 3UL, 8U, 5UL)]
[InlineData(0x1FEUL, 0x203UL, 8U, 5UL)]
public void ElapsedTicksHandlesCounterWrap(ulong start, ulong end, uint validBits, ulong expected)
{
Assert.Equal(expected, VulkanCommandProfile.ElapsedTicks(start, end, validBits));
}
[Fact]
public void OverlappingSubmissionsCountOnceTowardsBusyTime()
{
var spans = new List<(ulong Start, ulong End)> { (100, 150), (0, 40), (140, 200), (30, 60), (300, 310) };
Assert.Equal((170ul, 60ul), VulkanCommandProfile.MergeSpans(spans));
}
[Fact]
public void DefaultDeviceDoesNotCreateQueryResources()
{
if (!GatePrerequisites.Ready(fixture.Vulkan)) return;
using (var device = fixture.Vulkan.NewTickDevice())
Assert.Null(device.CommandProfile);
fixture.Vulkan.AssertNoValidationMessages();
}
[Fact]
public void CompletedBufferReuseCollectsQueriesAndPreservesTheCapacityLimit()
{
var vulkan = fixture.Vulkan;
if (!GatePrerequisites.Ready(vulkan)) return;
var device = new VulkanTickDevice(vulkan.Vk, vulkan.Device, vulkan.Queue, vulkan.QueueFamily, vulkan.QueueGate, vulkan.Physical);
var profile = Assert.IsType<VulkanCommandProfile>(device.CommandProfile);
using (device)
{
if (!profile.Supported) return;
profile.PerCommandMarkers = true;
var buffer = device.AllocateBuffers(1)[0];
var command = new CommandBuffer(buffer);
device.BeginBuffer(buffer);
for (var index = 0; index < VulkanCommandProfile.QueryCapacity * 2; index++)
profile.WriteMarker(command, VulkanCommandProfile.IntervalKind.Preparation);
device.EndBuffer(buffer);
Assert.Equal(0UL, profile.CollectedIntervals);
SubmitAndWait(device, buffer, 1);
device.BeginBuffer(buffer);
Assert.Equal((ulong)VulkanCommandProfile.QueryCapacity - 1, profile.CollectedIntervals);
Assert.Equal(1, profile.ProfiledBufferCount);
device.EndBuffer(buffer);
SubmitAndWait(device, buffer, 2);
}
Assert.Equal((ulong)VulkanCommandProfile.QueryCapacity, profile.CollectedIntervals);
vulkan.AssertNoValidationMessages();
}
[Fact]
public unsafe void MarkersInsideDynamicRenderingPassValidation()
{
var vulkan = fixture.Vulkan;
if (!GatePrerequisites.Ready(vulkan) || !vulkan.SupportsDynamicRendering || vulkan.ApiVersion < Vk.Version13) return;
using (var device = new VulkanTickDevice(vulkan.Vk, vulkan.Device, vulkan.Queue, vulkan.QueueFamily, vulkan.QueueGate, vulkan.Physical))
{
var buffer = device.AllocateBuffers(1)[0];
var command = new CommandBuffer(buffer);
device.BeginBuffer(buffer);
var rendering = new RenderingInfo
{
SType = StructureType.RenderingInfo,
RenderArea = new Rect2D(default, new Extent2D(1, 1)),
LayerCount = 1,
};
vulkan.Vk.CmdBeginRendering(command, &rendering);
device.CommandProfile!.PerCommandMarkers = true;
device.CommandProfile.WriteMarker(command, VulkanCommandProfile.IntervalKind.Preparation);
device.CommandProfile.WriteMarker(command, VulkanCommandProfile.IntervalKind.Draw, 1, 3, 1);
vulkan.Vk.CmdEndRendering(command);
device.EndBuffer(buffer);
SubmitAndWait(device, buffer, 1);
}
vulkan.AssertNoValidationMessages();
}
[Fact]
public void UnsubmittedQueriesAreNotCollected()
{
var vulkan = fixture.Vulkan;
if (!GatePrerequisites.Ready(vulkan)) return;
var device = new VulkanTickDevice(vulkan.Vk, vulkan.Device, vulkan.Queue, vulkan.QueueFamily, vulkan.QueueGate, vulkan.Physical);
var profile = Assert.IsType<VulkanCommandProfile>(device.CommandProfile);
using (device)
{
var buffer = device.AllocateBuffers(1)[0];
device.BeginBuffer(buffer);
device.EndBuffer(buffer);
}
Assert.Equal(0UL, profile.CollectedIntervals);
vulkan.AssertNoValidationMessages();
}
private static void SubmitAndWait(VulkanTickDevice device, nint buffer, ulong tick)
{
var submission = new SubmitBundle();
submission.AddSignal(device.TimelineHandle, tick);
lock (device.QueueGate)
Assert.True(device.TrySubmit(buffer, submission, out var submitFailure), submitFailure);
Assert.True(device.TryWaitTimeline(tick, out var waitFailure), waitFailure);
}
}