Evaluation Model
What a Graph block actually is, how the legacy front end of the 2.0 compiler builds it into nodes, and why it is a different language from the declarations around it.
Graph
DreamShader » Graph
The section whose body is a sequence of statements that the compiler lowers once, in order, at
generation time into the UMaterialExpression nodes of the generated material.
| Declared in | .dsm, .dsf — inside Shader, ShaderFunction, ShaderLayer, ShaderLayerBlend |
| Kind | section |
| Generates | UMaterialExpression nodes on the target UMaterial / UMaterialFunction |
| Section name | matched case-insensitively; the = before { is optional (since 1.5.0) |
Synopsis
Graph [=] {
<graph-statement> …
}A Graph body is not an expression language embedded in a shader — it is a node-construction
program. Nothing in it survives to runtime as code. What survives is the graph it built.
To write the same thing as DreamShaderLang 2.0 — with loops, ?: and && — see
the 2.0 language; dsc migrate rewrites a 1.x
file for you.
Where a Graph block may appear
| Block | Graph | Code |
|---|---|---|
Shader | ✔ required unless the Outputs section declares or binds something (DSH2255) | ✘ DSH2246 |
ShaderFunction | ✔ — without one the body is empty | ✘ DSH2246 |
ShaderLayer / ShaderLayerBlend | ✔ — without one the body is empty | ✘ DSH2246 |
VirtualFunction | ✘ DSH2247 | ✘ DSH2247 |
Function / GraphFunction | body is HLSL, not a Graph block | — |
[!WARNING]
Code = { … }isDSH2246in every block that has aGraph. UseGraph.
The same statement language is also used, in reduced form, in two more places:
| Place | Shape |
|---|---|
An Outputs binding's right-hand side | one full expression, no statements |
An Outputs declaration's = <default> | one expression: the initializer of a variable declared before the first Graph statement |
The evaluation model
- The legacy front end reads the body into the 2.0 tree (since 2.0.0). A
Shaderbecomes one material function whose body is, in order: everyOutputsdeclaration (with its initializer, or a zero of its type), theGraphstatements, and theOutputsbindings as assignments to the material. A function block'sGraphis the body of the function itsInputsandOutputsdeclare. - The binder types every name and expression before any node exists. A construct 1.x did not have, or that does not type, stops the build with a code — the body is never half-built.
- Statements are lowered once each, top to bottom. There is no loop construct, no
return, no recursion, no re-entry. Statement n can only see values produced by statements1 … n-1. - Every expression is a typed value: a number of 1–4 components (of kind
float,half,int,uintorbool), a texture, a sampler, aMaterialAttributesvalue or aSubstratevalue. A variable name is bound to one such value. - Passes run over the lowered graph: constant folding, deduplication of identical nodes, and
pruning of every node nothing reads. The emitter then creates the
UMaterialExpressionnodes. Outputsbinding sources are evaluated last, each as a full expression against the values the body left behind.
Consequences worth internalising
- There is no runtime control flow. An
iflowers both branches at build time; both branch node sets exist in the finished material, and anIfnode (aStaticSwitchfor a static condition) selects between them per variable. See if / else. - A declared variable is not storage. Assigning to a name rebinds it to a different node; the previously bound node stays in the graph if something else still reads it, and is pruned otherwise.
- Identical subexpressions collapse. The deduplication pass merges nodes with the same operation
and the same operands, so the same literal, the same
A + B, the sameUE.TexCoord(Index=0)produce one node no matter how many times they are written. See Node reuse. - Some constructs create no nodes of their own. See Swizzles for the cost of a channel selection.
- Types are checked, widths included. Two operands of different widths are
DSH4226unless one is a scalar; a wider value stored into a narrower variable keeps its leading components with the infoDSH5289;int,uint,boolandhalfare kinds of their own to the compiler and floats in the graph. See Conversions. - Unknown operators are refused, not dropped (since 2.0.0). 1.x silently ended an expression at
%,&&,||,?:,[ ],<<and friends, soa % bbuilta; each is now an error with a code (DSH2200–DSH2222). See Unsupported constructs.
Statements at a glance
| Form | Example | Reference |
|---|---|---|
| Declaration | float3 c = A * K; | Declarations |
| Comma declarators | float a = 1, b, c = 3; | Declarations |
| Brace initializer | vec4 v = {rgb, 1.0}; | Declarations |
| Assignment | Color = Tint; | Statements |
| Attribute member write | Attrs.BaseColor = Tint; | MaterialAttributes |
| Standalone call | F_Split(In, OutA, OutB); | Calls |
if / else / else if | if (m > .5) { … } else { … } | if / else |
The full synopsis table, with termination rules, is on Statements.
Comments and layout
Comments are read by the lexer (since 2.0.0): // line comments and /* */ block comments.
Block comments do not nest; the first */ closes, and one that never closes is
DSH2102. A // inside a string literal is not a comment.
Every diagnostic line and column is the position in the source file.
#Region / #EndRegion
#Region "Name" and #EndRegion lines inside a Graph body group the nodes produced by the
statements between them into a comment box in the generated graph. Each is a line of its own and a
statement of the body (since 2.0.0); regions nest. A #Region without a name, an #EndRegion with
none open, a region still open at the end of the body and any other # line are
DSH2216–DSH2219, or
DSH1035 when the preprocessor does not know the line. Full
syntax and nesting semantics are on Layout.
Diagnostic locations
Every diagnostic is reported against the source file, at the construct it is about:
<file>(<line>,<column>): DSHnnnn: <message>The code is the contract; the wording may change. The line and column are those of the construct
itself, inside a section or an if body alike (since 2.0.0); there is no In Graph if body:
prefix any more. A stage reports all of its errors, and a stage with errors stops the build before
the next one. The codes are listed in the diagnostics index.
Example
Shader(Name="DreamShaderTests/Corpus/M_Arithmetic")
{
Properties = {
vec3 A = vec3(1.0, 0.5, 0.2);
vec3 B = vec3(0.1, 0.2, 0.3);
float K = 2.0;
}
Settings = { Domain = "UI"; ShadingModel = "Unlit"; }
Outputs = { vec3 Color; Base.EmissiveColor = Color; }
Graph = {
vec3 Sum = A + B;
vec3 Diff = A - B;
vec3 Scaled = A * K;
vec3 Ratio = A / K;
Color = Sum + Diff - Scaled + Ratio;
}
}Generated nodes:
VectorParameter A, VectorParameter B, ScalarParameter K (property nodes)
Add (A, B) -> Sum
Subtract (A, B) -> Diff
Multiply (A, K) -> Scaled
Divide (A, K) -> Ratio
Add (Sum, Diff)
Subtract (.., Scaled)
Add (.., Ratio) -> Base.EmissiveColorPages in this section
| Page | Covers |
|---|---|
| Statements | every statement form, termination rules, assignment, expression statements |
| Declarations | T name = expr;, comma declarators, brace initializers, defaults, scope |
| Expressions | operator table, precedence and associativity, unary operators |
| Literals | numeric forms and suffixes, true / false, string literals |
| Constructors | the constructor names, splatting, packing, constant folding |
| Swizzles | channel sets, reorder and repeat |
| Conversions | how a value is fitted to the place it goes |
| if / else | the condition, the comparison truth table, branch merging |
| MaterialAttributes | attribute values, member reads and writes, Substrate interop |
| Calls | calling Function, GraphFunction, ShaderFunction, VirtualFunction, parameter pins |
| Name resolution | identifier and call lookup order, shadowing |
| Node reuse | the deduplication of identical nodes and what a user observes |
| Unsupported constructs | loops, return, ternary, %, comparisons, indexing — and the codes that refuse them |
See also
- Shader — the block that owns a
Graphand itsOutputs - ShaderFunction — a
Graphthat becomes aUMaterialFunction - GraphFunction — HLSL bodies with
UE.*calls hoisted into pins - Type tokens — which tokens a
Graphdeclaration accepts - Layout —
#Region/#EndRegionand node placement directives - UE builtins — the complete
UE.*catalogue callable from aGraph - Math builtins —
lerp,dot,saturate,frac,fmod, … - Reading parameters in Graph — bare reads and the pin call form
- Graph layout — how generated nodes are positioned
- The 2.0 language — the same compiler, with HLSL control flow
- Diagnostics index — every code, by stage
HLSL passes
The HLSL a compute pass, or a fullscreen pass without a material, runs — written in an hlsl block of the .dsp or in a .usf, the names its bindings make, the fixed parameters and helpers of DreamPass.ush, and how the code reaches the engine through a pre-check and a snapshot. Needs UE 5.8.
Statements
Every statement form a Graph body can contain — declarations, assignments, member writes, calls — plus termination, classification, MaterialAttributes and scope.