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.