Editor/Prism/Core/ShaderStage.cs

Editor-side type definitions and helpers for Prism shader stages and related enums. Declares ShaderStage, StageMask, StageMask helper methods, and enums for ShaderDomain, ShadingModel, SurfaceBlendMode, and CullMode, plus utility methods that map stages to entry points, block names, symbols, display names and capabilities.

namespace Editor.Prism.Core;

/// <summary>
/// A programmable stage Prism can emit. Hull and domain shaders are deliberately absent:
/// the engine's <c>.shader</c> parser throws <c>"HS does nothing!"</c> / <c>"DS does nothing!"</c>.
/// </summary>
public enum ShaderStage
{
	/// <summary>No stage. Used for stage-invariant values before the planner assigns one.</summary>
	None,
	/// <summary>Vertex shader, entry point <c>MainVs</c>.</summary>
	Vertex,
	/// <summary>Pixel shader, entry point <c>MainPs</c>.</summary>
	Pixel,
	/// <summary>Geometry shader, entry point <c>MainGs</c>.</summary>
	Geometry,
	/// <summary>Compute shader, entry point <c>MainCs</c>.</summary>
	Compute
}

/// <summary>A set of <see cref="ShaderStage"/> values.</summary>
[Flags]
public enum StageMask
{
	/// <summary>No stages.</summary>
	None = 0,
	/// <summary>Vertex shader.</summary>
	Vertex = 1 << 0,
	/// <summary>Pixel shader.</summary>
	Pixel = 1 << 1,
	/// <summary>Geometry shader.</summary>
	Geometry = 1 << 2,
	/// <summary>Compute shader.</summary>
	Compute = 1 << 3,

	/// <summary>The two stages every surface shader uses.</summary>
	VertexPixel = Vertex | Pixel,
	/// <summary>Everything in the rasterisation pipeline.</summary>
	Graphics = Vertex | Pixel | Geometry,
	/// <summary>Every stage Prism can emit.</summary>
	All = Vertex | Pixel | Geometry | Compute
}

/// <summary>Stage helpers: masks, entry point names, VFX block names and preprocessor symbols.</summary>
public static class ShaderStages
{
	/// <summary>Every real stage, in emission order.</summary>
	public static readonly ShaderStage[] All =
	[
		ShaderStage.Vertex, ShaderStage.Pixel, ShaderStage.Geometry, ShaderStage.Compute
	];

	/// <summary>The single-bit mask for a stage.</summary>
	public static StageMask ToMask( this ShaderStage stage ) => stage switch
	{
		ShaderStage.Vertex => StageMask.Vertex,
		ShaderStage.Pixel => StageMask.Pixel,
		ShaderStage.Geometry => StageMask.Geometry,
		ShaderStage.Compute => StageMask.Compute,
		_ => StageMask.None
	};

	/// <summary>True when <paramref name="mask"/> includes <paramref name="stage"/>.</summary>
	public static bool Contains( this StageMask mask, ShaderStage stage ) => ( mask & stage.ToMask() ) != 0;

	/// <summary>Enumerate the stages present in a mask, in emission order.</summary>
	public static IEnumerable<ShaderStage> Stages( this StageMask mask )
	{
		foreach ( var stage in All )
		{
			if ( mask.Contains( stage ) ) yield return stage;
		}
	}

	/// <summary>The fixed entry point name the engine expects for this stage.</summary>
	public static string EntryPoint( this ShaderStage stage ) => stage switch
	{
		ShaderStage.Vertex => PrismConstants.EntryPointVertex,
		ShaderStage.Pixel => PrismConstants.EntryPointPixel,
		ShaderStage.Geometry => PrismConstants.EntryPointGeometry,
		ShaderStage.Compute => PrismConstants.EntryPointCompute,
		_ => null
	};

	/// <summary>The VFX block keyword that wraps this stage in a <c>.shader</c> file.</summary>
	public static string BlockName( this ShaderStage stage ) => stage switch
	{
		ShaderStage.Vertex => "VS",
		ShaderStage.Pixel => "PS",
		ShaderStage.Geometry => "GS",
		ShaderStage.Compute => "CS",
		_ => null
	};

	/// <summary>The <c>system.fxc</c> <c>PROGRAM</c> symbol for this stage.</summary>
	public static string ProgramSymbol( this ShaderStage stage ) => stage switch
	{
		ShaderStage.Vertex => "VFX_PROGRAM_VS",
		ShaderStage.Pixel => "VFX_PROGRAM_PS",
		ShaderStage.Geometry => "VFX_PROGRAM_GS",
		ShaderStage.Compute => "VFX_PROGRAM_CS",
		_ => null
	};

	/// <summary>The Slang <c>[shader("...")]</c> stage string.</summary>
	public static string SlangStage( this ShaderStage stage ) => stage switch
	{
		ShaderStage.Vertex => "vertex",
		ShaderStage.Pixel => "pixel",
		ShaderStage.Geometry => "geometry",
		ShaderStage.Compute => "compute",
		_ => null
	};

	/// <summary>Short display name for status bars and diagnostics.</summary>
	public static string DisplayName( this ShaderStage stage ) => stage switch
	{
		ShaderStage.Vertex => "Vertex",
		ShaderStage.Pixel => "Pixel",
		ShaderStage.Geometry => "Geometry",
		ShaderStage.Compute => "Compute",
		_ => "None"
	};

	/// <summary>True when screen-space derivatives (and therefore implicit-LOD sampling) exist in this stage.</summary>
	public static bool HasDerivatives( this ShaderStage stage ) => stage == ShaderStage.Pixel;

	/// <summary>True when the stage can discard a fragment.</summary>
	public static bool CanDiscard( this ShaderStage stage ) => stage == ShaderStage.Pixel;
}

/// <summary>What the graph is for. Drives which output node is valid and how the shader is applied.</summary>
public enum ShaderDomain
{
	/// <summary>A material applied to geometry. The default.</summary>
	Surface,
	/// <summary>A full-screen effect blitted over the frame.</summary>
	PostProcess,
	/// <summary>A compute kernel.</summary>
	Compute,
	/// <summary>A reusable subgraph; has no entry point of its own.</summary>
	Subgraph
}

/// <summary>How a surface graph resolves to pixels.</summary>
public enum ShadingModel
{
	/// <summary>Routed through <c>ShadingModelStandard::Shade</c>, giving depth prepass and G-buffer for free.</summary>
	Lit,
	/// <summary>Returns colour directly with no lighting.</summary>
	Unlit,
	/// <summary>The graph produces the final <c>float4</c> itself and Prism emits no shading call.</summary>
	Custom
}

/// <summary>
/// Output blending. Written as <c>S_TRANSLUCENT</c> / <c>S_ALPHA_TEST</c> defines which must appear
/// BEFORE <c>common/pixel.hlsl</c> is included, or the render state silently comes out opaque.
/// <para>Named <c>SurfaceBlendMode</c> because <c>Sandbox.BlendMode</c> already exists and is globally imported.</para>
/// </summary>
public enum SurfaceBlendMode
{
	/// <summary>No blending.</summary>
	Opaque,
	/// <summary>Alpha tested / cutout.</summary>
	Masked,
	/// <summary>Standard source-alpha translucency.</summary>
	Translucent,
	/// <summary>Additive translucency.</summary>
	Additive,
	/// <summary>Multiplicative translucency.</summary>
	Multiply
}

/// <summary>Triangle culling for the generated render state.</summary>
public enum CullMode
{
	/// <summary>Cull back faces. The default.</summary>
	Back,
	/// <summary>Cull front faces.</summary>
	Front,
	/// <summary>Draw both sides.</summary>
	None
}