Editor/Prism/Compiler/Ir/IrType.cs

IR type and related enums used by the Prism compiler. Defines IrPrecision and IrInterpolation enums and the IrType record struct which pairs a ShaderType with a precision hint and exposes convenience properties and common predefined instances.

using Editor.Prism.Core;

namespace Editor.Prism.Compiler.Ir;

/// <summary>Precision hint carried by an IR type. Ignored by backends that cannot express it.</summary>
public enum IrPrecision
{
	/// <summary>Whatever the component type implies.</summary>
	Default,
	/// <summary>Minimum 16-bit precision (<c>min16float</c> and friends). Parses on SM 6.0.</summary>
	Min16,
	/// <summary>Force full precision.</summary>
	Full
}

/// <summary>How a value is interpolated across a triangle when it travels through a varying.</summary>
public enum IrInterpolation
{
	/// <summary>Perspective-correct linear interpolation. The default.</summary>
	Linear,
	/// <summary>Linear interpolation without perspective correction.</summary>
	NoPerspective,
	/// <summary>No interpolation; the provoking vertex's value is used.</summary>
	NoInterpolation,
	/// <summary>Interpolated at the pixel centroid.</summary>
	Centroid,
	/// <summary>Interpolated per sample.</summary>
	Sample
}

/// <summary>
/// A fully-resolved IR type: a concrete <see cref="ShaderType"/> plus lowering hints. By the time
/// anything reaches the IR, every type variable has been solved away — an <see cref="IrType"/> that
/// is not <see cref="IsResolved"/> is a compiler bug, not a user error.
/// </summary>
public readonly record struct IrType( ShaderType Type, IrPrecision Precision )
{
	/// <summary>Wrap a shader type with default precision.</summary>
	public IrType( ShaderType type ) : this( type, IrPrecision.Default ) { }

	/// <summary>void</summary>
	public static readonly IrType Void = new( ShaderType.Void );
	/// <summary>bool</summary>
	public static readonly IrType Bool = new( ShaderType.Bool );
	/// <summary>int</summary>
	public static readonly IrType Int = new( ShaderType.Int );
	/// <summary>uint</summary>
	public static readonly IrType UInt = new( ShaderType.UInt );
	/// <summary>float</summary>
	public static readonly IrType Float = new( ShaderType.Float );
	/// <summary>float2</summary>
	public static readonly IrType Float2 = new( ShaderType.Float2 );
	/// <summary>float3</summary>
	public static readonly IrType Float3 = new( ShaderType.Float3 );
	/// <summary>float4</summary>
	public static readonly IrType Float4 = new( ShaderType.Float4 );
	/// <summary>float4x4</summary>
	public static readonly IrType Float4x4 = new( ShaderType.Float4x4 );

	/// <summary>Wrap a shader type.</summary>
	public static IrType Of( ShaderType type ) => new( type );

	/// <summary>True when the underlying type is concrete.</summary>
	public bool IsResolved => !Type.IsVoid;

	/// <summary>Component count of the underlying type.</summary>
	public int Components => Type.Components;

	/// <summary>HLSL spelling, including the precision prefix when one applies.</summary>
	public string Hlsl => Precision == IrPrecision.Min16 && Type.IsNumeric && Type.IsFloatingPoint
		? "min16" + Type.Hlsl
		: Type.Hlsl;

	/// <summary>Slang spelling.</summary>
	public string Slang => Type.Slang;

	/// <summary>Implicitly widen a shader type into an IR type.</summary>
	public static implicit operator IrType( ShaderType type ) => new( type );

	/// <summary>Implicitly narrow an IR type back to its shader type.</summary>
	public static implicit operator ShaderType( IrType type ) => type.Type;

	/// <inheritdoc/>
	public override string ToString() => Hlsl;
}