Files
UShaderLab/Source/UShaderLabBuilder/Public/ShaderLabIntrinsicRegistry.h
2026-07-05 11:26:32 +08:00

109 lines
4.4 KiB
C++

// Copyright UShaderLab. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
class UMaterial;
class UMaterialExpression;
class UEnum;
/** Shader stage an intrinsic is valid in (used to reject e.g. PixelDepth inside a Vertex body). */
enum class EShaderLabIntrinsicFrequency : uint8
{
Any,
PixelOnly,
VertexOnly,
};
/** One formal parameter of an intrinsic, used only to generate the IDE authoring stub signature. */
struct FShaderLabIntrinsicParam
{
/** HLSL type of the parameter, e.g. "int", "float". */
FString Type;
/** Parameter name shown in completion, e.g. "Index". */
FString Name;
/** Default-value literal for the stub (e.g. "1"); empty means the parameter is required. */
FString DefaultLiteral;
/**
* When non-null, this parameter is a reflected enum passed by token name (e.g.
* `UE_ViewProperty(MEVP_FieldOfView)`). The stub generator emits the enum from it and types the
* parameter as it; the arg validator accepts only its token names. Points at a process-permanent
* native `UEnum` (`StaticEnum<T>()`), so a raw pointer is safe. `Type` is ignored when this is set.
*/
const UEnum* Enum = nullptr;
};
/**
* Describes one `UE_NodeName(...)` intrinsic: how to build the backing material expression node,
* which of its outputs to read, and what it requires of the material. The Custom-node input type is
* derived by the engine from the connected node's real output type, so no output type is declared here.
*
* The ReturnType/Params/Doc fields carry no runtime meaning — they exist so `ShaderLab.IDE.Prepare`
* can emit `UE_Name(...)` HLSL stub declarations from this single source of truth, giving the IDE
* member/signature completion for `UE_` calls without any drift from the real registration.
*/
struct FShaderLabIntrinsicDesc
{
FName Name;
/** Which output pin of the created node to connect (covers multi-output nodes like ObjectLocalBounds). */
int32 OutputIndex = 0;
/** Stage restriction; misuse is a build error. */
EShaderLabIntrinsicFrequency Frequency = EShaderLabIntrinsicFrequency::Any;
/**
* When true, this name is overloaded: the nullary form `UE_Name()` is this wired intrinsic, while
* `UE_Name(<args>)` is a raw-HLSL helper of the same name (see UEFunctions/*.ush). A call carrying more
* arguments than `Params` declares is the helper overload — the parser leaves it verbatim in the body
* for the shader compiler instead of rejecting it. Used e.g. by UE_DistanceToNearestSurface (nullary =
* current pixel; `(worldPos)` = distance-field lookup at an arbitrary position, whose global-distance-
* field binding is supplied by an anchor capability). Default false keeps the strict const-arg contract.
*/
bool bAllowRawHelperWithArgs = false;
/** HLSL return type for the authoring stub, e.g. "float2"/"float3"/"float". Required (see Register). */
FString ReturnType;
/** Formal parameters for the authoring stub (empty for the common no-arg intrinsics). */
TArray<FShaderLabIntrinsicParam> Params;
/** Optional one-line doc surfaced as a comment above the stub. */
FString Doc;
/**
* Create the backing expression node and add it to `Material`'s expression collection. `ConstArgs`
* are the literal arguments parsed from the call (e.g. {"0","2.0"} for `UE_TextureCoordinate(0,2.0)`).
* Return nullptr and set `OutError` on a bad argument. Contract: this only runs in the editor/cook
* (graphs are editor-only); the cooked runtime never builds a graph.
*/
TFunction<UMaterialExpression*(UMaterial& /*Material*/, const TArray<FString>& /*ConstArgs*/, FString& /*OutError*/)> MakeNode;
};
/**
* Open registry mapping intrinsic names to descriptors. ShaderLab seeds its builtins on first use;
* other editor modules can register intrinsics for their own custom UMaterialExpression nodes from
* their StartupModule via Get().Register(...).
*/
class USHADERLABBUILDER_API FShaderLabIntrinsicRegistry
{
public:
static FShaderLabIntrinsicRegistry& Get();
/** Register (or override) an intrinsic. */
void Register(FShaderLabIntrinsicDesc Desc);
/** Lookup by name; nullptr if unknown. */
const FShaderLabIntrinsicDesc* Find(FName Name) const;
/** Visit every registered intrinsic (used by ShaderLab.IDE.Prepare to generate the UE_ stub header). */
void ForEach(TFunctionRef<void(const FShaderLabIntrinsicDesc&)> Fn) const;
private:
FShaderLabIntrinsicRegistry();
void RegisterBuiltins();
TMap<FName, FShaderLabIntrinsicDesc> Descs;
};