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

99 lines
4.3 KiB
C++

// Copyright UShaderLab. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
class UMaterial;
class UMaterialExpression;
struct FShaderLabModel;
/**
* One body of HLSL scanned for capability triggers: the verbatim text plus its 1-based `.usl` source line
* (so a capability can map a contract violation back to the exact authored line). Covers a shader's own
* pixel/vertex bodies AND every reachable `.uslfunc` library-function body, since a library function called
* from a body is inlined into the same Custom node and can equally reference a raw scene-texture helper.
*/
struct FShaderLabScannedBody
{
/** Non-owning pointer to the body text (lives in the FShaderLabModel / resolved program). */
const FString* Text = nullptr;
/** 1-based source line of the body's first character, for error line-mapping. */
int32 SourceLine = 0;
};
/** Everything a capability needs to detect usage, validate it, and create its hidden anchor node(s). */
struct FShaderLabAnchorCapabilityContext
{
/** The material being built (target of MakeNode). */
UMaterial& Material;
/** The parsed shader model (domain / blend mode live here for per-id domain validation). */
const FShaderLabModel& Model;
/** Shader bodies + reachable library-function bodies to scan. */
const TArray<FShaderLabScannedBody>& Bodies;
/** The shader's `.usl` absolute path (`#line` directive target for mapped errors). */
const FString& SourcePath;
/**
* Create a material expression of `Class`, add it to the material's expression collection, and lay it
* out on the canvas — the capability then sets any type-specific fields (e.g. SceneTextureId) on the
* CastChecked<> result. Mirrors the builder's internal NewExpr helper.
*/
TFunction<UMaterialExpression*(UClass* /*Class*/)> MakeNode;
};
/**
* A "capability" that a raw-HLSL body helper depends on: the helper (e.g. `SceneTextureLookup`,
* `GetDistanceToNearestSurfaceGlobal`) compiles fine as bare HLSL inside a Custom node, but the runtime
* resource binding it needs is only established as a side effect of compiling a matching UMaterialExpression
* (via `UseSceneTextureId` / `bUsesGlobalDistanceField` / ...). Because the Custom body bypasses that node,
* we synthesize a hidden expression, wired into the before-attributes ParameterAnchor, purely for the
* side effect. This registry is the general mechanism; each dynamic-input helper family registers one entry.
*
* (Helpers with NO dynamic body input — e.g. EyeAdaptation — do not need this: they are exposed as ordinary
* `UE_` wired intrinsics whose real node is connected into the Custom input, so its Compile side effect fires
* naturally. This mechanism is only for helpers that take a body-computed argument, which cannot be fed into
* a node from inside the opaque Custom body.)
*/
struct FShaderLabAnchorCapability
{
/** Identifier for diagnostics / tests (e.g. "SceneTexture", "GlobalDistanceField"). */
FName Id;
/**
* HLSL tokens whose presence in any scanned body activates this capability (e.g. "UE_SceneTexture",
* "UE_SceneDepth"). Matched delimited by non-identifier characters.
*/
TArray<FString> TriggerTokens;
/**
* Emit the hidden before-attributes node(s) for this capability into OutNodes. Runs only when at least
* one TriggerToken is present. Returns false and appends `.usl`-mapped messages to OutErrors on a
* contract violation (e.g. a non-literal id) or an illegal domain — contract style, aborting the build.
*/
TFunction<bool(const FShaderLabAnchorCapabilityContext& /*Ctx*/,
TArray<UMaterialExpression*>& /*OutNodes*/, TArray<FString>& /*OutErrors*/)> Emit;
};
/**
* Open registry of anchor side-effect capabilities. ShaderLab seeds its builtins (SceneTexture, Global
* Distance Field) on first use; other editor modules may register their own from their StartupModule via
* Get().Register(...). The graph builder queries it once per build.
*/
class USHADERLABBUILDER_API FShaderLabAnchorCapabilityRegistry
{
public:
static FShaderLabAnchorCapabilityRegistry& Get();
/** Register (or override, by Id) a capability. */
void Register(FShaderLabAnchorCapability Capability);
/** Visit every registered capability. */
void ForEach(TFunctionRef<void(const FShaderLabAnchorCapability&)> Fn) const;
private:
FShaderLabAnchorCapabilityRegistry();
void RegisterBuiltins();
TArray<FShaderLabAnchorCapability> Capabilities;
};