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; } }