Texture Array Size

Hi,

this is general question about Texture Arrays, but specificaly in my case i am using it in a shader i made with Amplify.
Everything works well, but the problem is that the TextureArray i am creating is massive. It seems like every single texture i add there ( at 1024 resolution ) adds 10 MBs to the overall texture array size. Also i am not sure what is memory it takes to render since it seems i need to be using RGBA32 compression.

Can anyone please elaborate on TextureArray usage and what is its performance ?

Thanks
Luke

RGBA32 is not a form of compression, that’s an uncompressed texture format. And there’s nothing particularly special about texture arrays in terms of memory usage. If a single texture is 10 megabytes by itself, then it’ll be 10 megabytes as a layer in a texture array too.

Yeah, you are right, RGBA32 is not a compression, but my main point was about the fact that when i use RGBA32 in Unity on a 1k texture, its for example 5 MBs, but when i add the same texture to Texture Array under same format and same resolution the size of the Texture Array asset is 10 MBs only with that one texture in it. That is realy confusing to me.
And of course having 10 MBs for only one 1k texture is insanity for a game performance. So i am just wondering if Texture arrays work differently or if people actualy use this workflow with 1k texture taking 10 MBs of memory.

Thanks

Maybe it is an overhead? What if you put 10 or 50 textures in an array, maybe the average goes down?
Also if you can you should use DXT or other compression.

I was hoping that would be the case, but when i put there 20 textures the size was 230 MBs. As i mentioned i am using Amplify Texture Array builder so i dont have much control over it, but i tried to add DXT compression as additional format, but i got bunch of errors that SetPixels32 cant use compression. I honestly dont know much about the internal process how the Texture Array is built with the plugin. If i knew that redoing the Texture Array builder would fix it i would look into it, but it seems its necessary to use uncompressed formats for TextureArray ?

I never compressed runtime textures but you should be able to call Compress after you called SetPixels32().
That of course only makes sense if those textures don’t update often.
If there is anything special to TextureArrays then I can’t help, but it should be possible. At least you need one constant format for all that textures inside.
Maybe this helps, there seems to be no Compress method for TextureArrays.

Yeah, you shouldn’t be using SetPixels() unless you absolutely need uncompressed textures or direct script assignment of the pixel values. If you just need an array of diffuse textures you should be using CopyTexture. Though it is a little curious that the asset size is twice the size it should be. That seems like a bug to me (and I double checked, it does happen with arrays created with CopyTexture too). I would wonder if that’s just an issue with the .asset itself and the size is more as expected for a built project.

Thanks for looking into it. I will see what i can do. Yeah, the size is bothering me a bit. I am just not sure if the double size is actual memory used by gpu or just size of the asset ? Because if its doubling memory usage for gpu, than using Texture Arrays is realy not good solution.

I was looking a bit into the CopyTexture you suggested. Could you please elaborate a bit how to use it when creating Texture2DArray ? I wasnt able to figure it out. It seems only thing available there is SetPixels or SetPixels32 and there is no other way to store the texture into the Texture2DArray. Hopefully i am missing something.

Thanks

The short version:
Take list of Texture2Ds.
Copy each mip from the Texture2D into the appropriate index and mip of a Texture2DArray using Graphics.CopyTexture().
Maybe call “Apply()” on the Texture2DArray with generate mip maps set to false?

There are some discrepancies within the documentation here. Generally Graphics.CopyTexture() is a GPU only function, so any change you make to a texture won’t be reflected on the CPU side of things. The documentation for CopyTexture() states as much:

However the documentation for Texture Arrays says this:

But then the documentation for Texture2DArray.Apply() contradicts that:

So the answer may be that the correct thing to do is always construct the Texture2DArray at runtime from a list of Texture2D assets, then call Texture2DArray.Apply(false, true) to unload the texture array from system memory and leave it on the GPU. I don’t know what happens to the Texture2D assets as they should have had to be uploaded to the GPU to construct the array from when using CopyTexture, but I don’t know if there’s a good way to tell Unity to unload assets from the GPU.

There could also be some extra Editor magic with CopyTexture when copying to a Texture2DArray such that it’s overloading that function to additionally copy in system memory (which usually refers to the CPU side memory), which is what the Texture Array documentation suggests, or it could just be wrong.

I’m late by a couple of years, but we came across this same issue in our game.

The problem is related to the read/write toggle, which has no effect on Texture2DArray objects. Whenever a Texture2DArray updates from the inspector, ta.isReadable will return true.

The workaround I found is to run ‘if (ta.isReadable) { ta.Apply(false, true); }’ at runtime, or as an asset postprocess:

 public class DisableReadWriteTexture2DArray : AssetPostprocessor
{
    void OnPostprocessTexture2DArray(Texture2DArray texture)
    {    
        if (texture.isReadable)
            texture.Apply(false, true);
    }
}

I tested that with the memory profiling tool and it does seem to work.

I also made a bug report about that.

1 Like