A statement is a declaration when it starts with two names in a row — a type token and the variable's
name (since 2.0.0; 1.x split the text at its last top-level whitespace). Both are single tokens.
[!NOTE]
The name is one identifier. float3 A.B = x; is DSH2154 at
the .(since 2.0.0); 1.x declared a variable literally named A.B. A member write has no type in
front of it: Attrs.BaseColor = x;.
a sampler of its own, no longer a Texture2D(since 2.0.0); requires an initializer
Every row is matched case-insensitively (Float3, VEC3, materialattributes). A type token is one
token (since 2.0.0): float 3 and Material Attributes are no longer read as types.
vec*, ivec*, uvec* and bvec* read as float*, int*, uint* and bool*.
int, uint, bool and half are kinds of their own to the compiler — a / between two integer
constructors is DSH4243, a condition is a bool — and every one of them is
a float in the generated graph. See Type tokens. The matrix spellings
(mat3, float3x3) resolve, but the graph has no matrix values:
DSH4361.
(since 2.0.0) They no longer do: the declared type is the variable's type. 1.x let certain values
(UE.TexCoord, UE.CameraVectorWS, a constant-folded constructor, dot, …) keep their own width
whatever the declaration said, so float2 dir = UE.CameraVectorWS(); made dir three components
without a message. Now dir is a float2: the leading two components of the value, said by
DSH5289 when the value is typed wider.
Graph = { float3 c = Tint; // Tint is a float4 property: DSH5289, c = Tint.rgb float2 dir = UE.CameraVectorWS(); // dir is two components}
A 1.x source that relied on the override builds a different graph than it did through 1.9.x, and
nothing reports it: dsc migrate compares what the 2.0 compiler builds from the
1.x file with what it builds from the .dss, and the two agree. Rebuild such a material and check it.
Binary operators never narrow an operand: two operands of different widths, neither of them a
scalar, are DSH4226. Assignment, declaration, function inputs
and pins take the leading components (DSH5289). See Conversions.
A brace list as the initializer of a declaration is rewritten to a constructor call of the declared
type: T x = { a, b }; is T x = T(a, b);. Brace initializers are therefore exactly constructor
calls and inherit every rule of Constructors: positional arguments only
(DSH4225), a single scalar spreads to every component, and
several arguments must add up to exactly the type's width
(DSH4222).
{} is special-cased: it is no initializer at all, so the variable gets the zero of its type.
The target type is always the declared type token. A brace list anywhere else — on the right of an
assignment or a member write, as an argument — is DSH2162(since 2.0.0). A texture, Substrate or MaterialAttributes type has no constructor:
Texture2D t = {x}; is DSH4223.
[!WARNING]
Nested braces do not work. float4 m = {{1,2},{3,4}}; is
DSH2214; write float4(float2(1, 2), float2(3, 4)).
Graph = { float r = 1.0; float g = 0.5; float b = 0.2; vec3 rgb = {r, g, b}; // -> vec3(r, g, b) vec4 rgba = {rgb, 1.0}; // -> vec4(rgb, 1.0), mixed-width packing Color = rgba.rgb;}
A declaration whose name is already declared in the same block is
DSH4220. So is a declaration inside an if or else body of a
name the enclosing body already declares, and in a Shader a declaration named Base, which is the
material itself.
The check compares names exactly(since 2.0.0): float a = 1; float A = 2; declares two
variables, where 1.x reported a redeclaration. A read of a or A finds its own variable; a read
in a case that matches neither exactly finds a unique case-insensitive match, with the warning
DSH5275.
Assignment to an existing name is not a redeclaration — omit the type token to reassign. See
Statements.
Each if and else body is a block with a scope of its own (since 2.0.0); 1.x had one value map
for the whole body.
Situation
Visibility after the statement
Declaration at body level
visible for the rest of the body. In a Shader that body also holds the Outputs declarations, before the Graph statements
Declaration inside an if or else body
visible to the end of that body only
A property read inside a branch
a property is a file-scope input, never a branch value
Case
Outcome
a variable declared before the if and assigned in a branch
its two values are merged into one conditional node, and the variable keeps the merged value after the if — see if / else
a variable declared inside a branch
dropped when the branch ends; nothing is merged, and a read after the if is DSH4200
a branch declares a name the enclosing body already declares
DSH4220
[!WARNING]
1.x merged a name declared in both branches and let it be read after the if. That name is now
branch-local. Declare the variable before the if and assign it in each branch: