yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

aidanfnvFix broken links in User Guide (#7938)6d399804a

master
42.1 KiB1186 linesraw

layout: user-guide permalink: /user-guide/conventional-features

Conventional Language Features

Many of the language concepts in Slang are similar to those in other real-time shading languages like HLSL and GLSL, and also to general-purpose programming languages in the "C family." This chapter covers those parts of the Slang language that are conventional and thus unlikely to surprise users who are already familiar with other shading languages, or languages in the C family.

Readers who are comfortable with HLSL variables, types, functions, statements, as well as conventions for shader parameters and entry points may prefer to skip this chapter. Readers who are not familiar with HLSL, but who are comfortable with GLSL and/or C/C++, may want to carefully read the sections on types, expressions, shader parameters, and entry points while skimming the others.

Types

Slang supports conventional shading language types including scalars, vectors, matrices, arrays, structures, enumerations, and resources.

Note

Slang has limited support for pointers when targeting platforms with native pointer support, including SPIRV, C++, and CUDA.

Scalar Types

Integer Types

The following integer types are provided:

NameDescription
int8_t8-bit signed integer
int16_t16-bit signed integer
int32-bit signed integer
int64_t64-bit signed integer
uint8_t8-bit unsigned integer
uint16_t16-bit unsigned integer
uint32-bit unsigned integer
uint64_t64-bit unsigned integer

All targets support the 32-bit int and uint types, but support for the other types depends on the capabilities of each target platform.

Integer literals can be both decimal and hexadecimal. An integer literal can be explicitly made unsigned with a u suffix, and explicitly made 64-bit with the ll suffix. The type of a decimal non-suffixed integer literal is the first integer type from the list [int, int64_t] which can represent the specified literal value. If the value cannot fit, the literal is represented as an uint64_t and a warning is given. The type of hexadecimal non-suffixed integer literal is the first type from the list [int, uint, int64_t, uint64_t] that can represent the specified literal value. For more information on 64 bit integer literals see the documentation on 64 bit type support.

The following floating-point types are provided:

NameDescription
half16-bit floating-point number
float32-bit floating-point number
double64-bit floating-point number

All targets support the 32-bit float, but support for the other types depends on the capabilities of each target platform.

Boolean Type

The type bool is used to represent Boolean truth values: true and false.

For compatibility reasons, the sizeof(bool) depends on the target.

Targetsizeof(bool)
GLSL4 bytes / 32-bit value
HLSL4 bytes / 32-bit value
CUDA1 bytes / 8-bit value

Note

When storing bool types in structures, make sure to either pad host-side data structures accordingly, or store booleans as, eg, uint8_t, to guarantee consistency with the host language's boolean type.

The Void Type

The type void is used as a placeholder to represent the result type of functions that don't return anything.

Vector Types

Vector types can be written as vector<T,N> where T is a scalar type and N is an integer from 2 to 4 (inclusive). The type vector<T,N> is a vector of N elements (also called components) each of type T.

As a convenience, pre-defined vector types exist for each scalar type and valid element count, with a name using the formula <<scalar-type>><<element-count>>. For example, float3 is a convenient name for vector<float,3>.

Note: Slang doesn't support vectors longer than 4 elements. They map to native vector types on many platforms, including CUDA, and none of these platforms support vectors longer than 4 elements. If needed, you can use an array like float myArray[8].

Matrix Types

Matrix types can be written as matrix<T,R,C> where T is a scalar type and both R and C are integers from 2 to 4 (inclusive). The type matrix<T,R,C> is a matrix with elements of type T, and comprising R rows and C columns.

As a convenience, pre-defined matrix types exist for each scalar type and valid row/column count, with a name using the formula <<scalar-type>><<row-count>>x<<column-count>>. For example, a float3x4 is a convenient name for matrix<float,3,4>.

Note

Readers familiar with GLSL should be aware that a Slang float3x4 represents a matrix with three rows and four columns, while a GLSL mat3x4 represents a matrix with three columns and four rows. In most cases, this difference is immaterial because the subscript expression m[i] returns a float4 (vec4) in either language. For now it is enough to be aware that there is a difference in convention between Slang/HLSL/D3D and GLSL/OpenGL.

Array Types

An array type T[N] represents an array of N elements of type T. When declaring a variable with an array type, the [] brackets come after the variable name, following the C convention for variable declarations:

// the type of `a` is `int[3]`
int a[3];

Sometimes a value with an array type can be declared without an explicit element count. In some cases the element count is then inferred from the initial value of a variable:

// the type of `a` is `int[3]`
int a[] = { 1, 2, 3 };

In other cases, the result is a unsized array, where the actual element count will be determined later:

// the type of `b` is `int[]`
void f( int b[] )
{ ... }

It is allowed to pass a sized array as argument to an unsized array parameter when calling a function.

Array types has a getCount() member function that returns the length of the array.

int f( int b[] )
{
    return b.getCount(); // Note: all arguments to `b` must be resolvable to sized arrays.
}

void test()
{
    int arr[3] = { 1, 2, 3 };
    int x = f(arr); // OK, passing sized array to unsized array parameter, x will be 3.
}

Please note that if a function calls getCount() method on an unsized array parameter, then all calls to that function must provide a sized array argument, otherwise the compiler will not be able to resolve the size and will report an error. The following code shows an example of valid and invalid cases.

int f( int b[] )
{
    return b.getCount();
}
int g( int b[] )
{
    return f(b); // transitive calls are allowed.
}
uniform int unsizedParam[];
void test()
{
    g(unsizedParam); // Not OK, `unsizedParam` doesn't have a known size at compile time.
    int arr[3];
    g(arr); // OK.
}

There are more limits on how runtime-sized arrays can be used than on arrays of statically-known element count.

Note

In Slang arrays are value types, meaning that assignment, parameter passing, etc. semantically copy values of array type. In some languages -- notably C, C++, C#, and Java -- assignment and parameter passing for treat arrays as reference types, meaning that these operations assign/pass a reference to the same underlying storage.

Structure Types

Structure types can be introduced with the struct keyword, as in most C-family languages:

struct MyData
{
    int a;
    float b;
}

Note

Unlike C, and like most other C-family languages, the struct keyword in Slang introduces a type directly, and there is no need to combine it with a typedef.

Note

Slang allows for a trailing semicolon (;) on struct declarations, but does not require it.

Note

Unlike C/C++, class is not a valid keyword for GPU code and it is reserved for CPU/host side logic.

Structure types can have constructors. Constructors are defined with the __init keyword:

struct MyData
{
     int a;
     __init() { a = 5; }
     __init(int t) { a = t; }
}
void test()
{
     MyData d;  // invokes default constructor, d.a = 5
     MyData h = MyData(4); // invokes overloaded constructor, h.a = 4
}

Default Values for Struct Members

Alternatively, you can specify default values of members in the struct like so:

struct MyData
{
     int a = 1;
     float3 b = float3(0.5);
}
void test()
{
     MyData data = {}; // will initialize data.a to 1 and data.b to {0.5, 0.5, 0.5}
     MyData data2 = MyData(); // equivalent to MyData data2 = {};
     MyData data3; // data3.a and data3.b will be undefined !    
}

Enumeration Types

Enumeration types can be introduced with the enum keyword to provide type-safe constants for a range of values:

enum Channel
{
    Red,
    Green,
    Blue
}

Unlike C/C++, enum types in Slang are always scoped by default (like enum class in C++). You can write enum class in Slang if it makes you happy, but it isn't required. If you want a enum type to be unscoped, you can use the [UnscopedEnum] attribute:

[UnscopedEnum]
enum Channel
{
    Red, Green, Blue
}
void test(Channel c)
{
    if (c == Red) { /*...*/ }
}

You can specify an explicit underlying integer type for enum types:

enum Channel : uint16_t
{
    Red, Green, Blue
}

By default, the underlying type of an enumeration type is int. Enumeration types are implicitly convertible to their underlying type. All enumeration types conform to the builtin ILogical interface, which provides operator overloads for bit operations. The following code is allowed:

void test()
{
    Channel c = Channel.Red | Channel.Green;
}

You can explicitly assign values to each enum case:

enum Channel
{
    Red = 5,
    Green,   // = 6
    Blue     // = 7
}

Slang automatically assigns integer values to enum cases without an explicit value. By default, the value starts from 0 and is increment by 1 for each enum case.

You can override the implicit value assignment behavior with the [Flags] attribute, which will make value assignment start from 1 and increment by power of 2, making it suitable for enums that represent bit flags. For example:

[Flags]
enum Channel
{
    Red,   //  = 1
    Green, //  = 2
    Blue,  //  = 4
    Alpha, //  = 8
}

Opaque Types

The Slang core module defines a large number of opaque types which provide access to objects that are allocated via GPU APIs.

What all opaque types have in common is that they are not "first-class" types on most platforms. Opaque types (and structure or array types that contain them) may be limited in the following ways (depending on the platform):

  • Functions that return opaque types may not be allowed
  • Global and static variables that use opaque types may not be allowed
  • Opaque types may not appear in the element types of buffers, except where explicitly noted as allowed

Texture Types

Texture types -- including Texture2D, TextureCubeArray, RWTexture2D, and more -- are used to access formatted data for read, write, and sampling operations. Textures can be used to represent simple images, but also support mipmapping as a way to reduce noise when sampling at lower than full resolution. The full space of texture types follows the formula:

<<access>>Texture<<base shape>><<multisampleness>><<arrayness>><<element type>>

where:

  • The access can be read-only (no prefix), read-write (RW), or read-write with a guarantee of rasterization order for operations on the given resource (RasterizerOrdered).
  • The base shape can be 1D, 2D, 3D, or Cube.
  • The multisample-ness can be non-multiple-sample, or multi-sampled (MS).
  • The array-ness can either be non-arrayed, or arrayed (Array).
  • The element type can either be explicitly specified (<T>) or left as the default of float4

Not all combinations of these options are supported, and some combinations may be unsupported on some targets.

Sampler

Sampler types encapsulate parameters that control addressing and filtering for texture-sampling operations. There are two sampler types: SamplerState and SamplerComparisonState. SamplerState is applicable to most texture sampling operations, while SamplerComparisonState is used for "shadow" texture sampling operations which compare texels to a reference value before filtering.

Note

Some target platforms and graphics APIs do not support separation of textures and sampling state into distinct types in shader code. On these platforms the Slang texture types include their own sampling state, and the sampler types are placeholder types that carry no data.

Buffers

There are multiple buffer types supported by modern graphics APIs, with substantially different semantics.

Formatted Buffers

Formatted buffers (sometimes referred to as "typed buffers" or "buffer textures") are similar to 1D textures (in that they support format conversion on loads), without support for mipmapping. The formula for formatted buffer types is:

<<access>>Buffer<<arrayness>><<element type>>

Where the access, array-ness, and element type are the same as for textures, with the difference that element type is not optional.

A buffer type like Buffer<float4> represents a GPU resource that stores one or more values that may be fetched as a float4 (but might internally be stored in another format, like RGBA8).

Flat Buffers

Flat buffers differ from formatted buffers in that they do not support format conversion. Flat buffers are either structured buffers or byte-addressed buffers.

Structured buffer types like StructuredBuffer<T> include an explicit element type T that will be loaded and stored from the buffer. Byte-addressed buffer types like ByteAddressBuffer do not specify any particular element type, and instead allow for values to be loaded or stored from any (suitably aligned) byte offset in the buffer. Both structured and byte-addressed buffers can use an access to distinguish between read-only and read-write usage.

Constant Buffers

Constant buffers (sometimes also called "uniform buffers") are typically used to pass immutable parameter data from a host application to GPU code. The constant buffer type ConstantBuffer<T> includes an explicit element type. Unlike formatted or flat buffers, a constant buffer conceptually contains only a single value of its element type, rather than one or more values.

Expressions

Slang supports the following expression forms with nearly identical syntax to HLSL, GLSL, and C/C++:

  • Literals: 123, 4.56, false

Note

Unlike C/C++, but like HLSL/GLSL, an unsuffixed floating-point literal has the float type in Slang, rather than double

  • Member lookup: structValue.someField, MyEnumType.FirstCase

  • Function calls: sin(a)

  • Vector/matrix initialization: int4(1, 2, 3, 4)

  • Casts: (int)x, double(0.0)

  • Subscript (indexing): a[i]

  • Initializer lists: int b[] = { 1, 2, 3 };

  • Assignment: l = r

  • Operators: -a, b + c, d++, e %= f

Note

Like HLSL but unlike most other C-family languages, the && and || operators do not currently perform "short-circuiting". they evaluate all of their operands unconditionally. However, the ?: operator does perform short-circuiting if the condition is a scalar. Use of ?: where the condition is a vector is deprecated in Slang. The vector version of ?: operator does not perform short-circuiting, and the user is advised to call select instead. The default behavior of these operators is likely to change in a future Slang release.

Additional expression forms specific to shading languages follow.

Operators on Vectors and Matrices

The ordinary unary and binary operators can also be applied to vectors and matrices, where they apply element-wise.

Note

In GLSL, most operators apply component-wise to vectors and matrices, but the multiplication operator * computes the traditional linear-algebraic product of two matrices, or a matrix and a vector. Where a GLSL programmer would write m * v to multiply a mat3x4 by a vec3, a Slang programmer should write mul(v,m) to multiply a float3 by a float3x4. In this example, the order of operands is reversed to account for the difference in row/column conventions.

Swizzles

Given a value of vector type, a swizzle expression extracts one or more of the elements of the vector to produce a new vector. For example, if v is a vector of type float4, then v.xy is a float2 consisting of the x and y elements of v. Swizzles can reorder elements (v.yx) or include duplicate elements (v.yyy).

Note

Unlike GLSL, Slang only supports xyzw and rgba as swizzle elements, and not the seldom-used stpq.

Note

Unlike HLSL, Slang does not currently support matrix swizzle syntax.

Statements

Slang supports the following statement forms with nearly identical syntax to HLSL, GLSL, and C/C++:

  • Expression statements: f(a, 3);, a = b * c;

  • Local variable declarations: int x = 99;

  • Blocks: { ... }

  • Empty statement: ;

  • if statements

  • switch statements

Note

Unlike C/C++, case and default statements must be directly nested under a switch, rather than being allowed under nested control flow (Duff's Device and similar idioms are not allowed). In addition, while multiple cases can be grouped together, all other forms of "fall through" are unsupported.

  • for statements

  • while statements

  • do-while statements

  • break statements

  • continue statements

  • return statements

  • defer statements

Note

The defer statement in Slang is tied to scope. The deferred statement runs at the end of the scope like in Swift, not just at the end of the function like in Go. defer supports but does not require block statements: both defer f(); and defer { f(); g(); } are legal.

Note

Slang does not support the C/C++ goto keyword.

Note

Slang does not support the C++ throw keyword.

Additional statement forms specific to shading languages follow.

Discard Statements

A discard statement can be used in the context of a fragment shader to terminate shader execution for the current fragment, and to cause the graphics system to discard the corresponding fragment.

Functions

Slang supports function definitions with traditional C syntax:

float addSomeThings(int x, float y)
{
    return x + y;
}

In addition to the traditional C syntax, you can use the modern syntax to define functions with the func keyword:

func addSomeThings(x : int, y : float) -> float
{
    return x + y;
}

Slang supports overloading of functions based on parameter types.

Function parameters may be marked with a direction qualifier:

  • in (the default) indicates a by-value input parameter
  • out indicates an output parameter
  • inout or in out indicates an input/output parameter

Note

The out and inout directions are superficially similar to non-const reference parameters in C++. In cases that do not involve aliasing of mutable memory, the semantics should be equivalent.

Preprocessor

Slang supports a C-style preprocessor with the following directives;

  • #include
  • #define
  • #undef
  • #if, #ifdef, #ifndef
  • #else, #elif
  • #endif
  • #error
  • #warning
  • #line
  • #pragma, including #pragma once

Variadic macros are supported by the Slang preprocessor.

Note

The use of #include in new code is discouraged as this functionality has been superseded by the module system, please refer to Modules and Access Control

Attributes

Attributes are a general syntax for decorating declarations and statements with additional semantic information or meta-data. Attributes are surrounded with square brackets ([]) and prefix the declaration or statement they apply to.

For example, an attribute can indicate the programmer's desire that a loop be unrolled as much as possible:

[unroll]
for(int i = 0; i < n; i++)
{ /* ... */ }

Note

Traditionally, all attributes in HLSL used a single layer of [] brackets, matching C#. Later, C++ borrowed the idea from C# but used two layers of brackets ([[]]). Some recent extensions to HLSL have used the C++-style double brackets instead of the existing single brackets syntax. Slang tries to support both alternatives uniformly.

Global Variables and Shader Parameters

By default, global-scope variable declarations in Slang represent shader parameters passed from host application code into GPU code. Programmers must explicitly mark a global-scope variable with static for it not to be treated as a shader parameter, even if the variable is marked const:

// a shader parameter:
float a;

// also a shader parameter (despite `const`):
const int b = 2;

// a "thread-local" global variable
static int c = 3;

// a compile-time constant
static const int d = 4;

Global Constants

A global-scope static const variable defines a compile-time constant for use in shader code.

Global-Scope Static Variables

A non-const global-scope static variable is conceptually similar to a global variable in C/C++, with the key difference that it has distinct storage per thread rather than being truly global. Each logical thread of shader execution initiated by the GPU will be allocated fresh storage for these static variables, and values written to those variables will be lost when a shader thread terminates.

Note

Some target platforms do not support static global variables in all use cases. Support for static global variables should be seen as a legacy feature, and further use is discouraged.

Global Shader Parameters

Global shader parameters may use any type, including both opaque and non-opaque types:

ConstantBuffer<MyData> c;
Texture2D t;
float4 color;

To avoid confusion, the Slang compiler will warn on any global shader parameter that includes non-opaque types, because it is likely that a user thought they were declaring a global constant or a traditional global variable. This warning may be suppressed by marking the parameter as uniform:

// WARNING: this declares a global shader parameter, not a global variable
int gCounter = 0;

// OK:
uniform float scaleFactor;

Legacy Constant Buffer Syntax

For compatibility with existing HLSL code, Slang also supports global-scope cbuffer declarations to introduce constant buffers:

cbuffer PerFrameCB
{
    float4x4 mvp;
    float4 skyColor;
    // ...
}

A cbuffer declaration like this is semantically equivalent to a shader parameter declared using the ConstantBuffer type:

struct PerFrameData
{
    float4x4 mvp;
    float4 skyColor;
    // ...
}
ConstantBuffer<PerFrameData> PerFrameCB;

Explicit Binding Markup

For compatibility with existing codebases, Slang supports pre-existing markup syntax for associating shader parameters of opaque types with binding information for specific APIs.

Binding information for Direct3D platforms may be specified using register syntax:

Texture2D a : register(t0);
Texture2D b : register(t1, space0);

Binding information for Vulkan (and OpenGL) may be specified using [[vk::binding(...)]] attributes

[[vk::binding(0)]]
Texture2D a;

[[vk::binding(1, 0)]]
Texture2D b;

A single parameter may use both the D3D-style and Vulkan-style markup, but in each case explicit binding markup only applies to the API family for which it was designed.

Note

Explicit binding markup is tedious to write and error-prone to maintain. It is almost never required in Slang codebases. The Slang compiler can automatically synthesize bindings in a completely deterministic fashion and in most cases the bindings it generates are what a programmer would have written manually.

Shader Entry Points

An entry point is a function that can be used as the starting point for execution of a GPU thread.

Here is an example of an entry-point function in Slang:

[shader("vertex")]
float4 vertexMain(
    float3 modelPosition : POSITION,
    uint vertexID : SV_VertexID,
    uniform float4x4 mvp)
    : SV_Position
{ /* ... */ }

In the following sections we will use this example to explain important facets of entry point declarations in Slang.

Entry Point Attribute and Stages

The [shader(...)] attribute is used to mark a function in Slang as a shader entry point, and also to specify which pipeline stage it is meant for. In this example, the vertexMain shader indicates that it is meant for the vertex stage of the traditional rasterization pipeline. Rasterization, compute, and ray-tracing pipelines each define their own stages, and new versions of graphics APIs may introduce new stages.

For compatibility with legacy codebases, Slang supports code that leaves off [shader(...)] attributes; in these cases application developers must specify the names and stages for their entry points via explicit command-line or API options. Such entry points will not be found via IModule::findEntryPointByName(). Instead IModule::findAndCheckEntryPoint() must be used, and a stage must be specified. It is recommended that new codebases always use [shader(...)] attributes both to simplify their workflow, and to make code more explicit and "self-documenting."

Note

In GLSL, a file of shader code may only include one entry point, and all code #included into that file must be compatible with the stage of that entry point. By default, GLSL requires that an entry point be called main. Slang allows for multiple entry points to appear in a file, for any combination of stage, and with any valid identifier as a name.

Parameters

The parameter of an entry-point function represent either varying or uniform inputs. Varying inputs are those that may vary over threads invoked as part of the same batch (a draw call, compute dispatch, etc.), while uniform inputs are those that are guaranteed to be the same for all threads in a batch. Entry-point parameters in Slang default to varying, but may be explicitly marked uniform.

If an entry-point function declares a non-void result type, then its result behaves like an anonymous out parameter that is varying.

Binding Semantics

The varying parameters of an entry point must declare a binding semantic to indicate how those parameters should be connected to the execution environment. A binding semantic for a parameter may be introduced by suffixing the variable name with a colon (:) and an identifier for the chosen binding semantic. A binding semantic for a function result is introduced similarly, but comes after the parameter list.

It is not shown in this example, but binding semantics may also be applied to individual struct fields, in cases where a varying parameter of struct type is used.

System-Defined Binding Semantics

In the vertexMain entry point, the vertexID parameter uses the SV_VertexID binding semantic, which is a system-defined binding semantic. Standard system-defined semantics are distinguished by the SV_ prefix.

A system-defined binding semantic on an input parameter indicates that the parameter should receive specific data from the GPU as defined by the pipeline and stage being used. For example, in a vertex shader the SV_VertexID binding semantic on an input yields the ID of the particular vertex being processed on the current thread.

A system-defined binding semantic on an output parameter or function result indicates that when a shader thread returns from the entry point the value stored in that output should be used by the GPU in a specific way defined by the pipeline and stage being used. For example, in a vertex shader the SV_Position binding semantic on an output indicates that it represents a clip-space position that should be communicated to the rasterizer.

The set of allowed system-defined binding semantics for inputs and outputs depends on the pipeline and stage of an entry point. Some system-defined binding semantics may only be available on specific targets or specific versions of those targets.

Note

Instead of using ordinary function parameters with system-defined binding semantics, GLSL uses special system-defined global variables with the gl_ name prefix. Some recent HLSL features have introduced special globally-defined functions that behave similarly to these gl_ globals.

User-Defined Binding Semantics

In the vertexMain entry point, the modelPosition parameter used the POSITION binding semantic, which is a user-defined binding semantic.

A user-defined binding semantic on an input indicates that the parameter should receive data with a matching binding semantic from a preceding stage. A user-defined binding semantic on an output indicates that the parameter should provide data to a parameter with a matching binding semantic in a following stage.

Whether or not inputs and outputs with user-defined binding semantics are allowed depends on the pipeline and stage of an entry point.

Different APIs and different stages within the same API may match up entry point inputs/outputs with user-defined binding semantics in one of two ways:

  • By-index matching: user-defined outputs from one stage and inputs to the next are matched up by order of declaration. The types of matching output/input parameters must either be identical or compatible (according to API-specific rules). Some APIs also require that the binding semantics of matching output/input parameters are identical.

  • By-name matching: user-defined outputs from one stage and inputs to the next are matched up by their binding semantics. The types of matching output/input parameters must either be identical or compatible (according to API-specific rules). The order of declaration of the parameters need not match.

Because the matching policy may differ across APIs, the only completely safe option is for parameters passed between pipeline stages to match in terms of order, type, and binding semantic.

Note

Instead of using ordinary function parameters for user-defined varying inputs/outputs, GLSL uses global-scope variable declarations marked with the in or out modifier.

Entry-Point Uniform Parameters

In the vertexMain entry point, the mvp parameter is an entry-point uniform parameter.

Entry-point uniform parameters are semantically similar to global-scope shader parameters, but do not pollute the global scope.

Note

GLSL does not support entry-point uniform parameters; all shader parameters must be declared at the global scope. Historically, HLSL has supported entry-point uniform parameters, but this feature was dropped by recent compilers.

Mixed Shader Entry Points

Through the [shader(...)] syntax, users of slang can freely combine multiple entry points into the same file. This can be especially convenient for reuse between entry points which have a logical connection.

For example, mixed entry points offer a convenient way for ray tracing applications to concisely define a complete pipeline in one source file, while also providing users with additional opportunities to improve type safety of shared structure definitions:

struct Payload { float3 color; };

[shader("raygeneration")]
void rayGenerationProgram() {
    Payload payload;
    TraceRay(/*...*/, payload);
    /* ... */ 
}

[shader("closesthit")]
void closestHitProgram(out Payload payload) { 
    payload.color = {1.0};
}

[shader("miss")]
void missProgram(out Payload payload) { 
    payload.color = {1.0};
}

Note

GLSL does not support multiple entry-points; however, SPIR-V does. Vulkan users wanting to take advantage of Slang mixed entry points must pass -fvk-use-entrypoint-name and -emit-spirv-directly as compiler arguments.

Mixed Entry-Point Uniform Parameters

Like with the previous vertexMain example, mixed entry point setups also support entry-point uniform parameters.

However, because of certain systematic differences between entry point types, a uniform being global or local will have very important consequences on the underlying layout and behavior.

For most all entry point types, D3D12 will use one common root signature to define both global and local uniform parameters. Likewise, Vulkan descriptors will bind to a common pipeline layout. For both of these cases, Slang maps uniforms to the common root signature / pipeline layout.

However, for ray tracing entry points and D3D12, these parameters map to either global root signatures or to local root signatures, with the latter being stored in the shader binding table. In Vulkan, D3D12's global root signatures translate to a shared ray tracing pipeline layout, while local root signatures map again to shader binding table records.

When entry points match a "ray tracing" type, we bind uniforms which are in the global scope to the global root signature (or ray tracing pipeline layout), while uniforms which are local are bound to shader binding table records, which depend on the underlying runtime record indexing.

Consider the following:

uniform float3 globalUniform;

[shader("compute")][numThreads(1,2,3)]
void computeMain1(uniform float3 localUniform1) 
{ /* ... */ }

[shader("compute")][numThreads(1,2,3)]
void computeMain2(uniform float3 localUniform2) 
{ /* ... */ }

[shader("raygeneration")]
void rayGenerationMain(uniform float3 localUniform3) 
{ /* ... */ }

[shader("closesthit")]
void closestHitMain(uniform float3 localUniform4) 
{ /* ... */ }

In this example, globalUniform is appended to the global root signature / pipeline layouts for both compute and ray generation stages for all four entry points. Compute entry points lack "local root signatures" in D3D12, and likewise Vulkan has no concept of "local" vs "global" compute pipeline layouts, so localUniform1 is "pushed" to the stack of reserved global uniform parameters for use in computeMain1. Leaving that entry point scope "pops" that global uniform parameter such that localUniform2 can reuse the same binding location for computeMain2. However, local uniforms for ray tracing shaders map to the corresponding "local" hit records in the shader binding table, and so no "push" or "pop" to the global root signature / pipeline layouts occurs for these parameters.

Auto-Generated Constructors

Auto-Generated Constructors - Struct

Slang has the following rules:

  1. Auto-generate a __init() if not already defined.

    Assume:

    struct DontGenerateCtor
    {
        int a;
        int b = 5;
    
        // Since the user has explicitly defined a constructor
        // here, Slang will not synthesize a conflicting 
        // constructor.
        __init()
        {
            // b = 5;
            a = 5;
            b = 6;
        }
    };
    
    struct GenerateCtor
    {
        int a;
        int b = 5;
    
        // Slang will automatically generate an implicit constructor:
        // __init()
        // {
        //     b = 5;
        // }
    };
  2. If all members have equal visibility, auto-generate a 'member-wise constructor' if not conflicting with a user defined constructor.

    struct GenerateCtorInner
    {
        int a;
    
        // Slang will automatically generate an implicit
        // __init(int in_a)
        // {
        //     a = in_a;
        // }
    };
    struct GenerateCtor : GenerateCtorInner
    {
        int b;
        int c = 5;
    
        // Slang will automatically generate an implicit
        // __init(int in_a, int in_b, int in_c)
        // {
        //     c = 5;
        //
        //     this = GenerateCtorInner(in_a);
        //
        //     b = in_b;
        //     c = in_c;
        // }
    };
  3. If not all members have equal visibility, auto-generate a 'member-wise constructor' based on member visibility if not conflicting with a user defined constructor.

    We generate 3 different visibilities of 'member-wise constructor's in order:

    1. public 'member-wise constructor'
      • Contains members of visibility: public
      • Do not generate if internal or private member lacks an init expression
    2. internal 'member-wise constructor'
      • Contains members of visibility: internal, public
      • Do not generate if private member lacks an init expression
    3. private 'member-wise constructor'
      • Contains members of visibility: private, internal, public
    struct GenerateCtorInner1
    {
        internal int a = 0;
     
        // Slang will automatically generate an implicit
        // internal __init(int in_a)
        // {
        //     a = 0;
        //
        //     a = in_a;
        // }
    };
    struct GenerateCtor1 : GenerateCtorInner1
    {
        internal int b = 0;
        public int c;
    
        // Slang will automatically generate an implicit
        // internal __init(int in_a, int in_b, int in_c)
        // {
        //     b = 0;
        //
        //     this = GenerateCtorInner1(in_a);
        //
        //     b = in_b;
        //     c = in_c;
        // }
        //
        // public __init(int in_c)
        // {
        //     b = 0;
        //
        //     this = GenerateCtorInner1();
        //
        //     c = in_c;
        // }
    };
    
    struct GenerateCtorInner2
    {
        internal int a;
        // Slang will automatically generate an implicit
        // internal __init(int in_a)
        // {
        //     a = in_a;
        // }
    };
    struct GenerateCtor2 : GenerateCtorInner2
    {
        internal int b;
        public int c;
    
        /// Note: `internal b` is missing init expression,
        // Do not generate a `public` 'member-wise' constructor.
    
        // Slang will automatically generate an implicit
        // internal __init(int in_a, int in_b, int in_c)
        // {
        //     this = GenerateCtorInner2(in_a);
        //
        //     b = in_b;
        //     c = in_c;
        // }
    };

Initializer Lists

Initializer Lists are an expression of the form {...}.

int myFunc()
{
    int a = {}; // Initializer List
}

Initializer Lists - Scalar

// Equivalent to `int a = 1`
int a = {1};

Initializer Lists - Vectors

// Equivalent to `float3 a = float3(1,2,3)`
float3 a = {1, 2, 3};

Initializer Lists - Arrays/Matrices

Array Of Scalars

// Equivalent to `int[2] a; a[0] = 1; a[1] = 2;`
int a[2] = {1, 2}

Array Of Aggregates

// Equivalent to `float3 a[2]; a[0] = {1,2,3}; b[1] = {4,5,6};`
float3 a[2] = { {1,2,3}, {4,5,6} };

Flattened Array Initializer

// Equivalent to `float3 a[2] = { {1,2,3}, {4,5,6} };`
float3 a[3] = {1,2,3, 4,5,6}; 

Initializer Lists - Struct

In most scenarios, using an initializer list to create a struct typed value is equivalent to calling the struct's constructor using the elements in the initializer list as arguments for the constructor, for example:

struct GenerateCtorInner1
{
    internal int a = 0;
    
    // Slang will automatically generate an implicit
    // internal __init(int in_a)
    // {
    //     a = 0;
    //
    //     a = in_a;
    // }

    static GenerateCtorInner1 callGenerateCtorInner1()
    {
        // Calls `GenerateCtorInner1::__init(1);`
        return {1};
    }
};
struct GenerateCtor1 : GenerateCtorInner1
{
    internal int b = 0;
    public int c;

    // Slang will automatically generate an implicit
    // internal __init(int in_a, int in_b, int in_c)
    // {
    //     this = GenerateCtorInner1(in_a);
    //
    //     b = 0;
    //
    //     b = in_b;
    //     c = in_c;
    // }
    //
    // public __init(int in_c)
    // {
    //     this = GenerateCtorInner1();
    //
    //     b = 0;
    //
    //     c = in_c;
    // }
    static GenerateCtorInner1 callInternalGenerateCtor()
    {
        // Calls `GenerateCtor1::__init(1, 2, 3);`
        return {1, 2, 3};
    }
    static GenerateCtorInner1 callPublicGenerateCtor()
    {
        // Calls `GenerateCtor1::__init(1);`
        return {1}; 
    }
};

...

// Calls `{ GenerateCtor1::__init(3), GenerateCtor1::__init(2) }`
GenerateCtor1 val[2] = { { 3 }, { 2 } };

In addition, Slang also provides compatibility support for C-style initializer lists with structs. C-style initializer lists can use Partial Initializer List's and Flattened Array Initializer With Struct's

A struct is considered a C-style struct if:

  1. User never defines a custom constructor with more than 0 parameters
  2. All member variables in a struct have the same visibility (public or internal or private).

Partial Initializer List's

struct Foo
{
    int a;
    int b;
    int c;
};

...

// Equivalent to `Foo val; val.a = 1; val.b = 0; val.c = 0;`
Foo val = {1}; 

// Equivalent to `Foo val; val.a = 2; val.b = 3; val.c = 0;`
Foo val = {2, 3};

Flattened Array Initializer With Struct's

struct Foo
{
    int a;
    int b;
    int c;
};

...

// Equivalent to `Foo val[2] = { {0,1,2}, {3,4,5} };`
Foo val[2] = {0,1,2, 3,4,5};

Initializer Lists - Default Initializer

{} will default initialize a value:

Non-Struct Type

Value will zero-initialize

// Equivalent to `int val1 = 0;`
int val1 = {};

// Equivalent to `float3 val2 = float3(0);`
float3 val2 = {};

Struct Type

  1. Attempt to call default constructor (__init()) of a struct

    struct Foo
    {
        int a;
        int b;
        __init()
        {
            a = 5;
            b = 5;
        }
    };
    
    ...
    
    // Equivalent to `Foo val = Foo();`
    Foo val = {};
  2. As a fallback, zero-initialize the struct

    struct Foo
    {
        int a;
        int b;
    };
    
    ...
    
    // Equivalent to `Foo val; val.a = 0; val.b = 0;` 
    Foo val = {};

Initializer Lists - Other features

Slang allows calling a default-initializer inside a default-constructor.

__init()
{
    this = {}; //zero-initialize `this`
}
1---
2layout: user-guide
3permalink: /user-guide/conventional-features
4---
5
6Conventional Language Features
7==============================
8
9Many of the language concepts in Slang are similar to those in other real-time shading languages like HLSL and GLSL, and also to general-purpose programming languages in the "C family."
10This chapter covers those parts of the Slang language that are _conventional_ and thus unlikely to surprise users who are already familiar with other shading languages, or languages in the C family.
11
12Readers who are comfortable with HLSL variables, types, functions, statements, as well as conventions for shader parameters and entry points may prefer to skip this chapter.
13Readers who are not familiar with HLSL, but who are comfortable with GLSL and/or C/C++, may want to carefully read the sections on types, expressions, shader parameters, and entry points while skimming the others.
14
15Types
16-----
17
18Slang supports conventional shading language types including scalars, vectors, matrices, arrays, structures, enumerations, and resources.
19
20> #### Note ####
21> Slang has limited support for pointers when targeting platforms with native pointer support, including SPIRV, C++, and CUDA.
22
23### Scalar Types
24
25#### Integer Types
26
27The following integer types are provided:
28
29| Name          | Description |
30|---------------|-------------|
31| `int8_t`      | 8-bit signed integer |
32| `int16_t`     | 16-bit signed integer |
33| `int`         | 32-bit signed integer |
34| `int64_t`     | 64-bit signed integer |
35| `uint8_t`     | 8-bit unsigned integer |
36| `uint16_t`    | 16-bit unsigned integer |
37| `uint`        | 32-bit unsigned integer |
38| `uint64_t`    | 64-bit unsigned integer |
39
40All targets support the 32-bit `int` and `uint` types, but support for the other types depends on the capabilities of each target platform.
41
42Integer literals can be both decimal and hexadecimal. An integer literal can be explicitly made unsigned 
43with a `u` suffix, and explicitly made 64-bit with the `ll` suffix. The type of a decimal non-suffixed integer literal is the first integer type from
44the list [`int`, `int64_t`] which can represent the specified literal value. If the value cannot fit, the literal is represented as 
45an `uint64_t` and a warning is given. The type of hexadecimal non-suffixed integer literal is the first type from the list 
46[`int`, `uint`, `int64_t`, `uint64_t`] that can represent the specified literal value. For more information on 64 bit integer literals see the documentation on [64 bit type support](../64bit-type-support.md).
47
48The following floating-point types are provided:
49
50| Name          | Description                  |
51|---------------|------------------------------|
52| `half`        | 16-bit floating-point number |
53| `float`       | 32-bit floating-point number |
54| `double`      | 64-bit floating-point number |
55
56All targets support the 32-bit `float`, but support for the other types depends on the capabilities of each target platform.
57
58### Boolean Type
59
60The type `bool` is used to represent Boolean truth values: `true` and `false`. 
61
62For compatibility reasons, the `sizeof(bool)` depends on the target. 
63
64| Target |      sizeof(bool)      |
65|--------| ---------------------- |
66| GLSL   | 4 bytes / 32-bit value |
67| HLSL   | 4 bytes / 32-bit value |
68| CUDA   | 1 bytes /  8-bit value |
69
70> #### Note ####
71> When storing bool types in structures, make sure to either pad host-side data structures accordingly, or store booleans as, eg, `uint8_t`, to guarantee
72> consistency with the host language's boolean type.
73
74#### The Void Type
75
76The type `void` is used as a placeholder to represent the result type of functions that don't return anything.
77
78### Vector Types
79
80Vector types can be written as `vector<T,N>` where `T` is a scalar type and `N` is an integer from 2 to 4 (inclusive).
81The type `vector<T,N>` is a vector of `N` _elements_ (also called _components_) each of type `T`.
82
83As a convenience, pre-defined vector types exist for each scalar type and valid element count, with a name using the formula `<<scalar-type>><<element-count>>`.
84For example, `float3` is a convenient name for `vector<float,3>`.
85
86> Note: Slang doesn't support vectors longer than 4 elements. They map to native vector types on many platforms, including CUDA, and none of these platforms support vectors longer than 4 elements. If needed, you can use an array like `float myArray[8]`.
87
88### Matrix Types
89
90Matrix types can be written as `matrix<T,R,C>` where `T` is a scalar type and both `R` and `C` are integers from 2 to 4 (inclusive).
91The type `matrix<T,R,C>` is a matrix with _elements_ of type `T`, and comprising `R` rows and `C` columns.
92
93As a convenience, pre-defined matrix types exist for each scalar type and valid row/column count, with a name using the formula `<<scalar-type>><<row-count>>x<<column-count>>`.
94For example, a `float3x4` is a convenient name for `matrix<float,3,4>`.
95
96> #### Note ####
97> Readers familiar with GLSL should be aware that a Slang `float3x4` represents a matrix with three rows and four columns, while a GLSL `mat3x4` represents a matrix with three *columns* and four *rows*.
98> In most cases, this difference is immaterial because the subscript expression `m[i]` returns a `float4` (`vec4`) in either language.
99> For now it is enough to be aware that there is a difference in convention between Slang/HLSL/D3D and GLSL/OpenGL.
100
101### Array Types
102
103An array type `T[N]` represents an array of `N` elements of type `T`.
104When declaring a variable with an array type, the `[]` brackets come after the variable name, following the C convention for variable declarations:
105
106```hlsl
107// the type of `a` is `int[3]`
108int a[3];
109```
110
111Sometimes a value with an array type can be declared without an explicit element count.
112In some cases the element count is then inferred from the initial value of a variable:
113
114```hlsl
115// the type of `a` is `int[3]`
116int a[] = { 1, 2, 3 };
117```
118
119In other cases, the result is a _unsized_ array, where the actual element count will be determined later:
120
121```hlsl
122// the type of `b` is `int[]`
123void f( int b[] )
124{ ... }
125```
126
127It is allowed to pass a sized array as argument to an unsized array parameter when calling a function.
128
129Array types has a `getCount()` member function that returns the length of the array.
130
131```hlsl
132int f( int b[] )
133{
134    return b.getCount(); // Note: all arguments to `b` must be resolvable to sized arrays.
135}
136
137void test()
138{
139    int arr[3] = { 1, 2, 3 };
140    int x = f(arr); // OK, passing sized array to unsized array parameter, x will be 3.
141}
142```
143
144Please note that if a function calls `getCount()` method on an unsized array parameter, then all
145calls to that function must provide a sized array argument, otherwise the compiler will not be able
146to resolve the size and will report an error. The following code shows an example of valid and
147invalid cases.
148
149```hlsl
150int f( int b[] )
151{
152    return b.getCount();
153}
154int g( int b[] )
155{
156    return f(b); // transitive calls are allowed.
157}
158uniform int unsizedParam[];
159void test()
160{
161    g(unsizedParam); // Not OK, `unsizedParam` doesn't have a known size at compile time.
162    int arr[3];
163    g(arr); // OK.
164}
165```
166
167There are more limits on how runtime-sized arrays can be used than on arrays of statically-known element count.
168
169> #### Note ####
170> In Slang arrays are _value types_, meaning that assignment, parameter passing, etc. semantically copy values of array type.
171> In some languages -- notably C, C++, C#, and Java -- assignment and parameter passing for treat arrays as _reference types_,
172> meaning that these operations assign/pass a reference to the same underlying storage.
173
174### Structure Types
175
176Structure types can be introduced with the `struct` keyword, as in most C-family languages:
177
178```hlsl
179struct MyData
180{
181    int a;
182    float b;
183}
184```
185
186> #### Note ####
187> Unlike C, and like most other C-family languages, the `struct` keyword in Slang introduces a type directly, and there is no need to combine it with a `typedef`.
188
189> #### Note ####
190> Slang allows for a trailing semicolon (`;`) on `struct` declarations, but does not require it.
191
192> #### Note ####
193> Unlike C/C++, `class` is not a valid keyword for GPU code and it is reserved for CPU/host side logic.
194
195Structure types can have constructors. Constructors are defined with the `__init` keyword:
196
197```hlsl
198struct MyData
199{
200     int a;
201     __init() { a = 5; }
202     __init(int t) { a = t; }
203}
204void test()
205{
206     MyData d;  // invokes default constructor, d.a = 5
207     MyData h = MyData(4); // invokes overloaded constructor, h.a = 4
208}
209```
210
211### Default Values for Struct Members
212
213Alternatively, you can specify default values of members in the struct like so: 
214
215```hlsl
216struct MyData
217{
218     int a = 1;
219     float3 b = float3(0.5);
220}
221void test()
222{
223     MyData data = {}; // will initialize data.a to 1 and data.b to {0.5, 0.5, 0.5}
224     MyData data2 = MyData(); // equivalent to MyData data2 = {};
225     MyData data3; // data3.a and data3.b will be undefined !    
226}
227```
228
229### Enumeration Types
230
231Enumeration types can be introduced with the `enum` keyword to provide type-safe constants for a range of values:
232
233```hlsl
234enum Channel
235{
236    Red,
237    Green,
238    Blue
239}
240```
241
242Unlike C/C++, `enum` types in Slang are always scoped by default (like `enum class` in C++). You can write `enum class` in Slang if it makes you happy, but it isn't required. If you want a `enum` type to be unscoped, you can use the `[UnscopedEnum]` attribute:
243```csharp
244[UnscopedEnum]
245enum Channel
246{
247    Red, Green, Blue
248}
249void test(Channel c)
250{
251    if (c == Red) { /*...*/ }
252}
253```
254
255You can specify an explicit underlying integer type for `enum` types:
256```csharp
257enum Channel : uint16_t
258{
259    Red, Green, Blue
260}
261```
262
263By default, the underlying type of an enumeration type is `int`. Enumeration types are implicitly convertible to their underlying type. All enumeration types conform to the builtin `ILogical` interface, which provides operator overloads for bit operations. The following code is allowed:
264
265```csharp
266void test()
267{
268    Channel c = Channel.Red | Channel.Green;
269}
270```
271
272You can explicitly assign values to each enum case:
273```csharp
274enum Channel
275{
276    Red = 5,
277    Green,   // = 6
278    Blue     // = 7
279}
280```
281Slang automatically assigns integer values to enum cases without an explicit value. By default, the value starts from 0 and is increment by 1 for each
282enum case.
283
284You can override the implicit value assignment behavior with the `[Flags]` attribute, which will make value assignment start from 1 and increment by power of 2, making it suitable for enums that represent bit flags. For example:
285```csharp
286[Flags]
287enum Channel
288{
289    Red,   //  = 1
290    Green, //  = 2
291    Blue,  //  = 4
292    Alpha, //  = 8
293}
294```
295
296### Opaque Types
297
298The Slang core module defines a large number of _opaque_ types which provide access to objects that are allocated via GPU APIs.
299
300What all opaque types have in common is that they are not "first-class" types on most platforms.
301Opaque types (and structure or array types that contain them) may be limited in the following ways (depending on the platform):
302
303* Functions that return opaque types may not be allowed
304* Global and `static` variables that use opaque types may not be allowed
305* Opaque types may not appear in the element types of buffers, except where explicitly noted as allowed
306
307#### Texture Types
308
309Texture types -- including `Texture2D`, `TextureCubeArray`, `RWTexture2D`, and more -- are used to access formatted data for read, write, and sampling operations.
310Textures can be used to represent simple images, but also support _mipmapping_ as a way to reduce noise when sampling at lower than full resolution.
311The full space of texture types follows the formula:
312
313    <<access>>Texture<<base shape>><<multisampleness>><<arrayness>><<element type>>
314
315where:
316
317* The _access_ can be read-only (no prefix), read-write (`RW`), or read-write with a guarantee of rasterization order for operations on the given resource (`RasterizerOrdered`).
318* The _base shape_ can be `1D`, `2D`, `3D`, or `Cube`.
319* The _multisample-ness_ can be non-multiple-sample, or multi-sampled (`MS`).
320* The _array-ness_  can either be non-arrayed, or arrayed (`Array`).
321* The _element type_ can either be explicitly specified (`<T>`) or left as the default of `float4`
322
323Not all combinations of these options are supported, and some combinations may be unsupported on some targets.
324
325#### Sampler
326
327Sampler types encapsulate parameters that control addressing and filtering for texture-sampling operations.
328There are two sampler types: `SamplerState` and `SamplerComparisonState`.
329`SamplerState` is applicable to most texture sampling operations, while `SamplerComparisonState` is used for "shadow" texture sampling operations which compare texels to a reference value before filtering.
330
331> #### Note ####
332> Some target platforms and graphics APIs do not support separation of textures and sampling state into distinct types in shader code.
333> On these platforms the Slang texture types include their own sampling state, and the sampler types are placeholder types that carry no data.
334
335#### Buffers
336
337There are multiple buffer types supported by modern graphics APIs, with substantially different semantics.
338
339##### Formatted Buffers
340
341Formatted buffers (sometimes referred to as "typed buffers" or "buffer textures") are similar to 1D textures (in that they support format conversion on loads), without support for mipmapping.
342The formula for formatted buffer types is:
343
344    <<access>>Buffer<<arrayness>><<element type>>
345
346Where the _access_, _array-ness_, and _element type_ are the same as for textures, with the difference that _element type_ is not optional.
347
348A buffer type like `Buffer<float4>` represents a GPU resource that stores one or more values that may be fetched as a `float4` (but might internally be stored in another format, like RGBA8).
349
350##### Flat Buffers
351
352Flat buffers differ from formatted buffers in that they do not support format conversion.
353Flat buffers are either _structured_ buffers or _byte-addressed_ buffers.
354
355Structured buffer types like `StructuredBuffer<T>` include an explicit element type `T` that will be loaded and stored from the buffer.
356Byte-addressed buffer types like `ByteAddressBuffer` do not specify any particular element type, and instead allow for values to be loaded or stored from any (suitably aligned) byte offset in the buffer.
357Both structured and byte-addressed buffers can use an _access_ to distinguish between read-only and read-write usage.
358
359##### Constant Buffers
360
361Constant buffers (sometimes also called "uniform buffers") are typically used to pass immutable parameter data from a host application to GPU code.
362The constant buffer type `ConstantBuffer<T>` includes an explicit element type.
363Unlike formatted or flat buffers, a constant buffer conceptually contains only a *single* value of its element type, rather than one or more values.
364
365Expressions
366-----------
367
368Slang supports the following expression forms with nearly identical syntax to HLSL, GLSL, and C/C++:
369
370* Literals: `123`, `4.56`, `false`
371
372> #### Note ####
373> Unlike C/C++, but like HLSL/GLSL, an unsuffixed floating-point literal has the `float` type in Slang, rather than `double`
374
375* Member lookup: `structValue.someField`, `MyEnumType.FirstCase`
376
377* Function calls: `sin(a)`
378
379* Vector/matrix initialization: `int4(1, 2, 3, 4)`
380
381* Casts: `(int)x`, `double(0.0)`
382
383* Subscript (indexing): `a[i]`
384
385* Initializer lists: `int b[] = { 1, 2, 3 };`
386
387* Assignment: `l = r`
388
389* Operators: `-a`, `b + c`, `d++`, `e %= f`
390
391> #### Note ####
392> Like HLSL but unlike most other C-family languages, the `&&` and `||` operators do *not* currently perform "short-circuiting". 
393> they evaluate all of their operands unconditionally.
394> However, the `?:` operator does perform short-circuiting if the condition is a scalar. Use of `?:` where the condition is a vector is deprecated in Slang. The vector version of `?:` operator does *not* perform short-circuiting, and the user is advised to call `select` instead.
395> The default behavior of these operators is likely to change in a future Slang release.
396
397Additional expression forms specific to shading languages follow.
398
399### Operators on Vectors and Matrices
400
401The ordinary unary and binary operators can also be applied to vectors and matrices, where they apply element-wise.
402
403> #### Note ####
404> In GLSL, most operators apply component-wise to vectors and matrices, but the multiplication operator `*` computes the traditional linear-algebraic product of two matrices, or a matrix and a vector.
405> Where a GLSL programmer would write `m * v` to multiply a `mat3x4` by a `vec3`, a Slang programmer should write `mul(v,m)` to multiply a `float3` by a `float3x4`.
406> In this example, the order of operands is reversed to account for the difference in row/column conventions.
407
408### Swizzles
409
410Given a value of vector type, a _swizzle_ expression extracts one or more of the elements of the vector to produce a new vector.
411For example, if `v` is a vector of type `float4`, then `v.xy` is a `float2` consisting of the `x` and `y` elements of `v`.
412Swizzles can reorder elements (`v.yx`) or include duplicate elements (`v.yyy`).
413
414> #### Note ####
415> Unlike GLSL, Slang only supports `xyzw` and `rgba` as swizzle elements, and not the seldom-used `stpq`.
416
417> #### Note ####
418> Unlike HLSL, Slang does not currently support matrix swizzle syntax.
419
420Statements
421----------
422
423Slang supports the following statement forms with nearly identical syntax to HLSL, GLSL, and C/C++:
424
425* Expression statements: `f(a, 3);`, `a = b * c;`
426
427* Local variable declarations: `int x = 99;`
428
429* Blocks: `{ ... }`
430
431* Empty statement: `;`
432
433* `if` statements
434
435* `switch` statements
436
437> #### Note ####
438> Unlike C/C++, `case` and `default` statements must be directly nested under a `switch`, rather than being allowed under nested control flow (Duff's Device and similar idioms are not allowed).
439> In addition, while multiple `case`s can be grouped together, all other forms of "fall through" are unsupported.
440
441* `for` statements
442
443* `while` statements
444
445* `do`-`while` statements
446
447* `break` statements
448
449* `continue` statements
450
451* `return` statements
452
453* `defer` statements
454
455> #### Note ####
456> The `defer` statement in Slang is tied to scope. The deferred statement runs at the end of the scope like in Swift, not just at the end of the function like in Go.
457> `defer` supports but does not require block statements: both `defer f();` and `defer { f(); g(); }` are legal.
458
459> #### Note ####
460> Slang does not support the C/C++ `goto` keyword.
461
462> #### Note ####
463> Slang does not support the C++ `throw` keyword.
464
465Additional statement forms specific to shading languages follow.
466
467### Discard Statements
468
469A `discard` statement can be used in the context of a fragment shader to terminate shader execution for the current fragment, and to cause the graphics system to discard the corresponding fragment.
470
471Functions
472---------
473
474Slang supports function definitions with traditional C syntax:
475
476```hlsl
477float addSomeThings(int x, float y)
478{
479    return x + y;
480}
481```
482
483In addition to the traditional C syntax, you can use the modern syntax to define functions with the `func` keyword:
484```swift
485func addSomeThings(x : int, y : float) -> float
486{
487    return x + y;
488}
489```
490
491Slang supports overloading of functions based on parameter types.
492
493Function parameters may be marked with a _direction_ qualifier:
494
495* `in` (the default) indicates a by-value input parameter
496* `out` indicates an output parameter
497* `inout` or `in out` indicates an input/output parameter
498
499> #### Note ####
500> The `out` and `inout` directions are superficially similar to non-`const` reference parameters in C++.
501> In cases that do not involve aliasing of mutable memory, the semantics should be equivalent.
502
503Preprocessor
504------------
505
506Slang supports a C-style preprocessor with the following directives;
507
508* `#include`
509* `#define`
510* `#undef`
511* `#if`, `#ifdef`, `#ifndef`
512* `#else`, `#elif`
513* `#endif`
514* `#error`
515* `#warning`
516* `#line`
517* `#pragma`, including `#pragma once`
518
519Variadic macros are supported by the Slang preprocessor.
520
521> #### Note ####
522> The use of `#include` in new code is discouraged as this functionality has
523> been superseded by the module system, please refer to
524> [Modules and Access Control](04-modules-and-access-control.md)
525
526Attributes
527----------
528
529_Attributes_ are a general syntax for decorating declarations and statements with additional semantic information or meta-data.
530Attributes are surrounded with square brackets (`[]`) and prefix the declaration or statement they apply to.
531
532For example, an attribute can indicate the programmer's desire that a loop be unrolled as much as possible:
533
534```hlsl
535[unroll]
536for(int i = 0; i < n; i++)
537{ /* ... */ }
538```
539
540> #### Note ####
541> Traditionally, all attributes in HLSL used a single layer of `[]` brackets, matching C#.
542> Later, C++ borrowed the idea from C# but used two layers of brackets (`[[]]`).
543> Some recent extensions to HLSL have used the C++-style double brackets instead of the existing single brackets syntax.
544> Slang tries to support both alternatives uniformly.
545
546Global Variables and Shader Parameters
547--------------------------------------
548
549By default, global-scope variable declarations in Slang represent _shader parameters_ passed from host application code into GPU code.
550Programmers must explicitly mark a global-scope variable with `static` for it not to be treated as a shader parameter, even if the variable is marked `const`:
551
552```hlsl
553// a shader parameter:
554float a;
555
556// also a shader parameter (despite `const`):
557const int b = 2;
558
559// a "thread-local" global variable
560static int c = 3;
561
562// a compile-time constant
563static const int d = 4;
564```
565
566### Global Constants
567
568A global-scope `static const` variable defines a compile-time constant for use in shader code.
569
570### Global-Scope Static Variables
571
572A non-`const` global-scope  `static` variable is conceptually similar to a global variable in C/C++, with the key difference that it has distinct storage per *thread* rather than being truly global.
573Each logical thread of shader execution initiated by the GPU will be allocated fresh storage for these `static` variables, and values written to those variables will be lost when a shader thread terminates.
574
575> #### Note ####
576> Some target platforms do not support `static` global variables in all use cases.
577> Support for `static` global variables should be seen as a legacy feature, and further use is discouraged.
578
579### Global Shader Parameters
580
581Global shader parameters may use any type, including both opaque and non-opaque types:
582
583```hlsl
584ConstantBuffer<MyData> c;
585Texture2D t;
586float4 color;
587```
588
589To avoid confusion, the Slang compiler will warn on any global shader parameter that includes non-opaque types, because it is likely that a user thought they were declaring a global constant or a traditional global variable.
590This warning may be suppressed by marking the parameter as `uniform`:
591
592```hlsl
593// WARNING: this declares a global shader parameter, not a global variable
594int gCounter = 0;
595
596// OK:
597uniform float scaleFactor;
598```
599
600#### Legacy Constant Buffer Syntax
601
602For compatibility with existing HLSL code, Slang also supports global-scope `cbuffer` declarations to introduce constant buffers:
603
604```hlsl
605cbuffer PerFrameCB
606{
607    float4x4 mvp;
608    float4 skyColor;
609    // ...
610}
611```
612
613A `cbuffer` declaration like this is semantically equivalent to a shader parameter declared using the `ConstantBuffer` type:
614
615```hlsl
616struct PerFrameData
617{
618    float4x4 mvp;
619    float4 skyColor;
620    // ...
621}
622ConstantBuffer<PerFrameData> PerFrameCB;
623```
624
625#### Explicit Binding Markup
626
627For compatibility with existing codebases, Slang supports pre-existing markup syntax for associating shader parameters of opaque types with binding information for specific APIs.
628
629Binding information for Direct3D platforms may be specified using `register` syntax:
630
631```hlsl
632Texture2D a : register(t0);
633Texture2D b : register(t1, space0);
634```
635
636Binding information for Vulkan (and OpenGL) may be specified using `[[vk::binding(...)]]` attributes
637
638```hlsl
639[[vk::binding(0)]]
640Texture2D a;
641
642[[vk::binding(1, 0)]]
643Texture2D b;
644```
645
646A single parameter may use both the D3D-style and Vulkan-style markup, but in each case explicit binding markup only applies to the API family for which it was designed.
647
648> #### Note ####
649> Explicit binding markup is tedious to write and error-prone to maintain.
650> It is almost never required in Slang codebases.
651> The Slang compiler can automatically synthesize bindings in a completely deterministic fashion and in most cases the bindings it generates are what a programmer would have written manually.
652
653Shader Entry Points
654-------------------
655
656An _entry point_ is a function that can be used as the starting point for execution of a GPU thread.
657
658Here is an example of an entry-point function in Slang:
659
660```hlsl
661[shader("vertex")]
662float4 vertexMain(
663    float3 modelPosition : POSITION,
664    uint vertexID : SV_VertexID,
665    uniform float4x4 mvp)
666    : SV_Position
667{ /* ... */ }
668```
669
670In the following sections we will use this example to explain important facets of entry point declarations in Slang.
671
672### Entry Point Attribute and Stages
673
674The `[shader(...)]` attribute is used to mark a function in Slang as a shader entry point, and also to specify which pipeline stage it is meant for.
675In this example, the `vertexMain` shader indicates that it is meant for the `vertex` stage of the traditional rasterization pipeline.
676Rasterization, compute, and ray-tracing pipelines each define their own stages, and new versions of graphics APIs may introduce new stages.
677
678For compatibility with legacy codebases, Slang supports code that leaves off `[shader(...)]` attributes; in these cases application developers must specify the names and stages for their entry points via explicit command-line or API options.
679Such entry points will not be found via `IModule::findEntryPointByName()`. Instead `IModule::findAndCheckEntryPoint()` must be used, and a stage must be specified.
680It is recommended that new codebases always use `[shader(...)]` attributes both to simplify their workflow, and to make code more explicit and "self-documenting."
681
682> #### Note ####
683> In GLSL, a file of shader code may only include one entry point, and all code `#include`d into that file must be compatible with the stage of that entry point. By default, GLSL requires that an entry point be called `main`.
684> Slang allows for multiple entry points to appear in a file, for any combination of stage, and with any valid identifier as a name.
685
686### Parameters
687
688The parameter of an entry-point function represent either _varying_ or _uniform_ inputs.
689Varying inputs are those that may vary over threads invoked as part of the same batch (a draw call, compute dispatch, etc.), while uniform inputs are those that are guaranteed to be the same for all threads in a batch.
690Entry-point parameters in Slang default to varying, but may be explicitly marked `uniform`.
691
692If an entry-point function declares a non-`void` result type, then its result behaves like an anonymous `out` parameter that is varying.
693
694### Binding Semantics
695
696The varying parameters of an entry point must declare a _binding semantic_ to indicate how those parameters should be connected to the execution environment.
697A binding semantic for a parameter may be introduced by suffixing the variable name with a colon (`:`) and an identifier for the chosen binding semantic.
698A binding semantic for a function result is introduced similarly, but comes after the parameter list.
699
700It is not shown in this example, but binding semantics may also be applied to individual `struct` fields, in cases where a varying parameter of `struct` type is used.
701
702#### System-Defined Binding Semantics
703
704In the `vertexMain` entry point, the `vertexID` parameter uses the `SV_VertexID` binding semantic, which is a _system-defined_ binding semantic.
705Standard system-defined semantics are distinguished by the `SV_` prefix.
706
707A system-defined binding semantic on an input parameter indicates that the parameter should receive specific data from the GPU as defined by the pipeline and stage being used.
708For example, in a vertex shader the `SV_VertexID` binding semantic on an input yields the ID of the particular vertex being processed on the current thread.
709
710A system-defined binding semantic on an output parameter or function result indicates that when a shader thread returns from the entry point the value stored in that output should be used by the GPU in a specific way defined by the pipeline and stage being used.
711For example, in a vertex shader the `SV_Position` binding semantic on an output indicates that it represents a clip-space position that should be communicated to the rasterizer.
712
713The set of allowed system-defined binding semantics for inputs and outputs depends on the pipeline and stage of an entry point.
714Some system-defined binding semantics may only be available on specific targets or specific versions of those targets.
715
716> #### Note ####
717> Instead of using ordinary function parameters with system-defined binding semantics, GLSL uses special system-defined global variables with the `gl_` name prefix.
718> Some recent HLSL features have introduced special globally-defined functions that behave similarly to these `gl_` globals.
719
720#### User-Defined Binding Semantics
721
722In the `vertexMain` entry point, the `modelPosition` parameter used the `POSITION` binding semantic, which is a _user-defined_ binding semantic.
723
724A user-defined binding semantic on an input indicates that the parameter should receive data with a matching binding semantic from a preceding stage.
725A user-defined binding semantic on an output indicates that the parameter should provide data to a parameter with a matching binding semantic in a following stage.
726
727Whether or not inputs and outputs with user-defined binding semantics are allowed depends on the pipeline and stage of an entry point.
728
729Different APIs and different stages within the same API may match up entry point inputs/outputs with user-defined binding semantics in one of two ways:
730
731* By-index matching: user-defined outputs from one stage and inputs to the next are matched up by order of declaration. The types of matching output/input parameters must either be identical or compatible (according to API-specific rules). Some APIs also require that the binding semantics of matching output/input parameters are identical.
732
733* By-name matching: user-defined outputs from one stage and inputs to the next are matched up by their binding semantics. The types of matching output/input parameters must either be identical or compatible (according to API-specific rules). The order of declaration of the parameters need not match.
734
735Because the matching policy may differ across APIs, the only completely safe option is for parameters passed between pipeline stages to match in terms of order, type, *and* binding semantic.
736
737> #### Note ####
738> Instead of using ordinary function parameters for user-defined varying inputs/outputs, GLSL uses global-scope variable declarations marked with the `in` or `out` modifier.
739
740### Entry-Point Uniform Parameters
741
742In the `vertexMain` entry point, the `mvp` parameter is an _entry-point uniform parameter_.
743
744Entry-point uniform parameters are semantically similar to global-scope shader parameters, but do not pollute the global scope.
745
746> #### Note ####
747> GLSL does not support entry-point `uniform` parameters; all shader parameters must be declared at the global scope.
748> Historically, HLSL has supported entry-point `uniform` parameters, but this feature was dropped by recent compilers.
749
750Mixed Shader Entry Points
751--------------------------
752
753Through the `[shader(...)]` syntax, users of slang can freely combine multiple entry points into the same file. This can be especially convenient for reuse between entry points which have a logical connection.
754
755For example, mixed entry points offer a convenient way for ray tracing applications to concisely define a complete pipeline in one source file, while also providing users with additional opportunities to improve type safety of 
756shared structure definitions:
757
758```hlsl
759struct Payload { float3 color; };
760
761[shader("raygeneration")]
762void rayGenerationProgram() {
763    Payload payload;
764    TraceRay(/*...*/, payload);
765    /* ... */ 
766}
767
768[shader("closesthit")]
769void closestHitProgram(out Payload payload) { 
770    payload.color = {1.0};
771}
772
773[shader("miss")]
774void missProgram(out Payload payload) { 
775    payload.color = {1.0};
776}
777```
778
779> #### Note ####
780> GLSL does not support multiple entry-points; however, SPIR-V does. Vulkan users wanting to take advantage of Slang mixed entry points must pass `-fvk-use-entrypoint-name` and `-emit-spirv-directly` as compiler arguments.
781
782### Mixed Entry-Point Uniform Parameters
783
784Like with the previous `vertexMain` example, mixed entry point setups also support _entry-point uniform parameters_.
785
786However, because of certain systematic differences between entry point types, a uniform being _global_ or _local_ will have very important consequences on the underlying layout and behavior.
787
788For most all entry point types, D3D12 will use one common root signature to define both global and local uniform parameters. 
789Likewise, Vulkan descriptors will bind to a common pipeline layout. For both of these cases, Slang maps uniforms to the common root signature / pipeline layout. 
790
791However, for ray tracing entry points and D3D12, these parameters map to either _global_ root signatures or to _local_ root signatures, with the latter being stored in the shader binding table.
792In Vulkan, D3D12's global root signatures translate to a shared ray tracing pipeline layout, while local root signatures map again to shader binding table records. 
793
794When entry points match a "ray tracing" type, we bind uniforms which are in the _global_ scope to the _global_ root signature (or ray tracing pipeline layout), while uniforms which are _local_ are bound to shader binding table records, which depend on the underlying runtime record indexing. 
795
796Consider the following:
797
798```hlsl
799uniform float3 globalUniform;
800
801[shader("compute")][numThreads(1,2,3)]
802void computeMain1(uniform float3 localUniform1) 
803{ /* ... */ }
804
805[shader("compute")][numThreads(1,2,3)]
806void computeMain2(uniform float3 localUniform2) 
807{ /* ... */ }
808
809[shader("raygeneration")]
810void rayGenerationMain(uniform float3 localUniform3) 
811{ /* ... */ }
812
813[shader("closesthit")]
814void closestHitMain(uniform float3 localUniform4) 
815{ /* ... */ }
816```
817
818In this example, `globalUniform` is appended to the global root signature / pipeline layouts for _both_ compute _and_ ray generation stages for all four entry points. 
819Compute entry points lack "local root signatures" in D3D12, and likewise Vulkan has no concept of "local" vs "global" compute pipeline layouts, so `localUniform1` is "pushed" to the stack of reserved global uniform parameters for use in `computeMain1`. 
820Leaving that entry point scope "pops" that global uniform parameter such that `localUniform2` can reuse the same binding location for `computeMain2`.
821However, local uniforms for ray tracing shaders map to the corresponding "local" hit records in the shader binding table, and so no "push" or "pop" to the global root signature / pipeline layouts occurs for these parameters. 
822
823Auto-Generated Constructors
824----------
825
826### Auto-Generated Constructors - Struct
827
828Slang has the following rules:
8291. Auto-generate a `__init()` if not already defined.
830
831   Assume:
832   ```csharp
833   struct DontGenerateCtor
834   {
835       int a;
836       int b = 5;
837
838       // Since the user has explicitly defined a constructor
839       // here, Slang will not synthesize a conflicting 
840       // constructor.
841       __init()
842       {
843           // b = 5;
844           a = 5;
845           b = 6;
846       }
847   };
848
849   struct GenerateCtor
850   {
851       int a;
852       int b = 5;
853   
854       // Slang will automatically generate an implicit constructor:
855       // __init()
856       // {
857       //     b = 5;
858       // }
859   };
860   ```
861
8622. If all members have equal visibility, auto-generate a 'member-wise constructor' if not conflicting with a user defined constructor.
863   ```csharp
864   struct GenerateCtorInner
865   {
866       int a;
867
868       // Slang will automatically generate an implicit
869       // __init(int in_a)
870       // {
871       //     a = in_a;
872       // }
873   };
874   struct GenerateCtor : GenerateCtorInner
875   {
876       int b;
877       int c = 5;
878
879       // Slang will automatically generate an implicit
880       // __init(int in_a, int in_b, int in_c)
881       // {
882       //     c = 5;
883       //
884       //     this = GenerateCtorInner(in_a);
885       //
886       //     b = in_b;
887       //     c = in_c;
888       // }
889   };
890   ```
891
8923. If not all members have equal visibility, auto-generate a 'member-wise constructor' based on member visibility if not conflicting with a user defined constructor. 
893
894   We generate 3 different visibilities of 'member-wise constructor's in order:
895      1. `public` 'member-wise constructor'
896         - Contains members of visibility: `public`
897         - Do not generate if `internal` or `private` member lacks an init expression
898      2. `internal` 'member-wise constructor'
899         - Contains members of visibility: `internal`, `public`
900         - Do not generate if `private` member lacks an init expression
901      3. `private` 'member-wise constructor'
902         - Contains members of visibility: `private`, `internal`, `public`
903
904   ```csharp
905   struct GenerateCtorInner1
906   {
907       internal int a = 0;
908    
909       // Slang will automatically generate an implicit
910       // internal __init(int in_a)
911       // {
912       //     a = 0;
913       //
914       //     a = in_a;
915       // }
916   };
917   struct GenerateCtor1 : GenerateCtorInner1
918   {
919       internal int b = 0;
920       public int c;
921
922       // Slang will automatically generate an implicit
923       // internal __init(int in_a, int in_b, int in_c)
924       // {
925       //     b = 0;
926       //
927       //     this = GenerateCtorInner1(in_a);
928       //
929       //     b = in_b;
930       //     c = in_c;
931       // }
932       //
933       // public __init(int in_c)
934       // {
935       //     b = 0;
936       //
937       //     this = GenerateCtorInner1();
938       //
939       //     c = in_c;
940       // }
941   };
942
943   struct GenerateCtorInner2
944   {
945       internal int a;
946       // Slang will automatically generate an implicit
947       // internal __init(int in_a)
948       // {
949       //     a = in_a;
950       // }
951   };
952   struct GenerateCtor2 : GenerateCtorInner2
953   {
954       internal int b;
955       public int c;
956
957       /// Note: `internal b` is missing init expression,
958       // Do not generate a `public` 'member-wise' constructor.
959
960       // Slang will automatically generate an implicit
961       // internal __init(int in_a, int in_b, int in_c)
962       // {
963       //     this = GenerateCtorInner2(in_a);
964       //
965       //     b = in_b;
966       //     c = in_c;
967       // }
968   };
969   ```
970
971Initializer Lists
972----------
973Initializer Lists are an expression of the form `{...}`.
974
975```csharp
976int myFunc()
977{
978    int a = {}; // Initializer List
979}
980```
981
982### Initializer Lists - Scalar
983
984```csharp
985// Equivalent to `int a = 1`
986int a = {1};
987```
988
989### Initializer Lists - Vectors
990
991```csharp
992// Equivalent to `float3 a = float3(1,2,3)`
993float3 a = {1, 2, 3};
994```
995
996### Initializer Lists - Arrays/Matrices
997
998#### Array Of Scalars
999
1000```csharp
1001// Equivalent to `int[2] a; a[0] = 1; a[1] = 2;`
1002int a[2] = {1, 2}
1003```
1004
1005#### Array Of Aggregates
1006
1007```csharp
1008// Equivalent to `float3 a[2]; a[0] = {1,2,3}; b[1] = {4,5,6};`
1009float3 a[2] = { {1,2,3}, {4,5,6} };
1010```
1011
1012#### Flattened Array Initializer
1013
1014```csharp
1015// Equivalent to `float3 a[2] = { {1,2,3}, {4,5,6} };`
1016float3 a[3] = {1,2,3, 4,5,6}; 
1017```
1018
1019### Initializer Lists - Struct
1020
1021In most scenarios, using an initializer list to create a struct typed value is equivalent to calling the struct's constructor using the elements in the initializer list as arguments for the constructor, for example:
1022```csharp
1023struct GenerateCtorInner1
1024{
1025    internal int a = 0;
1026    
1027    // Slang will automatically generate an implicit
1028    // internal __init(int in_a)
1029    // {
1030    //     a = 0;
1031    //
1032    //     a = in_a;
1033    // }
1034
1035    static GenerateCtorInner1 callGenerateCtorInner1()
1036    {
1037        // Calls `GenerateCtorInner1::__init(1);`
1038        return {1};
1039    }
1040};
1041struct GenerateCtor1 : GenerateCtorInner1
1042{
1043    internal int b = 0;
1044    public int c;
1045
1046    // Slang will automatically generate an implicit
1047    // internal __init(int in_a, int in_b, int in_c)
1048    // {
1049    //     this = GenerateCtorInner1(in_a);
1050    //
1051    //     b = 0;
1052    //
1053    //     b = in_b;
1054    //     c = in_c;
1055    // }
1056    //
1057    // public __init(int in_c)
1058    // {
1059    //     this = GenerateCtorInner1();
1060    //
1061    //     b = 0;
1062    //
1063    //     c = in_c;
1064    // }
1065    static GenerateCtorInner1 callInternalGenerateCtor()
1066    {
1067        // Calls `GenerateCtor1::__init(1, 2, 3);`
1068        return {1, 2, 3};
1069    }
1070    static GenerateCtorInner1 callPublicGenerateCtor()
1071    {
1072        // Calls `GenerateCtor1::__init(1);`
1073        return {1}; 
1074    }
1075};
1076
1077...
1078
1079// Calls `{ GenerateCtor1::__init(3), GenerateCtor1::__init(2) }`
1080GenerateCtor1 val[2] = { { 3 }, { 2 } };
1081```
1082
1083In addition, Slang also provides compatibility support for C-style initializer lists with `struct`s. C-style initializer lists can use [Partial Initializer List's](#Partial-Initializer-Lists) and [Flattened Array Initializer With Struct's](#Flattened-Array-Initializer-With-Structs)
1084
1085A struct is considered a C-style struct if:
10861. User never defines a custom constructor with **more than** 0 parameters
10872. All member variables in a `struct` have the same visibility (`public` or `internal` or `private`).
1088
1089#### Partial Initializer List's
1090
1091```csharp
1092struct Foo
1093{
1094    int a;
1095    int b;
1096    int c;
1097};
1098
1099...
1100
1101// Equivalent to `Foo val; val.a = 1; val.b = 0; val.c = 0;`
1102Foo val = {1}; 
1103
1104// Equivalent to `Foo val; val.a = 2; val.b = 3; val.c = 0;`
1105Foo val = {2, 3};
1106```
1107
1108#### Flattened Array Initializer With Struct's
1109
1110```csharp
1111struct Foo
1112{
1113    int a;
1114    int b;
1115    int c;
1116};
1117
1118...
1119
1120// Equivalent to `Foo val[2] = { {0,1,2}, {3,4,5} };`
1121Foo val[2] = {0,1,2, 3,4,5};
1122```
1123
1124
1125### Initializer Lists - Default Initializer
1126
1127`{}` will default initialize a value:
1128
1129#### Non-Struct Type
1130
1131Value will zero-initialize
1132```csharp
1133// Equivalent to `int val1 = 0;`
1134int val1 = {};
1135
1136// Equivalent to `float3 val2 = float3(0);`
1137float3 val2 = {};
1138```
1139
1140#### Struct Type
1141
11421. Attempt to call default constructor (`__init()`) of a `struct`
1143
1144   ```csharp
1145   struct Foo
1146   {
1147       int a;
1148       int b;
1149       __init()
1150       {
1151           a = 5;
1152           b = 5;
1153       }
1154   };
1155
1156   ...
1157
1158   // Equivalent to `Foo val = Foo();`
1159   Foo val = {};
1160   ```
1161
11622. As a fallback, zero-initialize the struct
1163
1164   ```csharp
1165   struct Foo
1166   {
1167       int a;
1168       int b;
1169   };
1170
1171   ...
1172
1173   // Equivalent to `Foo val; val.a = 0; val.b = 0;` 
1174   Foo val = {};
1175   ```
1176
1177### Initializer Lists - Other features
1178
1179Slang allows calling a default-initializer inside a default-constructor.
1180
1181```c#
1182__init()
1183{
1184    this = {}; //zero-initialize `this`
1185}
1186```