Upload and write to RenderTexture as Texture2DArray in Compute shader !?

Hi, i need to create a render texture and set it as a Tex2DArrayto a compute shader and then work on every texture in the array. How one can access a specific index of the array in Compute Shader… let’s say i would just like to apply a red color to the first texture, green to the second and blue to the third… !?

rendTex = new RenderTexture(1024, 1024, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default);
rendTex.dimension = UnityEngine.Rendering.TextureDimension.Tex2DArray;
rendTex.enableRandomWrite = true;
rendTex.Create();
rendTex.volumeDepth = 3; // NOT SURE - IS THIS THE TOTAL TEXTURE ELEMENTS IN THE ARRAY !?
shader.SetTexture(kernel, "rendTex", rendTex);
// Than in the Compute Shader there is a UAV resources:
RWTexture2D<float4> rendTex : register(u0);

[numthreads(8, 8, 1)]
void CSMain(uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID)
{
     // SO THE QUESTION IS HOW TO REFERENCE FOR EXAMPLE THE 2ND ELEMENT IN THE ARRAY
     // WITH SPECIFIC X,Y COORDINATES... 
}

It’s the Z value in its UV. You’ll need to use the correct Texture2DArray type and sample it with SampleLevel().

Hi, thanks for the feedback. I actually need to apply color ( i see now that i wasn’t clear enough sorry ) to all the elements from withing the shader kernel… for example if having 3 texture elements in the array would like to make the first texture in array red color, second green and the third blue color…

may be something like :
rendTex[int3(DTid.xy, 0)] = float4(1.0, 0.0, 0.0, 1.0);
rendTex[int3(DTid.xy, 1)] = float4(0.0, 1.0, 0.0, 1.0);
rendTex[int3(DTid.xy, 2)] = float4(0.0, 0.0, 1.0, 1.0);

Is this suppose to work !?

Thanks !

Ah, yes although you’ll need RWTexture2DArray and uint3 (for indices). More here: RWTexture2DArray - Win32 apps | Microsoft Learn

Thanks, glad you have found time to answer again… !

This code is rather simple but i can not make it work.
I guess i am missing something… !?
It just should fill the three textures with a color only… !

If you can take a look at the code and see if there is something wrong will be awesome !?

RenderTexture rendTexArray;

void DoSomeWork()
{
    // Create a render texture array
    int kernel = shader.FindKernel("PaintTexturesInArray");
    rendTexArray = new RenderTexture(512, 512, 32, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default);
    rendTexArray.dimension = UnityEngine.Rendering.TextureDimension.Tex2DArray;
    rendTexArray.enableRandomWrite = true;
    rendTexArray.volumeDepth = 3;
    rendTexArray.Create();    
    shader.SetTexture(kernel, "rendTexArray", rendTexArray);

    // Dispatch the shader kernel
    shader.Dispatch(kernel, 512 / 8, 512 / 8, 1);
    
    //  Create an array of Texture2Ds and copy the render texture array to it
    Texture2DArray textures2DArray = new Texture2DArray(512, 512, 3, TextureFormat.ARGB32, false);
    Graphics.CopyTexture(rendTexArray, textures2DArray);
    textures2DArray.Apply();
    
    // Last step
    // Use textures2DArray.GetPixels(textureIndex) to get and save every texture in the array
    // All textures are white
}
// The Compute Shader

#pragma kernel PaintTexturesInArray

RWTexture2DArray<float4>    rendTexArray    : register(u0); // UAV

#define blocksize 8
[numthreads(blocksize, blocksize, 1)]
void PaintTexturesInArray(uint3 DTid : SV_DispatchThreadID)
{
    // Paint every slice in the array with different color
    rendTexArray[uint3(DTid.x, DTid.y, 0)] = float4(1, 0, 0, 1);
    rendTexArray[uint3(DTid.x, DTid.y, 1)] = float4(0, 1, 0, 1);
    rendTexArray[uint3(DTid.x, DTid.y, 2)] = float4(0, 0, 1, 1);
}

When you copy using Graphics.CopyTexture you’re not actually copying anything on the CPU side - it’s a GPU to GPU process. GetPixels won’t have access to the data. You’ll need to copy your texture to the CPU before trying to access it using any of the GetPixel methods.

Your shader code looks fine, however. :slight_smile:

Apologies for digging this thread up, but there is very little info on this subject elsewhere and this thread has been most helpful on the topic for a compute shader beginner such as myself.

How would one read back the RWTexture2DArray from the GPU into a Texture2DArray in a c# script?

Readback from the GPU will cause a pipeline stall which is expensive so generally considered something to avoid (the probable reason why there’s little info out there).

Since the original thread however, Unity has introduced AsyncGPUReadback which allows asynchronous data transfer and thus avoids the stall.

 // Member fields
public int Width = 512, Height = 512, Depth = 3;
public Texture2DArray Destination;

// Create GPU texture array
var source = new RenderTexture(Width, Height, 0, RenderTextureFormat.ARGB32)
{
    dimension = TextureDimension.Tex2DArray,
    enableRandomWrite = true,
    volumeDepth = Depth
};

source.Create();

//# Write to array. (Dispatch compute shader code goes here)

// Create a request and pass in a method to capture the callback
AsyncGPUReadback.Request(source, 0, 0, Width, 0, Height, 0, source.volumeDepth, new Action<AsyncGPUReadbackRequest>
(
    (AsyncGPUReadbackRequest request) =>
    {
        if (!request.hasError)
        {
            // Create CPU texture array
            Destination = new Texture2DArray(Width, Height, request.layerCount, TextureFormat.ARGB32, false);

            // Copy the data
            for (var i = 0; i < request.layerCount; i++)
            {
                Destination.SetPixels32(request.GetData<Color32>(i).ToArray(), i);
            }

            Destination.Apply();
       
            // You'll want to release the no longer required GPU texture somewhere here
            // source.Release();
        }
    }
));

Thanks so much for the example!

1 Like

Unfortunately, AsyncGPUReadback does not work on GLES. Are there plans to add support for it?

You can copy the texture using GPU if it supports hardware

if (SystemInfo.copyTextureSupport == CopyTextureSupport.None)
{
    destination.SetPixels(tex.GetPixels(0), index, 0);
}
else
{
    Graphics.CopyTexture(tex, 0, destination, index);
}