Search Unity

Texture samplers limit per pass

Discussion in 'Shaders' started by VictorKs, Dec 30, 2018.

  1. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    242
    How many Tex2D can I use in a pass?? Does using a texture array give me more samplers per pass?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    A shader can use up to 16 samplers per pass. Unity's surface shaders or lighting functions will use up some of those.

    However you can sample a single texture as many times as you want as it's reusing the same sampler. This is one of the reasons using a texture atlas is useful.

    Texture arrays also use a single sampler, so you can sample any layers from the array as many times as you want.

    Each sampler is actually a physical bit of the GPU designed to read and decode image data. The basic OpenGL and D3D9 shader code using sample2D and tex2D() is defining and using the sampler state (clamping, filtering, etc.) and the texture data at the same time, so they're linked together and each texture uses up a sampler unit. Using D3D11 style texture2D, samplerstate, and tex.Load(sampler, uv) breaks up the two and let's you reuse one sampler on multiple textures, or multiple samplers on a single texture.
     
    GenaSG and VictorKs like this.
  3. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    242
    Thanks for the info so I guess I have a maximum of 16 textures per pass. About texture arrays you mean that sampling any layer costs 1 sampler right?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    Sampling 1 layer costs 1 sampler.

    Sampling 100 layers 100 times still costs 1 sampler. Not each, total.

    Think about it this way. You can sample (call tex2D()) on any one texture as many times as you want and it'll only cost one sampler total be it 1 or 100 times. A Texture2DArray is considered "one texture" in this context, and can be sampled as many times as you want at the cost of only one sampler.

    Sampler limits are more about the number of texture samplers defined in the shader outside the vertex or fragment functions, not how many times the texture is sampled. In other words how many "sampler2D" or "UNITY_DECLARE_TEX2DARRAY" lines you have in your shader file or in the includes that are also used by the shader. Those that are defined, but never sampled, are removed at compile time.
     
    Last edited: Dec 31, 2018
    Beauque, GenaSG and VictorKs like this.
  5. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    242
    This is great news so I guess there is no need for a texture atlas. I mean suppose I use 6 textures for a mesh, I can store the layer index in the uv0.z and then sample using only 1 sampler ?? Is this the usual workflow?
     
  6. bgolus

    bgolus

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

    Nope, an atlas is the usual workflow, or just using multiple materials. The most common place texture arrays are used are for terrain, or other situations where many textures need to be blended between (and thus sampled at once). Generally the additional friction of dealing with texture arrays isn't worth the effort.

    Some games do go this route though, often moving everything into texture arrays to reduce draw calls, or more specifically reduce render state changes. It's just usually not necessary unless you're really pushing how much stuff you want to put on screen.
     
    MaxEden, GenaSG and VictorKs like this.
  7. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    242
    Well I'm trying to render 1600 units with attachments(weapons/shields/helmets) so to reduce draw calls I combined all the meshes and use 1 material. Because I'm CPU bound.
    So texture arrays come in pretty handy, the only issue I've encountered though was size for some reason array layers are double size the original texture where is this attributed?
     
  8. Teosis

    Teosis

    Joined:
    Jan 24, 2018
    Posts:
    22
    Hey, would the same principle apply to the Shader Graph shaders?
    In other words, does this picture below mean I'm using a single Sampler regardless of how many Sample Texture 2D nodes are present there provided that I feed to them the same atlas texture?
    Additional question. Supposing that I'm storing my normal maps alongside albedo maps in the same atlas and, as such, I have to change the Type of the Sample Texture 2D node from Default to Normal, would it still be considered as using the same Sampler, or would it break it up in two different ones?
    Thanks in advance!
    P.s.
    Also, just to clarify, am I right to assume that if the above mentioned is true then adding different Sampler States nodes (i.e. one to the Normal Texture and a different one to the Albedo one) will not split the whole thing in two Samplers as long as Sample Texture 2D nodes use the same atlas texture?
     
    Last edited: Dec 10, 2019
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    As long as you're using a single texture asset and not overriding the Sampler(SS) with a unique Sampler State node per Sample Texture node, then yes, it'll reuse the texture sampler for that one texture for all of them. If you plug in a single Sampler State node to all Sample Texture nodes you can even reuse a single sampler with multiple textures, though this doesn't work on OpenGLES platforms (Android).
     
    GenaSG and Teosis like this.
  10. wagner_de_souza

    wagner_de_souza

    Joined:
    Apr 9, 2019
    Posts:
    15
    Hello @bgolus , here in the documentation the max limit of texture is 8 for OpenGL ES 2. Is this wrong?
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    That is the minimum required for the spec... and all most GLES 2.0 devices implemented.
     
    wagner_de_souza likes this.
  12. wagner_de_souza

    wagner_de_souza

    Joined:
    Apr 9, 2019
    Posts:
    15
    :confused: Sorry but I didnt understand:(
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If you are targeting support for OpenGLES 2.0 devices, you will be limited to 8 textures per shader stage. And ideally you want to use less because sampling 8 textures on a GLES 2.0 device will be quite expensive.


    Some very late era GLES 2.0 class devices did support more than 8 textures, but they’re extremely uncommon, and you would not be able to make use of those extra textures using Unity. If you need more textures, you’ll need to use multiple passes, or target GLES 3.0 as your min spec.
     
    wagner_de_souza likes this.
  14. devanglimbad

    devanglimbad

    Joined:
    Oct 10, 2018
    Posts:
    5
    Hello @bgolus ,

    I'm doing the same thing for my shader, which has too many textures. Is there any workaround for Android? I can not use an atlas, as my textures are dynamic and updating via APIs.

    I'm building for Quest 2 with OpenGLES 3 , the shader works great on editor, but on Quest 2 it's messed up. I am getting this error:
    1111.png

    Also, how can I make my shadergraph use multiple passes? I'm using the shadergraph with URP on Unity 2021.3.25f1
     
  15. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    If they're dynamic, then you can pack them dynamically.

    Otherwise the solution is... don't do that. Use multiple materials.

    Neither URP* or Shader Graph supports multi pass materials. You'd have to use multiple separate shader graph materials and apply all of them to the mesh. As long as the mesh only has one material index, it will render the mesh with each material one after the other.

    Though, really, the solution is probably to find some way to not have so many textures in one material and split it up into multiple separate materials and meshes that don't overlap.

    (* Technically URP can do multi pass materials, but it's very limited, and you have to hand write the shaders. Even then, it's only a single lit and single unlit pass per material.)
     
  16. devanglimbad

    devanglimbad

    Joined:
    Oct 10, 2018
    Posts:
    5
    @bgolus Thank you for the quick response.
    It's a complex shader that I'm using, it has two layers for the base and coat. And the coating layer will be visible according to the user's raycast.
    So I don't know if I can pack the textures dynamically for this one.

    But yes, I managed to reduce 4 textures from the shader and now it's working on the Android build.