DreamShaderLang
Plugin referenceC++ API

DreamShaderVersionCompat.h

DreamShaderVersionCompat.h

DreamShaderVersionCompat.h

DreamShader » C++ API » DreamShaderVersionCompat.h

Thirteen preprocessor macros that gate every engine-version-dependent behaviour in the plugin — and, outside the header, one module definition: the Custom Pass gate, DREAMSHADER_WITH_CUSTOM_PASS. This page is the source of truth for every (since UE 5.x) marker in the manual.

Defined in header DreamShaderVersionCompat.h.

ModuleDreamShader (Runtime), included by DreamShader and DreamShaderEditor. DreamShaderLang cannot include it — it depends on Core alone and has no engine version to gate on
Include#include "DreamShaderVersionCompat.h"
Contents13 macros. No types, no functions, no namespace.
DependenciesMisc/CoreDelegates.h (for DREAMSHADER_POST_ENGINE_INIT_DELEGATE), Runtime/Launch/Resources/Version.h
Verified enginesUE 5.3 – 5.8 (Win64)

Synopsis

The version arithmetic every other macro is built on. The feature gates that use it are in Macros below.

#include "Misc/CoreDelegates.h"
#include "Runtime/Launch/Resources/Version.h"

#ifndef DREAMSHADER_UE_MAJOR
#define DREAMSHADER_UE_MAJOR ENGINE_MAJOR_VERSION
#endif

#ifndef DREAMSHADER_UE_MINOR
#define DREAMSHADER_UE_MINOR ENGINE_MINOR_VERSION
#endif

#ifndef DREAMSHADER_UE_PATCH
#define DREAMSHADER_UE_PATCH ENGINE_PATCH_VERSION
#endif

#ifndef DREAMSHADER_WITH_SUBSTRATE_BUILTINS
#define DREAMSHADER_WITH_SUBSTRATE_BUILTINS (DREAMSHADER_UE_MAJOR > 5 || (DREAMSHADER_UE_MAJOR == 5 && DREAMSHADER_UE_MINOR >= 4))
#endif

#define DREAMSHADER_UE_VERSION_AT_LEAST(MajorVersion, MinorVersion) \
    (DREAMSHADER_UE_MAJOR > (MajorVersion) || (DREAMSHADER_UE_MAJOR == (MajorVersion) && DREAMSHADER_UE_MINOR >= (MinorVersion)))

#if DREAMSHADER_UE_VERSION_AT_LEAST(5, 4)
#define DREAMSHADER_ALLOW_SHRINKING_NO EAllowShrinking::No
#else
#define DREAMSHADER_ALLOW_SHRINKING_NO false
#endif

Macros

