DreamShaderLang
The Graph Language

What Graph Is Not

Loops, return, ternary, %, comparisons and indexing — every HLSL / GLSL construct a Graph block does not implement, the code the compiler reports for it, and what to write instead.

Unsupported constructs

DreamShader » Graph » Unsupported constructs

Every HLSL and GLSL construct that a Graph block does not implement, the code the compiler reports for it, and what to write instead.

Declared in.dsm, .dsf — applies to every Graph { … } body, Outputs binding expression and Outputs declaration default
Kindreference of refused syntax

A Graph block is a node-graph builder, not a shader compiler. It has a fixed set of statement forms, four arithmetic operators and no control flow other than if. Anything else is refused with a diagnostic code. Nothing is compiled into something smaller than what was written (since 2.0.0): the 1.x generator silently dropped the rest of an expression at the first operator it did not know (a % b built a), and every such construct is now an error whose message says what 1.x did.

To write loops, ?:, && or bitwise operators, move the code into a Function or migrate the file to .dss with dsc migrate, where they exist.

How a construct is refused

The legacy front end reads a Graph body with the 2.0 statement and expression parser, whose grammar is a superset of the 1.x one, and then refuses what 1.x did not have (since 2.0.0):

StageWhat it refusesCodes
Lexera character that is no token at all (@, $, a backtick), a malformed number (0.5.5, 1.0fx)DSH2101, DSH2105
Parsertext that is not a statement or expression of the 2.0 grammar: a missing ;, ) or name, a switchDSH2150–DSH2165
Legacy restriction passa construct the 2.0 grammar has and 1.x did not: operators, [ ], casts, loops, return, compound assignmentDSH2200–DSH2222
Bindera construct that parses but has no meaning: a string value, an integer division, mismatched widthsDSH42xx

Every diagnostic carries the line and column of the construct. A stage reports all of its errors, and a stage with errors stops the build before the next one.

Operators 1.x truncated

Each of these was accepted by 1.x and built only the part before the operator, without a message. (since 2.0.0) each is an error.

WrittenCodeWrite instead
a % bDSH2201fmod(a, b)
a && b, a || b, !aDSH2200nested if statements, or max / min on 0/1 masks
a & b, a | b, a ^ b, ~aDSH2201a Function with an HLSL body
a << 2, a >> 2DSH2201a * 4.0, a / 4.0, or a Function
a < b, a > b, a <= b, a >= b, a == b, a != b outside an if conditionDSH2200an if condition
a ? b : cDSH2200if / else, lerp(c, b, mask), or a StaticSwitchParameter call
v[0]DSH2202a swizzle: v.x
a second comparison, && or || in an if conditionDSH2210nested if statements
// Refused (DSH2201); 1.x built this as UE.Time().
float Wave = UE.Time() % 1.0;

// Correct:
float Wave = fmod(UE.Time(), 1.0);
// Refused (DSH2210); 1.x built this as if (Color.r > 0.5).
if (Color.r > 0.5 && Color.g > 0.5) { Out = One; } else { Out = Zero; }

// Correct:
if (Color.r > 0.5) {
    if (Color.g > 0.5) { Out = One; } else { Out = Zero; }
} else {
    Out = Zero;
}

The restriction pass reports a refused construct once and does not look inside it, so a % b % c is one diagnostic; a % b + c % d is two.

Statement-level constructs

