Editor/Prism/Compiler/Ir/GlobalDecl.cs

IR types for material/global declarations. Defines enums for GlobalKind and UiControl, a UiHints record for editor metadata, and the GlobalDecl record representing a module-level shader/global parameter with properties and simple helpers.

Reflection
using Editor.Prism.Core;

namespace Editor.Prism.Compiler.Ir;

/// <summary>What kind of module-level declaration this is.</summary>
public enum GlobalKind
{
	/// <summary>A scalar, vector or matrix uniform.</summary>
	Uniform,
	/// <summary>A texture.</summary>
	Texture,
	/// <summary>A sampler.</summary>
	Sampler,
	/// <summary>A read-only buffer.</summary>
	Buffer,
	/// <summary>A writable texture. Compute only.</summary>
	RwTexture,
	/// <summary>A writable buffer. Compute only.</summary>
	RwBuffer,
	/// <summary>A compile-time constant, emitted as <c>static const</c>.</summary>
	Constant
}

/// <summary>Which inline editor the material UI should show for a parameter.</summary>
public enum UiControl
{
	/// <summary>Pick from the type.</summary>
	Default,
	/// <summary>Numeric slider with a range.</summary>
	Slider,
	/// <summary>Colour swatch.</summary>
	Color,
	/// <summary>Checkbox.</summary>
	Toggle,
	/// <summary>Enumeration dropdown.</summary>
	Dropdown,
	/// <summary>Component-wise vector fields.</summary>
	Vector,
	/// <summary>Texture picker.</summary>
	Texture
}

/// <summary>
/// Material-UI metadata for a parameter. Lowered into the VFX annotation grammar
/// (<c>UiType( Slider ); Default( 0.62 ); Range( 0, 1 ); UiGroup( "Surface,10/20" );</c>) by the
/// HLSL backend, and into Slang user-defined attributes by the Slang backend.
/// </summary>
public sealed record UiHints
{
	/// <summary>Which editor to show.</summary>
	public UiControl Control { get; init; }

	/// <summary>Slider minimum.</summary>
	public float Min { get; init; }

	/// <summary>Slider maximum.</summary>
	public float Max { get; init; } = 1f;

	/// <summary>Slider step. Zero means continuous.</summary>
	public float Step { get; init; }

	/// <summary>Group heading in the material editor.</summary>
	public string Group { get; init; }

	/// <summary>Sort order within the group.</summary>
	public int Order { get; init; }

	/// <summary>Tooltip text.</summary>
	public string Tooltip { get; init; }

	/// <summary>Values offered by a dropdown.</summary>
	public IReadOnlyList<string> Options { get; init; }
}

/// <summary>
/// A module-level declaration: a uniform, texture, sampler or buffer, plus everything the material
/// UI and the preview attribute bus need to bind to it.
/// <para>
/// Globals are deduplicated by <see cref="Name"/> when added to an <see cref="IrModule"/>; two
/// different declarations claiming the same name is a compile error, not a silent overwrite.
/// </para>
/// </summary>
public sealed record GlobalDecl( string Name, ShaderType Type, GlobalKind Kind )
{
	/// <summary>The blackboard parameter this global was generated from, when there is one.</summary>
	public ParamId Parameter { get; init; }

	/// <summary>
	/// Render-attribute name, so the value can be pushed at runtime without a recompile.
	/// In preview mode every literal gets one of these; that is what makes slider drags free.
	/// </summary>
	public string AttributeName { get; init; }

	/// <summary>Numeric default value.</summary>
	public ConstValue? Default { get; init; }

	/// <summary>Asset path default, for textures.</summary>
	public string DefaultAsset { get; init; }

	/// <summary>True when a texture's contents are sRGB encoded.</summary>
	public bool Srgb { get; init; }

	/// <summary>Name of the sampler paired with this texture, when the backend declares one.</summary>
	public string SamplerName { get; init; }

	/// <summary>Material UI metadata.</summary>
	public UiHints Ui { get; init; }

	/// <summary>Array length, or zero for a non-array declaration.</summary>
	public int ArraySize { get; init; }

	/// <summary>True when this global exists only for preview and is stripped from the final shader.</summary>
	public bool PreviewOnly { get; init; }

	/// <summary>Stages that read this global. Used to place it in the right block.</summary>
	public StageMask Stages { get; init; } = StageMask.All;

	/// <summary>True when the declaration is an opaque resource rather than a numeric uniform.</summary>
	public bool IsResource => Kind is not ( GlobalKind.Uniform or GlobalKind.Constant );

	/// <summary>
	/// True when two declarations share a name but disagree about anything that matters —
	/// the collision that must be reported instead of silently resolved.
	/// </summary>
	public bool ConflictsWith( GlobalDecl other )
	{
		if ( other is null || ReferenceEquals( this, other ) ) return false;
		if ( Name != other.Name ) return false;

		return Type != other.Type || Kind != other.Kind || ArraySize != other.ArraySize;
	}

	/// <inheritdoc/>
	public override string ToString() => $"{Kind} {Type.Hlsl} {Name}";
}