Table of Contents

Arrays

Why not C# arrays

C# arrays, such as float[], can't be used in shader code, because they work differently from the arrays a GPU has:

  • A C# array is an object. It's created on the heap, and a variable only refers to it: assigning an array to another variable, or passing it to a method, shares the same array. GPUs have no heap and no references. An HLSL array is a value, stored in place and copied on assignment, like a vector.
  • A C# array's size is decided at run time, with new float[count], and is a property of each array, not of its type. An HLSL array's size is fixed when the shader is compiled and is part of its type: float weights[4] always holds exactly 4 floats. The GPU compiler needs to know it, to reserve registers and unroll loops.

So Shader Script has its own array types, which behave like HLSL arrays: array4<float> is a value holding exactly 4 floats, and is translated to float name[4]. The available sizes are 1 to 8, 10, 16, 20, 24, 32, 40, 50, 64 and 128: array1<T> to array128<T>. The elements can be scalars, vectors, matrices or [ShaderCode] structs.

C# array Shader Script array HLSL
float[] w = new float[4]; array4<float> w = new array4<float>(); float w[4] = (float[4])0;
float[] w = { 1, 2, 3, 4 }; array4<float> w = new array4<float>(1, 2, 3, 4); float w[4] = { 1, 2, 3, 4 };
w.Length array4<float>.Length, a constant 4

These types aren't placeholders that only exist to be translated: they're fully working C# structs, which behave on the CPU as they do in a shader, copied on assignment and indexed with a[i]. You can use them in ordinary C# code too, for example to test a [ShaderCode] function that takes an array. On the CPU, an index outside the array throws an IndexOutOfRangeException; the GPU doesn't check indices, so there, reading or writing outside an array has an undefined result.

Creating arrays

// With all the elements:
array4<float> weights = new array4<float>(0.1f, 0.2f, 0.3f, 0.4f);

// Filled with zeros:
array8<float3> samples = new array8<float3>();

An array created with elements can only be the initial value of a variable. To pass one to a method, declare the variable first. An empty array, filled with zeros, can be used anywhere.

Using arrays

Read and write elements with a[i], and use Length for loops:

float sum = 0;
for (int i = 0; i < array4<float>.Length; i++)
    sum += weights[i];

array4<float>.Length is a constant, 4, so the GPU compiler can unroll the loop.

To change a single component or field of an element, copy the element to a variable, change it and assign it back. C# doesn't allow samples[i].x = 1, because the indexer returns a copy:

float3 s = samples[i];
s.x = 1;
samples[i] = s;

Arrays and methods

Arrays are copied on assignment, like the other shader types. A method can take an array as a parameter, including ref and out parameters, but can't return one: return it through an out parameter instead.

void FillKernel(out array4<float> kernel)
{
    kernel = new array4<float>();
    for (int i = 0; i < 4; i++)
        kernel[i] = 1f / (i + 1);
}

Arrays of arrays aren't supported. A [ShaderCode] struct can have an array field.