MacroExpands toOverridablePurpose
DREAMSHADER_UE_MAJORENGINE_MAJOR_VERSIONyes — #ifndef guardedThe major version every other test is built on.
DREAMSHADER_UE_MINORENGINE_MINOR_VERSIONyes — #ifndef guardedThe minor version.
DREAMSHADER_UE_PATCHENGINE_PATCH_VERSIONyes — #ifndef guardedRead only by the preprocessor, as the builtin define DS_ENGINE_PATCH. No C++ version test uses it.
DREAMSHADER_WITH_SUBSTRATE_BUILTINS(MAJOR > 5 || (MAJOR == 5 && MINOR >= 4)) — i.e. UE ≥ 5.4yes — #ifndef guardedThe single Substrate feature gate.
DREAMSHADER_WITH_MOON_ENGINE1 when the engine's SceneTypes.h declares MP_MoonEncodedAttribute0, 0 otherwiseyes — #ifndef guarded, and DreamShader.Build.cs sets it as a PublicDefinition from that probeMoon Engine's extra material attributes. Not a version test: the probe reads the enumerator the guarded code needs, so it works for any fork that carries it.
DREAMSHADER_UE_VERSION_AT_LEAST(Major, Minor)(MAJOR > Major || (MAJOR == Major && MINOR >= Minor))no — unconditional #defineThe general "at least this engine" test.
DREAMSHADER_ALLOW_SHRINKING_NOEAllowShrinking::No on UE ≥ 5.4, false belowno — selected by #ifPortability shim, described below.
DREAMSHADER_POST_ENGINE_INIT_DELEGATE()FCoreDelegates::GetOnPostEngineInit() on UE ≥ 5.8, the OnPostEngineInit data member belowno — selected by #if5.8 added the accessor and deprecated the member, so naming either one directly breaks the other engine.
DREAMSHADER_THUMBNAIL_PRIM_SHADERBALLTPT_ShaderBall on UE ≥ 5.8, TPT_Sphere belowno — selected by #ifEThumbnailPrimType stops at TPT_Cylinder before 5.8; the preview falls back to the sphere.
DREAMSHADER_MATERIAL_AGGREGATE_HANDLES_SUBSTRATEUE ≥ 5.8no — unconditional #defineWhether MaterialValueTypeToMaterialAggregateAttributeType has a case for MCT_Substrate. Below 5.8 it checkf(false)s, so neither the manifest exporter nor the builtin catalog's reflection makes the call for UMaterialExpressionAggregate at all.
DREAMSHADER_WITH_SCALAR_PARAMETER_CONTROL_TYPEUE ≥ 5.7no — unconditional #defineWhether UMaterialExpressionScalarParameter has ControlType, Enumeration and EnumerationIndex.
DREAMSHADER_WITH_MATERIAL_PARAMETERS_HEADERUE ≥ 5.7no — unconditional #defineWhich header declares FMaterialParameterInfo: Materials/MaterialParameters.h from 5.7, MaterialTypes.h before it.
DREAMSHADER_WITH_PARAMETER_COLLECTION_PARAMETERS (since 2.0.0)1 when the engine's EMaterialParameterType has a ParameterCollection enumerator, 0 otherwiseyes — #ifndef guarded (falls back to UE ≥ 5.8), and DreamShader.Build.cs sets it as a PublicDefinition from that probeWhether a material instance can override a parameter collection. Asked of the header that declares the enum, because a case label cannot ask the type.

The six #ifndef-guarded ones may be pre-defined by a build target — for example to compile the Substrate paths out on a 5.4+ engine by defining DREAMSHADER_WITH_SUBSTRATE_BUILTINS=0, or to force a version branch when testing. DreamShader.Build.cs uses that to set DREAMSHADER_WITH_MOON_ENGINE and DREAMSHADER_WITH_PARAMETER_COLLECTION_PARAMETERS from their header probes. The plugin's only other definitions are DREAMSHADER_WITH_CUSTOM_PASS, a PublicDefinitions entry of DreamShaderPass.Build.cs that this header does not define (below), and MOON_ENGINE=1, a PrivateDefinitions entry that DreamShaderEditor.Build.cs adds when the engine's SceneTypes.h declares MP_MoonEncodedAttribute0 and that no source file reads.

[!NOTE] DREAMSHADER_ALLOW_SHRINKING_NO carries no behavioural difference. It exists solely because UE 5.4 changed the trailing bool bAllowShrinking parameter of TArray::RemoveAt, TArray::Pop, FString::RightChopInline and FString::LeftChopInline into an EAllowShrinking enum. Both spellings mean "do not shrink the allocation". It is used throughout the plugin and is not a tuning knob.

[!NOTE] There are no raw #if ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION or UE_VERSION_NEWER_THAN tests anywhere in the plugin outside this header. The one version test outside it is not C++: DreamShaderPass.Build.cs reads Target.Version to set DREAMSHADER_WITH_CUSTOM_PASS. Every version-dependent behaviour goes through these macros or that definition, which is what makes the table below exhaustive.

[!NOTE] (since 2.0.0) Where a member is younger than the oldest supported engine and nobody can say in which release it arrived, the code asks the type instead of a version: if constexpr (requires { … }) inside a small template, with a static_assert of the same question on the engine where the member is known to exist, so a misspelling cannot read as "this engine does not have it". Those sites are listed under Asked of the type.

DREAMSHADER_WITH_CUSTOM_PASS

(since 2.1.0) The Custom Pass gate is not in this header. DreamShaderPass.Build.cs computes it from the engine being built against and adds it to the module's PublicDefinitions:

