Search Unity

Setting an array of textures possible?

Discussion in 'Shaders' started by TOES, Dec 12, 2020.

  1. TOES

    TOES

    Joined:
    Jun 23, 2017
    Posts:
    134
    I have an advanced shader in GLSL that takes an array of sampler2DArray. So, the GLSL has:

    Code (CSharp):
    1. uniform sampler2DArray textureArrays[64];  //Note, 64 cannot be hardcoded, it could be any number of entries and should be easy to change, so I cannot make 64 individual samplers and some branching as a workaround for example, this would also be slow and ugly.
    2.  
    The problem is, in Unity I can only set one such array, like this

    Code (CSharp):
    1. Texture2DArray ta;
    2. ...
    3. renderEngineMaterial.SetTexture("textureArray", ta);

    While I need something like this:

    Code (CSharp):
    1. Texture2DArray []tas= new Texture2DArray[64];
    2. ...
    3. renderEngineMaterial.SetTexture("textureArray", tas);
    Is this impossilbe in Unity? Any workarounds?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    A Texture2DArray is itself the only allowed “array of textures”. Neither Direct3D* nor OpenGL support actual arrays of textures, only explicit Texture2DArrays. Some shader compilers will let you do something like that
    uniform sampler2D myTextures[16];
    , but that number must be defined in the shader and cannot be dynamic because at compile time it’s converting that array into:
    Code (CSharp):
    1. uniform sampler2D myTextures0;
    2. uniform sampler2D myTextures1;
    3. uniform sampler2D myTextures2;
    4. // etc up to the hard set count
    Similarly any code you have that iterates over that “array” of textures is being unrolled too and likely every single texture is being individual sampled regardless of if you would expect a “dynamic” loop to do so.

    The work around to this is ... don’t do it. If you want an array of texture arrays, the best you can do is have a bigger texture array.

    * Technically both Vulkan and Direct3D 12 support real arrays of textures, and by extension arrays of texture arrays, but this isn’t something you can do with Unity.
     
  3. TOES

    TOES

    Joined:
    Jun 23, 2017
    Posts:
    134
    Thank you, this is very useful information! :) I saw something about this before, but I thought it must be referring to an old version of GLSL, too bad if there is still no proper array support natively.

    I could set a max array size, to say 3000 textures, and then just never access those outside the actual array size, but if I understand you right then each reference to a texture element would be translated into a giant list of if branch tests. So a reference to

    uniform sampler2D myTextures[3000];


    where the index is a non constant int, would actually be translated into 3000 if's? Phew, that is definitively not optimal.

    The reason I am doing this is because I am writing a raytracing extension in GLSL that needs to be triggered from Unity. The raytracer is an editor tool, so I cannot control the number of or format of the textures supplied form the developer.

    Today I simply resize and reformat all textures into one sampler2DArray. This approach works well actually, but this requires all the textures to be in the same format and size. So say the developer uses 100 256x256 textures and 50 4096x4096 textures, they would all have to be in the same resolution. If upscaling, they would waste memory, if downscaling they would lose details. I could upscale every texture to 8192x8192 theoretically, but then of course, the graphics card would run out of memory.

    So, I was thinking I can make multiple sampler2DArray's that all have a specific size and format. I suppose I can make one per size, 2x2, 4x4, 8x8...8192x8192, and if/else the index to get the sampler2darray I need. That will be better than todays method, but I still have to convert the texture format to something quite memory consuming. Like 32bit ARGB. That soon becomes a problem if you have multiple textures like that. Even though the original texture might be compressed, and would fit in GPU-RAM well...

    So, a long array of textures would actually be the best choice theoretically, but typically a render can contain hundreds or thousands of different types of textures (albedo, bump, roughness, opacity and so on), so seems it would compile into a giant shader. Unfortunately I have to use GLSL as the raytracer is now very complex with thousands of line of code, it would be too hard to convert it into another shader language...

    Not sure if this made any sense. Anyway, if you have any suggestions please let me know... :)
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Yep.

    And yes, having a hand full of different arrays of fixed sizes / formats may be your best bet. Depending on what you plan to do with this you could also atlas smaller textures into a larger resolution texture array.