Gabriel C.
Gabriel C.

Reputation: 127

Understanding the "sampler array index must be a literal expression" error in ComputeShaders

Let's say I have a compute shader that retrieves data from a Texture2DArray using the Id of the group like this:

Texture2DArray<float4> gTextureArray[2];
[numthreads(32, 1, 1)]
void Kernel(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID)
{
    float3 tmp = gTextureArray[GroupID.x].Load(int4(GroupThreadID.x,GroupThreadID.x,0,0)).rgb;

    ....
}

And let's say I launch it like this deviceContext->Dispatch(2, 0, 0);

So, 2 groups, 32 threads each that read pixel values from a Texture2DArray. All the threads in GroupID.x = 0 will read values from gTextureArray[0] and all the threads in GroupID.y = 0 will read values from gTextureArray[1]. It turns out I can't compile that simple code, instead I get this compile error error X3512: sampler array index must be a literal expression

Now, I know I can do this instead:

Texture2DArray<float4> gTextureArray[2];
[numthreads(32, 1, 1)]
void Kernel(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID)
{
    float3 tmp = float3(0,0,0);
    if(GroupID.x == 0)
        tmp = gTextureArray[0].Load(int4(GroupThreadID.x,GroupThreadID.x,0,0)).rgb;
    else if(GroupID.x == 1)
        tmp = gTextureArray[1].Load(int4(GroupThreadID.x,GroupThreadID.x,0,0)).rgb;

    ....
}

Or use a switch in case I have lots of groups so it doesn't look that much awful (it still does)

Notice how there is no warp divergence since all threads in each group will go one branch or the other. My question is, am I missing something here? Why does HLSL not support that kind of indexing since I can not see any divergence or other problems, at least in this case?

Upvotes: 3

Views: 7396

Answers (1)

mrvux
mrvux

Reputation: 8963

I'm not sure if you bind your pipeline correctly, but let's evaluate both cases.

When you have:

Texture2DArray<float4> gTextureArray[2];

You technically bind 2 texture array (one in slot 0, one in slot 1), so runtime is not able to switch shader resource slot dynamically.

The line above is the same as doing:

Texture2DArray<float4> gTextureArray0;
Texture2DArray<float4> gTextureArray1;

You effectively bind 2 different resources in both cases, hence why you can't dynamically switch.

In the case where you have a Texture Array with 2 slices, this becomes possible, you need to change your code by:

Texture2DArray<float4> gTextureArray;

[numthreads(32, 1, 1)]
void Kernel(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID)
{
    float3 tmp = gTextureArray.Load(int4(GroupThreadID.x,GroupThreadID.x,GroupID.x,0)).rgb;

}

Z component is the slice index, so in that case that's perfectly possible.

Upvotes: 1

Related Questions