bool bForcedOff = Target.ProjectDefinitions.Any(Definition =>
    Definition == "DREAMSHADER_FORCE_NO_CUSTOM_PASS" || Definition == "DREAMSHADER_FORCE_NO_CUSTOM_PASS=1");
bool bWithCustomPass = !bForcedOff
    && (Target.Version.MajorVersion > 5 || (Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 8));
PublicDefinitions.Add("DREAMSHADER_WITH_CUSTOM_PASS=" + (bWithCustomPass ? "1" : "0"));
Defined byDreamShaderPass.Build.cs — not DreamShaderVersionCompat.h, which neither defines nor mentions it
Value1 on UE ≥ 5.8, 0 below or when forced off. Always defined where it is visible, so test it with #if, not #ifdef
Visible inDreamShaderPass and the modules that depend on it: in the plugin, DreamShaderCompiler and DreamShaderEditor, which ask it rather than test the version again. DreamShaderLang and DreamShader do not depend on DreamShaderPass and do not have it
Overridabledown only, from the UBT command line: -ProjectDefine:DREAMSHADER_FORCE_NO_CUSTOM_PASS builds 0 on any engine, which is how a 5.8 machine compiles and tests the other side (Testing). Nothing makes it 1 below 5.8, and code cannot pre-empt it: the rules file sets it with no #ifndef guard
Also decidesthe Renderer/Internal include path of DreamShaderPass (for FPostProcessingInputs), added at 1 only

The module builds on every engine. Every reflected type in it is plain data, so pipelines, the settings, the components and the volume load and save on any supported engine; below 5.8 nothing renders them. What 0 compiles out, module by module, is under UE ≥ 5.8 — DREAMSHADER_WITH_CUSTOM_PASS.

Complete version-gated behaviour

Every site in the plugin guarded by one of these macros, grouped by the version it requires. Diagnostics are cited by code; the strings still quoted below are log lines, material-compile errors and UI text, which carry no code.

Decided by the node catalog

(since 2.0.0) Most of what the 1.x generator gated by engine version is no longer a DreamShader gate at all. The UE.* and Substrate.* nodes, their pins and properties, the values of their enumerated properties and the material attributes a source can write come from the builtin catalog, which is reflected from the running engine (every non-abstract UMaterialExpression class it has loaded). What an engine does not have, the catalog does not list, and the binder says so:

What 1.x gatedGateNow
A Substrate.* call5.4On UE ≥ 5.4 the Substrate namespace holds the engine's Substrate expression classes; on 5.3 it is empty. A name the catalog lacks is DSH5210; Substrate.Select (5.6) and Substrate.Toon (5.8), and Substrate sugar that needs a node the engine lacks, are DSH5294, which names the version
Base.FrontMaterial5.4A material attribute like any other; an engine whose attribute list has no FrontMaterial answers DSH5200
UE.TransformPosition(PeriodicWorldTileSize=…)5.5A pin or property of the engine's node; where the class has no such member, DSH5213
UE.TransformPosition(FirstPersonInterpolationAlpha=…)5.6as above, DSH5213
The periodicworld, firstperson and firstpersontranslatedworld bases5.5 / 5.6Matched against the values of the engine's enum behind the node's Source / Destination property (1.x text loosely, with DSH5278); a spelling that matches no value is DSH5215
UE.UserSceneTexture—DSH5300 on an engine whose catalog lacks it (the binder knows it from 5.6)
UE.DreamPassBuffer / UE.DreamPassOutput—left out of the catalog below 5.8 on purpose, so DSH5300 (see UE ≥ 5.8)

A catalog exported with dsc export-catalog from an older engine gives the same answers. The version gates that remain in C++ are the ones below.

UE ≥ 5.4 — DREAMSHADER_UE_VERSION_AT_LEAST(5, 4)

FeatureOn UE ≥ 5.4On UE 5.3
TArray / FString shrink argumentEAllowShrinking::Nofalse
Generated Custom nodesUMaterialExpressionCustom::ShowCode = false on every Custom node the IR emitter creates, so the HLSL body stays collapsed in the material editorthe property does not exist; not set, and generated Custom nodes show their code
Material reset before a rebuild, and the generated-asset digestbHasPixelAnimation is cleared along with the other material flags, and recorded in the digestskipped
Decompiler material-flag exportbHasPixelAnimation is included in the emitted Settings flag listexcluded

