namespace AcDream.App.Rendering.Gpu;
///
/// Backend-neutral view of what the device can do. Both backends fill this from
/// their own probe layer — GraphicalCapabilityRecord for GL, and its
/// Vulkan sibling added at slice V5 — and the renderers consult only this.
///
/// The alignment fields are load-bearing rather than informational: ring
/// allocations must satisfy them, and getting one wrong produces a driver error
/// on Vulkan and silently wrong data on some GL implementations.
///
internal sealed record GpuCapabilityRecord
{
public required GpuBackendKind Backend { get; init; }
/// Adapter name, e.g. "AMD Radeon RX 9070 XT".
public required string DeviceName { get; init; }
/// Driver identification string for diagnostics and bug reports.
public required string DriverInfo { get; init; }
/// API version actually in use, e.g. "OpenGL 4.6" or "Vulkan 1.3.280".
public required string ApiVersion { get; init; }
/// Simultaneously registerable texture-table slots. Must reach .
public required uint MaxTextureTableSlots { get; init; }
/// Storage-buffer bindings available. Must reach .
public required uint MaxStorageBufferBindings { get; init; }
/// Push-constant bytes available. Must reach .
public required uint MaxPushConstantBytes { get; init; }
/// Required alignment for a storage-buffer binding offset.
public required uint MinStorageBufferOffsetAlignment { get; init; }
/// Required alignment for a uniform-buffer binding offset.
public required uint MinUniformBufferOffsetAlignment { get; init; }
/// Clip distances usable by a shader. Phase U.3's per-cell clip gate needs 8.
public required uint MaxClipDistances { get; init; }
/// Highest supported multisample count for the backbuffer.
public required uint MaxSampleCount { get; init; }
/// Multi-draw-indirect. Mandatory — it is the entire draw architecture.
public required bool SupportsMultiDrawIndirect { get; init; }
/// Shader draw parameters (gl_DrawID). Mandatory — batch lookup depends on it.
public required bool SupportsDrawParameters { get; init; }
/// BC1/2/3 sampling. Mandatory — DAT surfaces upload as DXT without transcoding.
public required bool SupportsTextureCompressionBc { get; init; }
/// GPU timestamps. Optional: absence degrades profiling, not rendering.
public required bool SupportsTimestampQueries { get; init; }
///
/// Whether per-frame data can be written straight into mapped memory the GPU
/// reads. Both backends report this; only Vulkan currently answers true, and
/// it is the mechanism behind Campaign V's CPU-cost target.
///
public required bool SupportsPersistentlyMappedRings { get; init; }
///
/// Every mandatory capability this device fails to provide, phrased as
/// operator-facing sentences. Empty means the device can run acdream.
/// Startup turns a non-empty list into the same NotSupportedException
/// and exit-code-4 contract the GL gate already publishes.
///
public IReadOnlyList SupportFailures
{
get
{
List failures = [];
if (!SupportsMultiDrawIndirect)
failures.Add("Multi-draw-indirect is required to submit world geometry.");
if (!SupportsDrawParameters)
failures.Add("Shader draw parameters (gl_DrawID) are required to select per-draw batch data.");
if (!SupportsTextureCompressionBc)
failures.Add("BC (DXT) texture compression is required to upload DAT surfaces.");
if (MaxTextureTableSlots < GpuBindingModel.TextureTableCapacity)
{
failures.Add(
$"The texture table needs {GpuBindingModel.TextureTableCapacity} slots; " +
$"this device provides {MaxTextureTableSlots}.");
}
if (MaxStorageBufferBindings < GpuBindingModel.StorageBindingCount)
{
failures.Add(
$"{GpuBindingModel.StorageBindingCount} storage-buffer bindings are required; " +
$"this device provides {MaxStorageBufferBindings}.");
}
if (MaxPushConstantBytes < GpuBindingModel.PushConstantBytes)
{
failures.Add(
$"{GpuBindingModel.PushConstantBytes} push-constant bytes are required; " +
$"this device provides {MaxPushConstantBytes}.");
}
if (MaxClipDistances < GpuBindingModel.ClipPlanesPerSlot)
{
failures.Add(
$"{GpuBindingModel.ClipPlanesPerSlot} clip distances are required by the per-cell clip gate; " +
$"this device provides {MaxClipDistances}.");
}
return failures;
}
}
public bool IsSupported => SupportFailures.Count == 0;
}