IR helper function representation used by the Editor Prism compiler. Defines parameter passing modifiers and a HelperFunction type that stores name, return type, parameters, emitted HLSL/Slang bodies, dependencies, metadata and simple conflict detection.
using Editor.Prism.Core;
namespace Editor.Prism.Compiler.Ir;
/// <summary>How a helper parameter is passed.</summary>
public enum IrParamModifier
{
/// <summary>Passed by value.</summary>
In,
/// <summary>Written by the callee.</summary>
Out,
/// <summary>Read and written by the callee.</summary>
InOut,
/// <summary>Uniform across the draw call.</summary>
Uniform
}
/// <summary>One parameter of a helper or IR function.</summary>
public readonly record struct HelperParam( string Name, ShaderType Type, IrParamModifier Modifier = IrParamModifier.In )
{
/// <summary>HLSL spelling of the parameter, e.g. <c>float3 c</c>.</summary>
public string Hlsl => Modifier switch
{
IrParamModifier.Out => $"out {Type.Hlsl} {Name}",
IrParamModifier.InOut => $"inout {Type.Hlsl} {Name}",
IrParamModifier.Uniform => $"uniform {Type.Hlsl} {Name}",
_ => $"{Type.Hlsl} {Name}"
};
/// <inheritdoc/>
public override string ToString() => Hlsl;
}
/// <summary>
/// A reusable function emitted once per module, carrying a body per backend.
/// <para>
/// Helpers are keyed by <see cref="Name"/> and deduplicated per <see cref="IrModule"/>. A same-name,
/// different-body collision is a hard compile error naming both contributors — unlike the built-in
/// editor's process-global function table, which silently keeps whichever body registered first and
/// lets two unrelated nodes share one implementation forever.
/// </para>
/// </summary>
public sealed class HelperFunction
{
/// <summary>Declare a helper.</summary>
public HelperFunction( string name, ShaderType returnType, IReadOnlyList<HelperParam> parameters )
{
Name = name;
ReturnType = returnType;
Parameters = parameters ?? Array.Empty<HelperParam>();
}
/// <summary>Unique name. Also the dedup key and the emitted function name.</summary>
public string Name { get; }
/// <summary>Return type.</summary>
public ShaderType ReturnType { get; }
/// <summary>Parameters, in order.</summary>
public IReadOnlyList<HelperParam> Parameters { get; }
/// <summary>Complete HLSL definition, including the signature.</summary>
public string Hlsl { get; init; }
/// <summary>Complete Slang definition. Null means "reuse <see cref="Hlsl"/> verbatim".</summary>
public string Slang { get; init; }
/// <summary>Other helpers this one calls. Emitted before it, topologically sorted.</summary>
public IReadOnlyList<HelperFunction> Requires { get; init; } = Array.Empty<HelperFunction>();
/// <summary>Includes this helper needs, added to the module's include set.</summary>
public IReadOnlyList<string> Includes { get; init; } = Array.Empty<string>();
/// <summary>Stages the helper is legal in.</summary>
public StageMask Stages { get; init; } = StageMask.All;
/// <summary>Minimum shader model the body requires.</summary>
public ShaderModel MinShaderModel { get; init; }
/// <summary>Capabilities the body requires.</summary>
public IReadOnlyList<Capability> Capabilities { get; init; } = Array.Empty<Capability>();
/// <summary>False when the helper has side effects or depends on neighbouring lanes.</summary>
public bool Pure { get; init; } = true;
/// <summary>The dedup key. Always the name.</summary>
public string DedupKey => Name;
/// <summary>The body to emit for a given backend id.</summary>
public string BodyFor( string backendId ) =>
backendId == PrismConstants.BackendSlang ? Slang ?? Hlsl : Hlsl;
/// <summary>
/// True when two helpers share a name but not a body — the collision that must be reported as a
/// compile error rather than silently resolved.
/// </summary>
public bool ConflictsWith( HelperFunction other )
{
if ( other is null || ReferenceEquals( this, other ) ) return false;
if ( Name != other.Name ) return false;
return Hlsl != other.Hlsl || Slang != other.Slang;
}
/// <summary>The signature line, without a body, in HLSL.</summary>
public string SignatureHlsl =>
$"{ReturnType.Hlsl} {Name}( {string.Join( ", ", Parameters.Select( x => x.Hlsl ) )} )";
/// <inheritdoc/>
public override string ToString() => SignatureHlsl;
}