UE ≥ 5.4 — DREAMSHADER_WITH_SUBSTRATE_BUILTINS

The Substrate gate is a separate macro so it can be overridden independently, but its default threshold is the same 5.4.

FeatureOn UE ≥ 5.4On UE 5.3
Substrate.* namespace of the builtin catalogthe engine's UMaterialExpressionSubstrateBSDF and …SubstrateUtilityBase subclasses are listed under Substrate.every class is listed under UE., so Substrate. names nothing: a call is DSH5210 or DSH5294 (see Decided by the node catalog)
MaterialExpressionSubstrate.hincluded by the builtin catalog's reflection and the three graph-decompiler filesnot included
ShadingModel = "Substrate" (or "Strata")acceptederror DSH7128
Shading-model alias catalogueMSM_Strata is kept despite its Hidden metadata, and the Substrate and Strata aliases are addedMSM_Strata is skipped and neither alias is added
Material connection type for Substrate values (catalog pin types, IsSubstrateMaterialValueType)MCT_SubstrateMCT_Strata
Decompiler type namingMCT_Substrate prints as "Substrate"MCT_Strata prints as "Substrate"
Decompiler shading-model export"Substrate" is emitted for MSM_Stratanot emitted
Decompiler binding tableMP_FrontMaterial is includedexcluded
substrate-builtins.json manifestsupported: true and a populated builtins arraysupported: false with unsupportedReason: "Substrate builtins require Unreal Engine 5.4 or newer.", and an empty builtins array
Bridge manifest, shading modelsMSM_Strata offeredMSM_Strata added to the excluded-shading-model set

UE ≥ 5.5

FeatureOn UE ≥ 5.5On UE 5.3 – 5.4
Counting an expression's inputsExpression->CountInputs()Expression->GetInputsView().Num()
Reading one input pinExpression->GetInput(Index)Expression->GetInput(Index) — identical. GetInputsView() would serve too, but it is deprecated in favour of FExpressionInputIterator / GetInput() and emits C4996 on UE 5.8, so the array view is never built just to reach a single pin
Resolving UMaterialExpressionObjectPositionWS (decompiler)StaticClass() and IsA<> directlyFindObject<UClass>(nullptr, "/Script/Engine.MaterialExpressionObjectPositionWS")

The periodicworld basis and the PeriodicWorldTileSize argument are no longer gated here (since 2.0.0): see Decided by the node catalog.

UE ≥ 5.6

FeatureOn UE ≥ 5.6On UE 5.3 – 5.5
Package metadata accessUPackage::GetMetaData() returns FMetaData&returns UMetaData*, null-checked before use
Expression pin value typesGetInputValueType / GetOutputValueTypeGetInputType / GetOutputType, cast to EMaterialValueType
Rebuilding an expression's outputsExpression->RebuildOutputs()manual Outputs.Reset() followed by a rebuild
Resolving UMaterialExpressionScreenPosition (decompiler)StaticClass() and IsA<> directlyFindObject<UClass>(nullptr, "/Script/Engine.MaterialExpressionScreenPosition")
Pin types read off a class default object — the builtin catalog's reflection, the workspace service, the graph importerGetInputValueType / GetOutputValueTypethe deprecated GetInputType / GetOutputType, wrapped in PRAGMA_DISABLE_DEPRECATION_WARNINGS
Shader(Root="Plugin.X")additionally requires Plugin->IsMounted(); otherwise DSH8094mount check skipped
Path(Plugin.X, "…") asset rootssame mount check; otherwise DSH8122mount check skipped
Texture-collection parameters on a .dsi instancethe kind is in the parent's schema, and an override is written to the instancenot in the schema, so an override is refused as unknown; one built by hand fails with DSH8252
Decompiler TextureSample exportGatherMode is round-trippedomitted
DREAMSHADER_ENGINE_EXPRESSION_CLASS(Name) (in DreamShaderMaterialExpressionCompat.h)U<Name>::StaticClass()FindObject<UClass>(nullptr, "/Script/Engine.<Name>"). Still defined, and called nowhere since 2.0.0: the UE.* catalog is built by iterating the loaded classes, which needs no StaticClass()

