namespace XFESystem.Core;
public enum RenderAdapterKind { Software, Virtual, Integrated, Discrete }
public enum RenderBackendKind { CpuFramebuffer, VirtIo2D, NativeGpu, Cuda }
public enum RenderExecutionScope { XFESystemGuest, Host }
/// Describes an initialized renderer, not merely an enumerated PCI adapter.
public readonly record struct RenderBackendIdentity(
string Name, RenderAdapterKind Adapter, RenderBackendKind Backend,
RenderExecutionScope Scope, ushort VendorId = 0);
///
/// The probe must initialize its driver/context and verify a real rendering submission
/// and presentation path. Hardware enumeration, EDID and host CUDA DLL presence cannot
/// satisfy this contract. An unavailable probe supplies the actual failure reason.
///
public readonly record struct RenderBackendProbeResult(
RenderBackendIdentity Identity,
bool InitializationPassed,
bool RenderSelfTestPassed,
bool PresentationSelfTestPassed,
bool RasterizationOnGpu,
bool CudaSubmissionVerified,
string Evidence);
///
/// Implemented by the owning runtime/driver, which keeps its resources alive while in use
/// and releases failed or unselected contexts. This interface does not implement a GPU driver.
///
public interface IRenderBackendProbe
{
RenderBackendIdentity Identity { get; }
RenderBackendProbeResult InitializeAndVerify();
}
public readonly record struct RenderBackendAttempt(RenderBackendProbeResult Result, bool Eligible, string Reason);
public sealed record RenderBackendSelectionResult(RenderBackendProbeResult? Selected, RenderBackendAttempt[] Attempts)
{
public bool HasBackend => Selected.HasValue;
public bool GpuAccelerationActive => Selected is { RasterizationOnGpu: true };
public bool CudaAccelerationActive => Selected is { Identity.Backend: RenderBackendKind.Cuda, CudaSubmissionVerified: true };
}
///
/// Prefers a verified discrete GPU, then a verified integrated GPU, then a verified
/// virtual GPU. A working CPU rasterizer remains the fallback. Host renderers are never
/// treated as a native XFESystem renderer unless that execution scope is explicitly selected.
///
public static class RenderBackendSelection
{
public static RenderBackendSelectionResult Select(ReadOnlySpan initialized,
RenderExecutionScope scope = RenderExecutionScope.XFESystemGuest)
{
var attempts = new RenderBackendAttempt[initialized.Length];
RenderBackendProbeResult? selected = null;
int selectedRank = -1;
for (int i = 0; i < initialized.Length; i++)
{
var result = initialized[i];
string reason = RejectionReason(result, scope);
bool eligible = reason.Length == 0;
attempts[i] = new(result, eligible, eligible ? "Initialized render and presentation self-tests passed." : reason);
if (!eligible) continue;
int rank = result.RasterizationOnGpu ? 100 + (int)result.Identity.Adapter : 0;
if (rank > selectedRank) { selected = result; selectedRank = rank; }
}
return new(selected, attempts);
}
public static RenderBackendSelectionResult InitializeAndSelect(ReadOnlySpan probes,
RenderExecutionScope scope = RenderExecutionScope.XFESystemGuest)
{
var initialized = new RenderBackendProbeResult[probes.Length];
for (int i = 0; i < probes.Length; i++)
{
ArgumentNullException.ThrowIfNull(probes[i]);
var identity = probes[i].Identity;
try
{
var result = probes[i].InitializeAndVerify();
initialized[i] = result.Identity == identity ? result : new(identity, false, false, false, false, false,
"The probe returned an identity different from the device being initialized.");
}
catch (Exception exception) when (exception is not OutOfMemoryException)
{
initialized[i] = new(identity, false, false, false, false, false,
exception.GetType().Name + ": " + exception.Message);
}
}
return Select(initialized, scope);
}
private static string RejectionReason(RenderBackendProbeResult result, RenderExecutionScope scope)
{
if (string.IsNullOrWhiteSpace(result.Identity.Name)) return "The renderer has no identity.";
if (result.Identity.Adapter is not (RenderAdapterKind.Software or RenderAdapterKind.Virtual or RenderAdapterKind.Integrated or RenderAdapterKind.Discrete))
return "Unknown adapter kind.";
if (result.Identity.Backend is not (RenderBackendKind.CpuFramebuffer or RenderBackendKind.VirtIo2D or RenderBackendKind.NativeGpu or RenderBackendKind.Cuda))
return "Unknown renderer backend.";
if (result.Identity.Scope != scope) return "The renderer executes in a different environment; host hardware is not a guest backend.";
if (!result.InitializationPassed) return "Driver or context initialization did not pass: " + result.Evidence;
if (!result.RenderSelfTestPassed) return "No successful rendering submission self-test: " + result.Evidence;
if (!result.PresentationSelfTestPassed) return "No successful presentation self-test: " + result.Evidence;
if (string.IsNullOrWhiteSpace(result.Evidence)) return "The self-tests have no evidence.";
if (result.Identity.Backend is RenderBackendKind.CpuFramebuffer or RenderBackendKind.VirtIo2D)
{
if (result.RasterizationOnGpu || result.CudaSubmissionVerified)
return "CPU framebuffer and VirtIO 2D scanout do not execute a GPU rasterizer or CUDA submission.";
}
else if (!result.RasterizationOnGpu) return "The GPU backend has no verified GPU rasterization path.";
if (result.RasterizationOnGpu && result.Identity.Adapter == RenderAdapterKind.Software)
return "A software adapter cannot report GPU rasterization.";
if (result.Identity.Backend == RenderBackendKind.Cuda)
{
if (result.Identity.VendorId != 0x10DE) return "CUDA requires an NVIDIA adapter.";
if (!result.CudaSubmissionVerified) return "No CUDA kernel submission and result verification were completed.";
}
else if (result.CudaSubmissionVerified) return "CUDA evidence was attached to a non-CUDA backend.";
return string.Empty;
}
}