mirror of
https://github.com/Eragon-Brisingr/UShaderLab.git
synced 2026-09-15 23:04:37 +00:00
Fix SceneTexture reference data declare.
This commit is contained in:
@@ -33,6 +33,7 @@
|
||||
#include "Materials/MaterialExpressionVertexInterpolator.h"
|
||||
#include "MaterialExpressionShaderLabParameterAnchor.h"
|
||||
#include "ShaderLabIntrinsicRegistry.h"
|
||||
#include "ShaderLabAnchorCapabilityRegistry.h"
|
||||
#include "ShaderLabImportResolver.h"
|
||||
#include "ShaderLabRuntimeBuilder.h"
|
||||
#include "UObject/Class.h"
|
||||
@@ -667,7 +668,15 @@ namespace ShaderLabGraph
|
||||
// Copy just the `UE_Name` identifier and keep scanning from the '(' — so any
|
||||
// intrinsic nested in the arguments (e.g. UE_Noise(UE_WorldPosition(), ...)) still
|
||||
// gets rewritten.
|
||||
if (!Registry.Find(FName(*Name)))
|
||||
//
|
||||
// Overloaded intrinsics (bAllowRawHelperWithArgs) also fall through here when the call
|
||||
// carries arguments: the nullary form is the wired intrinsic, but `UE_Name(<args>)` is a
|
||||
// same-named raw HLSL helper — leave it for the shader compiler (its side-effect binding
|
||||
// is handled by an anchor capability).
|
||||
const FShaderLabIntrinsicDesc* Found = Registry.Find(FName(*Name));
|
||||
const bool bRawHelperOverload = Found && Found->bAllowRawHelperWithArgs
|
||||
&& SplitArgs(ArgsRaw).Num() > Found->Params.Num();
|
||||
if (!Found || bRawHelperOverload)
|
||||
{
|
||||
Result += Body.Mid(i, j - i);
|
||||
i = j;
|
||||
@@ -1015,12 +1024,19 @@ namespace ShaderLabGraph
|
||||
i = e;
|
||||
continue;
|
||||
}
|
||||
if (Registry.Find(FName(*Name)))
|
||||
if (const FShaderLabIntrinsicDesc* Desc = Registry.Find(FName(*Name)))
|
||||
{
|
||||
// Registry intrinsic: read its balanced (...) and record the use (args are literals).
|
||||
int32 depth = 0, m = paren;
|
||||
for (; m < Len; ++m) { if (Body[m] == TEXT('(')) { ++depth; } else if (Body[m] == TEXT(')')) { if (--depth == 0) { break; } } }
|
||||
const FString ArgsRaw = (m < Len) ? Body.Mid(paren + 1, m - (paren + 1)) : FString();
|
||||
// Overloaded intrinsic called with args: it's the same-named raw HLSL helper, not the wired
|
||||
// intrinsic — leave it and keep scanning its arguments (like an unknown UE_ helper below).
|
||||
if (Desc->bAllowRawHelperWithArgs && SplitArgs(ArgsRaw).Num() > Desc->Params.Num())
|
||||
{
|
||||
i = e;
|
||||
continue;
|
||||
}
|
||||
const FString ArgSig = MakeArgSig(ArgsRaw);
|
||||
if (!OutIntr.ContainsByPredicate([&](const FIntrinsicUse& U) { return U.Name == Name && U.ArgSig == ArgSig; }))
|
||||
{
|
||||
@@ -1205,11 +1221,19 @@ namespace ShaderLabGraph
|
||||
if (bCall && Ident.StartsWith(TEXT("UE_")))
|
||||
{
|
||||
const FString Name = Ident.Mid(3);
|
||||
if (Registry.Find(FName(*Name)))
|
||||
if (const FShaderLabIntrinsicDesc* Desc = Registry.Find(FName(*Name)))
|
||||
{
|
||||
int32 depth = 0, m = paren;
|
||||
for (; m < Len; ++m) { if (Body[m] == TEXT('(')) { ++depth; } else if (Body[m] == TEXT(')')) { if (--depth == 0) { break; } } }
|
||||
const FString ArgsRaw = (m < Len) ? Body.Mid(paren + 1, m - (paren + 1)) : FString();
|
||||
// Overloaded intrinsic called with args -> the same-named raw HLSL helper form: emit the
|
||||
// identifier verbatim (do NOT rewrite to an SLI_ input) and keep scanning its arguments.
|
||||
if (Desc->bAllowRawHelperWithArgs && SplitArgs(ArgsRaw).Num() > Desc->Params.Num())
|
||||
{
|
||||
P1 += Ident;
|
||||
i = e;
|
||||
continue;
|
||||
}
|
||||
P1 += MakeIntrinsicVar(Name, MakeArgSig(ArgsRaw));
|
||||
i = (m < Len) ? m + 1 : e;
|
||||
continue;
|
||||
@@ -2757,39 +2781,74 @@ bool FShaderLabGraphBuilder::BuildInto(UMaterial& Material, const FShaderLabMode
|
||||
}
|
||||
}
|
||||
|
||||
// Scene-texture reads: the UE_SceneColor/SceneDepth/CustomDepth/CustomStencil/SceneTexture helpers call the
|
||||
// engine's SceneTextureLookup in HLSL. That only works when the material sets bNeedsSceneTextures, which the
|
||||
// engine derives from a UMaterialExpressionSceneTexture node being present + compiled. We add one hidden node
|
||||
// wired into the ParameterAnchor (compiled before attributes, so its Compile always runs and flips the flag).
|
||||
// Anchor side-effect capabilities: raw-HLSL body helpers (SceneTexture reads, global distance field
|
||||
// reads, ...) that call an engine HLSL function directly from a Custom node. The helper compiles as bare
|
||||
// HLSL, but the runtime resource binding it needs is only established as a side effect of compiling a
|
||||
// matching UMaterialExpression (UseSceneTextureId / bUsesGlobalDistanceField / ...). Because the Custom
|
||||
// body bypasses that node, we synthesize hidden expression(s) wired into the before-attributes
|
||||
// ParameterAnchor purely for the side effect. See FShaderLabAnchorCapabilityRegistry.
|
||||
{
|
||||
auto BodyUsesScene = [](const FString& Body) -> bool
|
||||
// Bodies to scan: the shader's own pixel/vertex bodies AND every reachable `.uslfunc` library-function
|
||||
// body (a library function is inlined into the consuming Custom node, so it can equally reference a raw
|
||||
// helper and must contribute to binding). Emit.Ctx holds exactly the reachable library functions.
|
||||
TArray<FShaderLabScannedBody> Bodies;
|
||||
if (Model.bHasSurface) { Bodies.Add({ &Model.SurfaceBody, Model.SurfaceBodyLine }); }
|
||||
for (const FShaderLabSlab& Slab : Model.Slabs) { Bodies.Add({ &Slab.Body, Slab.BodyLine }); }
|
||||
for (const FShaderLabValue& Value : Model.Values) { Bodies.Add({ &Value.Body, Value.BodyLine }); }
|
||||
for (const FShaderLabInterpolator& Interp : Model.Interpolators) { Bodies.Add({ &Interp.Body, Interp.BodyLine }); }
|
||||
if (Model.bHasVertex) { Bodies.Add({ &Model.VertexBody, Model.VertexBodyLine }); }
|
||||
if (Emit.HasLibraries())
|
||||
{
|
||||
return ReferencesToken(Body, TEXT("UE_SceneColor")) || ReferencesToken(Body, TEXT("UE_SceneDepth"))
|
||||
|| ReferencesToken(Body, TEXT("UE_CustomDepth")) || ReferencesToken(Body, TEXT("UE_CustomStencil"))
|
||||
|| ReferencesToken(Body, TEXT("UE_SceneTexture"));
|
||||
};
|
||||
bool bUsesScene = Model.bHasSurface && BodyUsesScene(Model.SurfaceBody);
|
||||
for (const FShaderLabSlab& Slab : Model.Slabs) { bUsesScene |= BodyUsesScene(Slab.Body); }
|
||||
for (const FShaderLabValue& Value : Model.Values) { bUsesScene |= BodyUsesScene(Value.Body); }
|
||||
|
||||
if (bUsesScene)
|
||||
{
|
||||
const EShaderLabDomain Dom = Model.Settings.Domain;
|
||||
const bool bNonOpaqueSurface = (Dom == EShaderLabDomain::Surface)
|
||||
&& Model.Settings.BlendMode != EShaderLabBlendMode::Opaque
|
||||
&& Model.Settings.BlendMode != EShaderLabBlendMode::Masked;
|
||||
const bool bDomainOk = (Dom == EShaderLabDomain::PostProcess) || (Dom == EShaderLabDomain::Decal) || bNonOpaqueSurface;
|
||||
if (!bDomainOk)
|
||||
for (const TPair<FName, FFunctionCtx>& Pair : Emit.Ctx)
|
||||
{
|
||||
OutErrors.Add(TEXT("Scene-texture reads (UE_SceneColor/UE_SceneDepth/UE_SceneTexture/...) require a PostProcess or Decal material, or a translucent (non-opaque) Surface material."));
|
||||
return false;
|
||||
int32 LibIdx = INDEX_NONE;
|
||||
if (const FShaderLabFunction* Fn = Program.FindFunction(Pair.Key, LibIdx))
|
||||
{
|
||||
Bodies.Add({ &Fn->Body, Fn->BodyLine });
|
||||
}
|
||||
}
|
||||
// The hidden node flips bNeedsSceneTextures. In PostProcess use PostProcessInput0; elsewhere use
|
||||
// SceneDepth — the only scene-texture family the engine permits outside PostProcess/Decal (scene
|
||||
// color via SceneTexture is disallowed there, so we don't request it).
|
||||
UMaterialExpressionSceneTexture* Hidden = NewExpr<UMaterialExpressionSceneTexture>(Material, ParamY, -1300);
|
||||
Hidden->SceneTextureId = (Dom == EShaderLabDomain::PostProcess) ? PPI_PostProcessInput0 : PPI_SceneDepth;
|
||||
AnchorInputs.Add(Hidden);
|
||||
}
|
||||
|
||||
const FString SrcPath = MakeLineDirectivePath(Model.SourceFilePath);
|
||||
bool bCapabilityFailed = false;
|
||||
FShaderLabAnchorCapabilityRegistry::Get().ForEach(
|
||||
[&](const FShaderLabAnchorCapability& Cap)
|
||||
{
|
||||
if (bCapabilityFailed) { return; }
|
||||
|
||||
bool bActive = false;
|
||||
for (const FShaderLabScannedBody& B : Bodies)
|
||||
{
|
||||
for (const FString& Token : Cap.TriggerTokens)
|
||||
{
|
||||
if (ReferencesToken(*B.Text, Token)) { bActive = true; break; }
|
||||
}
|
||||
if (bActive) { break; }
|
||||
}
|
||||
if (!bActive) { return; }
|
||||
|
||||
FShaderLabAnchorCapabilityContext CapCtx{ Material, Model, Bodies, SrcPath,
|
||||
[&Material, &ParamY](UClass* Class) -> UMaterialExpression*
|
||||
{
|
||||
UMaterialExpression* Expr = NewObject<UMaterialExpression>(&Material, Class);
|
||||
Material.GetExpressionCollection().AddExpression(Expr);
|
||||
Expr->MaterialExpressionEditorX = -1300;
|
||||
Expr->MaterialExpressionEditorY = ParamY;
|
||||
ParamY += 120;
|
||||
return Expr;
|
||||
} };
|
||||
|
||||
TArray<UMaterialExpression*> CapNodes;
|
||||
if (!Cap.Emit(CapCtx, CapNodes, OutErrors))
|
||||
{
|
||||
bCapabilityFailed = true;
|
||||
return;
|
||||
}
|
||||
AnchorInputs.Append(CapNodes);
|
||||
});
|
||||
if (bCapabilityFailed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user