The firstperson bases and the FirstPersonInterpolationAlpha argument are no longer gated here (since 2.0.0): see Decided by the node catalog.

[!NOTE] DREAMSHADER_ENGINE_EXPRESSION_CLASS is a link-time gate, not a compile-time one. UE 5.6 changed UHT to emit DECLARE_CLASS2 with an exported Z_Construct_<Class>_NoRegister, so StaticClass() resolves from a plugin even for a UCLASS() that carries neither MinimalAPI nor ENGINE_API. UE 5.5 and earlier emit DECLARE_CLASS(..., NO_API): GetPrivateStaticClass never leaves Engine.dll, and naming StaticClass() compiles on every engine and then fails with LNK2019. Only a full RunUAT BuildPlugin sees it — an editor build against one engine never will.

Asked of the type

(since 2.0.0) No version number here: each row is decided by whether the engine's own type has the member -- or, (since 2.0.1), whether the engine has the node's header at all (__has_include).

FeatureWhere the member existsWhere it does not
Reading a Convert node (Make / Break FloatN) back as the channels each output is put together from — Materials/MaterialExpressionConvert.h, the node of the 5.6 material editor (since 2.0.1)written as swizzles and constructorsno graph can hold one
Reading a Switch node back as its branches — Materials/MaterialExpressionSwitch.h (since 2.0.1)written as a chain of == branches, or as the one input an unwired SwitchValue picksno graph can hold one
Re-applying a kept double vector override to a ThinCustom instance — UMaterialInstanceConstant::SetDoubleVectorParameterValueEditorOnly (absent through 5.6)setreported by name as a kind this build cannot express, like a parameter the source dropped
Re-applying a kept static component mask override — SetStaticComponentMaskParameterValueEditorOnly on the instance (absent through 5.6)setreported the same way
Reading an instance's usage-flags override — FMaterialInstanceBasePropertyOverrides::bOverride_UsageFlags (absent through 5.6)named as unsupported by the instance decompiler when setthere is no such override to report
TextureSample.GatherMode when the graph importer decides whether a sample is the plain one (absent in 5.5)compared with the defaultnothing to compare

DREAMSHADER_WITH_PARAMETER_COLLECTION_PARAMETERS

(since 2.0.0)

FeatureWhere EMaterialParameterType::ParameterCollection existsWhere it does not (5.6 and earlier)
A .dsi override of a parameter-collection parameterbound against the parent's schema and written to the instancethe kind is not in the parent's schema, so the override is refused as unknown; building one by hand answers this engine has no parameter collection parameters.
The instance decompilerwrites the overridenever meets one

UE ≥ 5.7

FeatureOn UE ≥ 5.7On UE 5.3 – 5.6
Material-resource diagnostics in the bridgeiterate every EShaderPlatform × EMaterialQualityLevel combinationa different, narrower path
dsc check -Shaders, the material resource per quality leveltaken for the target's shader platformtaken for GMaxRHIFeatureLevel
Layer-blend function inputsUMaterialExpressionFunctionInput::BlendInputRelevance is set from the IR, and the graph importer reads it backnot set, not read
Node preview height in the layout passExpression->ShouldShowPreview()!bHidePreviewWindow && !bCollapsed, the two flags 5.7 composed it from
Decompiler scalar-parameter metadataControlType, Enumeration and EnumerationIndex are exportednot exported — the properties do not exist. A source that carries them still parses; there is nothing to write them to
FMaterialParameterInfo includeMaterials/MaterialParameters.hMaterialTypes.h, which 5.7 keeps only as a deprecation stub

UE ≥ 5.8

