using System;
using AcDream.App.Rendering.Gpu.Vk;
namespace AcDream.App.Tests.Rendering.Gpu.Vk;
///
/// Campaign V slice V5 — the parts of the active Vulkan probe that can be
/// checked without a device.
///
/// The probe's submission path needs a driver and is covered by the manual
/// "Vulkan boots to a clear colour" gate. Its verdict — did the
/// readback really contain the colour we cleared to? — is pure, and it is the
/// step most worth pinning: a probe that accepts zeroed memory would report a
/// pass on a device that rendered nothing at all.
///
public sealed class VulkanActiveDeviceProbeTests
{
private static byte[] Filled(byte r, byte g, byte b, byte a)
{
int pixels = (int)(VulkanActiveDeviceProbe.ProbeExtent * VulkanActiveDeviceProbe.ProbeExtent);
var buffer = new byte[pixels * 4];
for (int i = 0; i < buffer.Length; i += 4)
{
buffer[i] = r;
buffer[i + 1] = g;
buffer[i + 2] = b;
buffer[i + 3] = a;
}
return buffer;
}
private static byte[] Expected() => Filled(
VulkanActiveDeviceProbe.ExpectedClearRgba[0],
VulkanActiveDeviceProbe.ExpectedClearRgba[1],
VulkanActiveDeviceProbe.ExpectedClearRgba[2],
VulkanActiveDeviceProbe.ExpectedClearRgba[3]);
///
/// Every channel of the probe colour differs from the others and none is 0
/// or 255, so zeroed or saturated memory cannot accidentally match.
///
[Fact]
public void TheProbeColourCannotBeMatchedByZeroedOrSaturatedMemory()
{
ReadOnlySpan colour = VulkanActiveDeviceProbe.ExpectedClearRgba;
Assert.Equal(4, colour.Length);
foreach (byte channel in colour)
{
Assert.NotEqual(0, channel);
Assert.NotEqual(255, channel);
}
Assert.Throws(
() => VulkanActiveDeviceProbe.VerifyClearColour(Filled(0, 0, 0, 0)));
Assert.Throws(
() => VulkanActiveDeviceProbe.VerifyClearColour(Filled(255, 255, 255, 255)));
}
[Fact]
public void TheExpectedClearColourIsAccepted()
{
VulkanActiveDeviceProbe.VerifyClearColour(Expected());
}
///
/// The clear colour is specified as a float and quantised by the
/// implementation, so one least-significant bit of slack is allowed — and
/// exactly one.
///
[Fact]
public void OneBitOfQuantisationSlackIsAllowedButTwoIsNot()
{
byte[] colour = VulkanActiveDeviceProbe.ExpectedClearRgba.ToArray();
VulkanActiveDeviceProbe.VerifyClearColour(
Filled((byte)(colour[0] + 1), colour[1], colour[2], colour[3]));
VulkanActiveDeviceProbe.VerifyClearColour(
Filled((byte)(colour[0] - 1), colour[1], colour[2], colour[3]));
Assert.Throws(
() => VulkanActiveDeviceProbe.VerifyClearColour(
Filled((byte)(colour[0] + 2), colour[1], colour[2], colour[3])));
}
///
/// A driver that clears only the first tile, or that returns a row-padded
/// copy, must fail here rather than at the V7 differential — which is why
/// every pixel is compared and not a sample.
///
[Fact]
public void ASinglyWrongPixelAnywhereIsRejected()
{
byte[] pixels = Expected();
int lastPixel = pixels.Length - 4;
pixels[lastPixel + 1] ^= 0xFF;
Assert.Throws(
() => VulkanActiveDeviceProbe.VerifyClearColour(pixels));
}
[Fact]
public void AShortReadbackIsRejectedRatherThanPartiallyChecked()
{
Assert.Throws(
() => VulkanActiveDeviceProbe.VerifyClearColour(new byte[64]));
}
}