How to write to Texture2DArray in compute shader? (getting UAV errors)

I am trying to create a simple compute shader that writes data to a texture array, but I keep getting UAV errors. I am new to compute shaders, so I may be missing something completely obvious.

I wish to be able to display the textures in the array on screen using a simple shader (currently one I created in ShaderGraph). I assumed I could use a Texture2DArray for this (and old forums posts seem to hint at this), but it seemingly doesn’t work.

The error I get is “Compute shader (Test): Property (data) at kernel index (0): Attempting to bind texture as UAV but the texture wasn’t created with the UAV usage flag set!”

I am using the somewhat unusual ‘RFloat’ textureformat, since my calculations will be in floating point buffers, but I don’t assume that’s related to the problem.

I made a very simple version of the shader to isolate the problem:

Shader:

#pragma kernel Test

RWTexture2DArray<float> data : register(u0); // Supposedly this initializes it for UAV?

float value;

[numthreads(8,8,1)]
void Test(uint3 id : SV_DispatchThreadID)
{
    data[uint3(id.x, id.y, 0)] = value;
}

Unity:

    private void TestCompute(int depth, RawImage image)
    {
        textureArray = new Texture2DArray(textureWidth, textureHeight, depth, TextureFormat.RFloat, false, true);

        int kernel = computeShader.FindKernel("Test");

        computeShader.SetTexture(kernel, "data", textureArray);
        computeShader.SetFloat("value", 0.2f);

        computeShader.Dispatch(kernel, textureWidth / 8, textureHeight / 8, 1);

        image.material = new Material(arrayMaterial);
        image.material.SetTexture("TextureArray", textureArray);
        image.material.SetInteger("ArrayIndex", 0);
    }

I don’t know if there’s a way to create a Texture2DArray that enables UAV or I need to use a different texture class in Unity (like RenderTexture). However if I do use a different texture class, then how do I access it in my shader? I would like to avoid copying the data (especially back to the CPU).

Old forum posts that hints that this is possible (but the linked example doesn’t use Texture2DArray):

The last of the post is the one that uses the “: register(u0)” bit when declaring the RWTexture2DArray, but I have no idea what it actually does. I know they also set “enableRandomWrite = true” on their RenderTexture, but there’s no such property on a Texture2DArray.

Ok it seems the solution is to use a RenderTexture formatted as a Tex2DArray and then just pretend that’s a Texture2DArray when assigning it to a Unity shader.

Here’s the code that works:

Compute:

#pragma kernel Test

RWTexture2DArray<float> data : register(u0); // Supposedly this initializes it for UAV

float value;

[numthreads(8,8,1)]
void Test(uint3 id : SV_DispatchThreadID)
{
    data[uint3(id.x, id.y, 4)] = value;
}

Unity:

    private void TestCompute(int depth, RawImage image)
    {
        RenderTexture texture = new RenderTexture(textureWidth, textureHeight, depth, RenderTextureFormat.RFloat, RenderTextureReadWrite.Default);

        texture.dimension = TextureDimension.Tex2DArray;
        texture.enableRandomWrite = true;
        texture.volumeDepth = depth;
        texture.Create();

        int kernel = computeShader.FindKernel("Test");

        computeShader.SetTexture(kernel, "data", texture);
        computeShader.SetFloat("value", 0.9f);

        computeShader.Dispatch(kernel, textureWidth / 8, textureHeight / 8, 1);

        image.material = new Material(arrayMaterial);
        image.material.SetTexture("_TextureArray", texture);
        image.material.SetInteger("_ArrayIndex", 4);
    }

ShaderGraph:

1 Like