FeatureOn UE ≥ 5.8On UE 5.3 – 5.7
The builtin catalog's material attributes (since 2.0.0)FMaterialAttributeDefinitionMap::GetAttributeNameToIDListthat list is private, so the attributes are reached by walking EMaterialProperty through the public GetID / GetProperty / GetAttributeName. The same attributes either way; only their order in the exported catalog differs
Default volumetric-cloud usage written when settings are appliedUMaterial::SetUsageByFlag(MATUSAGE_VolumetricCloud, …)the bUsedWithVolumetricCloud field directly. The same value; each spelling is the one that compiles without a deprecation warning on its engines

UE ≥ 5.8 — DREAMSHADER_WITH_CUSTOM_PASS

(since 2.1.0) Custom Pass as a whole. The language does not change: a .dsp parses and binds on every engine, and so does a .dss — what changes is what the engine side can build and run.

ModuleFeatureOn UE ≥ 5.8On UE 5.3 – 5.7
DreamShaderPassModule startupmaps /DreamPassUser to the project source root's .dreampass/ folder, creating Slots/ and the two registry .ush files when missing and dropping registry sections whose snapshot is missing; the mapping is skipped in a cooked buildlogs DreamShaderPass: Custom Pass needs Unreal Engine 5.8 or later; this build has the asset types only. and does nothing else
DreamShaderPassThe renderer half — everything under Private/Render/compiled in: the scene view extension (one per world, made by UDreamPassSubsystem), the slot global shaders FDreamPassCS / FDreamPassPS, the mesh pass shaders, buffers, exports, the buffer visualization and the stat DreamPass groupcompiled out. The subsystem still keeps pipelines, activations, lists and layers, but no view extension exists, so no pass runs
DreamShaderPassDreamPass.Dumpends each world with the last frame's report: views with passes, passes run, passes skippedthe same listing without the frame report
DreamShaderPassr.DreamPass.Enable, r.DreamPass.DisablePipelines, r.DreamPass.Visualizeregisteredregistered; with nothing rendering, there is nothing for them to change
DreamShaderPassUMaterialExpressionDreamPassBuffer / …Output (UE.DreamPassBuffer, UE.DreamPassOutput)compile as documentedthe classes exist — UHT cannot gate a UCLASS — but Compile returns Dream Pass Buffer needs Unreal Engine 5.8 or later. / Dream Pass Output needs Unreal Engine 5.8 or later., and most of their overrides — pin value types, the referenced texture, the shader tag, IsAllowedIn — are compiled out
DreamShaderCompilerThe builtin cataloglists both nodesleaves both out, so a source that calls one fails when it is bound, with DSH5300 naming 5.8 and the Custom Pass module
DreamShaderCompilerBuilding a .dspthe pipeline and its export render targets are writtenDSH8300; nothing is built
DreamShaderCompilerWhat a pass's material is checked againstdomain, blendable location, UserSceneTexture inputs, UE.DreamPassOutput pins, usage flags, pre-exposure and translator settingsdomain and blendable location only (FPipelineReferences::bCustomPassAvailable is false), with the info DSH7360 saying the rest was not checked; the build key carries CustomPass=0
DreamShaderCompilerHLSL slot pre-checkcompiles each changed slot for the shader formats of the active feature levels and of the target platformsDSH8323
DreamShaderCompilerdsc check -Shaders on a .dsppre-checks its slotsDSH8338
DreamShaderCompilerHot reload of the slot shaders after a registry commitin the editor, never in a commandletnone
DreamShaderEditorPipeline details panelas documentedone more row in Pipeline Overview: Custom Pass runs on Unreal Engine 5.8 and later. This engine loads and saves the pipeline, and runs none of it.
DreamShaderEditorpass-keys.jsonsupported: truesupported: false and unsupportedReason: "Custom Pass pipelines run on Unreal Engine 5.8 and later."; the keys, injection points and formats are written either way
DreamShaderEditorThe render automation tests (DreamShaderPassRenderTests.cpp)compiled incompiled out

"Since UE 5.x" summary