WrittenCodeWrite instead
return Color;, return;DSH2208assign the declared output variable: Color = …;
for (int i = 0; i < 3; i = i + 1) {}DSH2208unroll by hand, or move the loop into a Function whose body is real HLSL
while (t) {}, do {} while (t);DSH2208as above
break;, continue;, discard;DSH2208—
switch (Mode) {}DSH2160nested if statements, or a StaticSwitchParameter call
float Foo(float x) { return x; }DSH2154, at the ( where the declaration's ; belongsdeclare it at top level as a Function or GraphFunction and call it
struct S { float a; }DSH2151: struct cannot start an expressionuse MaterialAttributes, or a struct inside a Function body
{ … } with no if / else in frontDSH2220drop the braces
static / const on a variableDSH2212a const property
float w[4];DSH2213separate variables
a + b; — a value nothing receivesDSH2211assign it
#include "X.ush", #pragma …DSH1035: the preprocessor of a .dsm / .dsf knows no such lineimport at file level, or a Function that uses the include
#define K 2none in the Graph: the line belongs to the preprocessor, which uses a define in #if conditionsa const property

Graph bodies are read as statements, not as text split at ;, so a braced construct followed by more code no longer swallows that code into one statement (since 2.0.0).

Expression-level constructs

WrittenCode
x = a == b;DSH2200
a == b; (as a statement)DSH2211 — the comparison's value goes nowhere
x = a++;, a++;, --a;DSH2207
x = (float)a;DSH2203 — there are no C-style casts; write float(a)
x = (y = z);DSH2204
x = 0x10;DSH2222 — there are no hex literals; 1.x read 0x10 as 0
x = 1.0fx;, x = 0.5.5;DSH2105
x = "text";DSH4202
x = {1, 2, 3};, Attrs.BaseColor = {1, 0, 0};DSH2162 — a brace list is read only as a declaration's initializer
float4 v = {{1,2},{3,4}};DSH2214 — brace initializers do not nest
f(a b)DSH2152 — a missing ,
if (a) Color = X;DSH2209 — braces are mandatory
@, $, a backtickDSH2101

Compound assignment and increment

+= -= *= /= %=, the bitwise compound forms, ++ and -- are not 1.x operators.

WrittenCode
a += b;, a -= b;, a *= b;, a /= b;, a %= b;DSH2205
a++;, a--;, ++a;, --a;DSH2207

(since 2.0.0) the spacing no longer matters. 1.x read a += b; as a declaration of a variable named + of type a, and a+=b; as an assignment to a brand-new variable named a+, leaving a unchanged without a message; both are DSH2205 now. Write the expansion: a = a + b;.

// Refused (DSH2205).
Sum+=Tint;

// Correct:
Sum = Sum + Tint;

Wrong-case keywords

if and else are keywords and are matched case-sensitively, as are for, while, return, struct and the other HLSL keywords. A mis-cased spelling is a name.

WrittenResult
If (x) {}, IF (x) {}If (x) is a call to a function named If, and the { where that statement's ; belongs is DSH2154
if (x) {} Else {}, ELSE {}the if parses; Else is a name, and its { is DSH2154

Type tokens, constructor names and true / false are matched in any case. Other names are looked up exactly first and then ignoring case, with a warning (DSH5275, DSH5276 for engine names); see Name resolution.

Features that simply do not exist

FeatureStatusAlternative
Matrix types (float3x3, float4x4, mat2, mat3, mat4)the graph has no matrix values: DSH4361UE.TransformVector / UE.TransformPosition, or matrix locals inside a Function body
Arrays and indexingDSH2213, DSH2202swizzles for channels; Texture2DArray sampling for layers
inout parametersa 1.x function has in and out only: DSH6302pass an in and an out
Function declarations inside GraphDSH2154top-level Function / GraphFunction
Integer arithmeticabsent — int, uint, bool and half are floats in the graph; a / between two integer constructors is DSH4243see Expressions
Hex, octal and binary literalsDSH2222 for hexdecimal literals; see Literals
String valuesDSH4202 outside named UE.* arguments—
Ternary conditionalDSH2200if / else, lerp, StaticSwitchParameter
Comma operatorabsentseparate statements
Assignment inside an expressionDSH2204separate statements
Nested brace initializersDSH2214a constructor call: float4(float2(1,2), float2(3,4))
Preprocessor directivesonly the conditional set and #Region / #EndRegionimport, const properties
Code = { … } inside a Shaderremoved — DSH2246Graph = { … }

Diagnostics

CodeRaised when
DSH1035a # line other than the conditional set and #Region / #EndRegion — #include, #pragma
DSH2101a character that is not part of any token
DSH2105a malformed number: 0.5.5, 1.0fx, 1e
DSH2151an expression was expected and something else was found: struct, = 5;, if ()
DSH2152a ) is missing — often a missing , between two arguments
DSH2154a statement does not end with ;: a mis-cased If, a function definition, a final statement without ;
DSH2160switch, case or default at the start of a statement
DSH2162a brace list used as a value, outside a declaration
DSH2200a comparison, a logical operator or ?: outside an if condition
DSH2201%, a shift or a bitwise operator
DSH2202[ ]
DSH2203a C-style cast
DSH2204an assignment inside an expression
DSH2205a compound assignment
DSH2207++ or --
DSH2208for, while, do, return, break, continue, discard
DSH2209an if or else body without braces
DSH2210more than one comparison, or && / ||, in an if condition
DSH2211an expression statement whose value nothing receives
DSH2212a storage keyword on a Graph variable
DSH2213an array declarator
DSH2214a brace list inside a brace initializer
DSH2220a bare { … } block
DSH2222a hexadecimal literal
DSH2246a Code section in a Shader or function block
DSH4202a string used as a value
DSH4243both operands of / are integer constructor calls
DSH4361a matrix value
DSH6302an inout parameter on a 1.x function

The complete list, with every message, lives in the diagnostics index.

Example

A GLSL-shaped attempt and its DreamShaderLang equivalent.

// Refused.
Graph {
    vec2  uv    = UE.TexCoord(Index = 0);
    float t     = UE.Time() % 4.0;                 // DSH2201
    float mask  = uv.x > 0.5 && uv.y > 0.5;        // DSH2200
    vec3  col   = mask ? Hot : Cold;               // DSH2200
    col        *= Gain;                            // DSH2205
    Color = col;
}
// Correct.
Graph {
    vec2  uv = UE.TexCoord(Index = 0);
    float t  = fmod(UE.Time(), 4.0);

    vec3 col;
    if (uv.x > 0.5) {
        if (uv.y > 0.5) { col = Hot; } else { col = Cold; }
    } else {
        col = Cold;
    }

    Color = col * Gain;
}

Anything genuinely imperative — a loop, a bitwise mask, a switch — belongs in a Function, whose body is real HLSL and is compiled into a Custom node:

Function SelfContained float Ring(in float2 uv, in float count)
{
    float acc = 0.0;
    for (int i = 0; i < 4; ++i)
    {
        acc += sin(uv.x * count * (i + 1));
    }
    return acc * 0.25;
}
Graph {
    float r = Ring(uv, 8.0);
}

See also

  • Statements — the statement forms that are supported
  • Expressions — the four operators that exist, and their precedence
  • if / else — the only control flow, and the condition operators
  • Literals — accepted numeric forms, suffixes, and what is not a literal
  • Constructors — the replacement for casts and matrix construction
  • Swizzle — the replacement for indexing
  • Calls — moving imperative code into a callable function
  • Function — HLSL bodies, where loops and bitwise operators are legal
  • GraphFunction — HLSL bodies with UE.* nodes hoisted into pins
  • Math builtins — fmod, min, max, clamp, lerp
  • Name resolution — the case-sensitivity matrix
  • dsc migrate — rewrite a 1.x file as .dss, where loops and ?: exist
  • Diagnostics index — every code, by stage

On this page