VersionFeatures that require it
5.4Substrate — the Substrate. catalog namespace, ShadingModel="Substrate", MSM_Strata aliases, Base.FrontMaterial in the decompiler · collapsed Custom-node code (ShowCode) · bHasPixelAnimation reset, digest and export · EAllowShrinking
5.5ObjectPositionWS resolved directly · CountInputs
5.6plugin-mount validation for Root= and Path(...) · texture-collection instance parameters · TextureSample.GatherMode round-trip · FMetaData&, GetInputValueType, RebuildOutputs, ScreenPosition resolved directly
5.7BlendInputRelevance on layer-blend inputs · per-platform × per-quality material-resource diagnostics · scalar-parameter ControlType / Enumeration / EnumerationIndex round-trip · node preview height from ShouldShowPreview()
5.8Custom Pass (since 2.1.0) — building and running .dsp pipelines, HLSL slots, the UE.DreamPassBuffer / UE.DreamPassOutput nodes (DREAMSHADER_WITH_CUSTOM_PASS) · parameter-collection instance parameters (asked of the engine's enum)

Nodes, pins, enumerators and material attributes that only newer engines have are not in this table: the running engine's catalog decides them (above). Everything else works identically on every engine from 5.3 to 5.8. A .dsp is the one source that parses and binds everywhere and builds on 5.8 only.

Notes

  • The gate is compile-time, not run-time. The plugin binary built against UE 5.3 does not contain the Substrate code paths at all. Moving a project to a newer engine requires rebuilding the plugin to gain the newer behaviours.
  • GetInputsView() survives in exactly one place. Epic deprecated it in favour of FExpressionInputIterator and UMaterialExpression::GetInput(), and naming it produces C4996 on UE 5.8 — a warning today, a compile error in whichever release removes it. The remaining call is the UE 5.3 – 5.4 branch of GetDreamShaderExpressionInputCount, where CountInputs() does not exist yet and the deprecation is not in force; every other site asks for a pin by index.
  • (since 2.0.0) A node, pin, enumerator or attribute the engine lacks is missing from its catalog, so a source that uses one is an error when it is bound; 1.x dropped some of them (an unread argument) without a word. Only a few of those errors name a version — DSH5294 (a Substrate node from a later engine), DSH5300 (UE.UserSceneTexture, the Custom Pass nodes), DSH7128 (ShadingModel="Substrate" on 5.3) and DSH8300 (a .dsp below 5.8); the rest say that the node, pin, value or attribute does not exist (DSH5210, DSH5213, DSH5215, DSH5200). The substrate-builtins.json manifest on 5.3 still carries an unsupportedReason. To build one source on several engines, guard the newer code with #if DS_ENGINE_MINOR >= ….
  • DREAMSHADER_WITH_SUBSTRATE_BUILTINS is evaluated as a value, so it must be written #if DREAMSHADER_WITH_SUBSTRATE_BUILTINS, not #ifdef. It is always defined.
  • DREAMSHADER_UE_VERSION_AT_LEAST is defined unconditionally, so pre-defining it in a build target produces a macro redefinition. Override the three version-number macros instead.
  • Because DreamShaderModule.h includes this header, any translation unit that uses the plugin's path helpers also has the macros available.

Example

Guarding project code that consumes a version-gated DreamShader behaviour:

#include "DreamShaderVersionCompat.h"

void ConfigureShadingModel(FString& OutSettingValue)
{
#if DREAMSHADER_WITH_SUBSTRATE_BUILTINS
    OutSettingValue = TEXT("Substrate");
#else
    OutSettingValue = TEXT("DefaultLit");
#endif
}

void TrimTrailing(TArray<int32>& InOut)
{
    if (InOut.Num() > 0)
    {
        InOut.RemoveAt(InOut.Num() - 1, 1, DREAMSHADER_ALLOW_SHRINKING_NO);
    }
}

#if DREAMSHADER_UE_VERSION_AT_LEAST(5, 6)
// Plugin-rooted asset paths are mount-validated here; a packaged-but-unmounted plugin is an error.
#endif

The corresponding DreamShaderLang source, written so it compiles on 5.3 as well:

Shader(Name="Materials/M_Portable")
{
    Settings { ShadingModel = "DefaultLit"; }   // "Substrate" would require UE 5.4+
    Outputs  { vec3 Color; Base.BaseColor = Color; }
    Graph    { Color = UE.TransformVector(Input = UE.VertexNormalWS(), Source = "World", Destination = "Tangent"); }
}

See also

On this page