Search Unity

how to fix maximum ps_4_0 sampler register index (16) exceeded?

Discussion in 'Shaders' started by jister, Oct 25, 2017.

  1. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    Code (CSharp):
    1. #if ENABLE_SPLATMAP
    2.             fixed4 s = tex2D (_SplatMap, IN.uv_MainTex);
    3.             fixed4 splat = fixed4(s.r * tex2D(_Splat0, IN.uv_MainTex*_SplatScale).rgb, s.r);
    4.             splat += fixed4(s.g * tex2D(_Splat1, IN.uv_MainTex*_SplatScale).rgb, s.g);
    5.             splat += fixed4(s.b * tex2D(_Splat2, IN.uv_MainTex*_SplatScale).rgb, s.b);
    6.             splat += fixed4(s.a * tex2D(_Splat3, IN.uv_MainTex*_SplatScale).rgb, s.a);
    7.             //c = lerp(c, splat, 1-_Color.a)*0.5;
    8.             c += splat;
    9.             splatB = s.r * UnpackNormal(tex2D (_SplatBump0, IN.uv_MainTex*_SplatScale));
    10.             splatB += s.g * UnpackNormal(tex2D (_SplatBump1, IN.uv_MainTex*_SplatScale));
    11.             splatB += s.b * UnpackNormal(tex2D (_SplatBump2, IN.uv_MainTex*_SplatScale));
    12.             splatB += s.a * UnpackNormal(tex2D (_SplatBump3, IN.uv_MainTex*_SplatScale));
    13.             //b = lerp(b, splatB, 1-_Color.a);
    14.             b = lerp(splatB, fixed3(0, 0, 1), -_SplatBumpStrength + 1);
    15.             #endif
    if i use
    Code (CSharp):
    1.  c += splat;
    or
    Code (CSharp):
    1. b = lerp(b, splatB, 1-_Color.a);
    i get the error
    Code (CSharp):
    1. maximum ps_4_0 sampler register index (16) exceeded
    if i use
    Code (CSharp):
    1. c = splat;
    i don't get the error?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
  3. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    @Atair 's quote is probably the most important bit there. My comment about Texture2DArrays was more a comment on using a Texture3D.

    Using a texture array is an option, but using no-sampler textures is generally a little more straight forward as it only requires shader code modifications. Here's the document he was quoting:
    https://docs.unity3d.com/Manual/SL-SamplerStates.html

    See the UNITY_DECLARE_TEX2D and related macros mentioned on that page.

    Using a Texture2DArray could plausibly be a little more efficient on newer hardware, and gives you a limit of 2048 textures per array. DX11 has a limit of 128 texture objects per shader, so you could theoretically pass 262144 textures to your shader with texture arrays. However it's important to note that the number of samplers is still limited to 16. Basically when you sample from a texture array you're likely using the same sampler every time. A sampler is a physical bit of silicon on the GPU that the shader is accessing. Reading 8 textures with 8 samplers will cause the textures to be sampled in parallel where as sampling 8 textures with 1 sampler is done in serial as each texture effectively has to wait their turn. Modern GPUs are incredibly fast and very good at hiding the time it takes to sample a texture, but if you pile them all onto one sampler unit it's likely to cause a stall in your shader computation that can show up as a higher frame render time and potentially a lower frame rate.

    If you're targeting OpenGL ES 2.0, texture arrays and separate samplers are out of the question. Your options are texture atlases, multiple passes (which is what Unity's terrain shader does), or abusing cube maps to store 6 textures as a proto-texture array.
     
  5. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    wauw thanks again @bgolus the cubemap method sound smart and easy i'll try that one first ;)
     
  6. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,550
    This part has confused me because, how could it possibly do this in a situation where the next sampler line depends on variable information taken from a previous line's sampled info? Or is that an deoptimization you have to be aware of if you're feeding your sampler any info that isn't coming directly from the vertex function?
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    The shader stalls in this case until the sampler returns the data.

    This is always what happens if you sample a texture and then use the output to control another texture sample, and has nothing to do with multiple or single samplers. If you can you want to do the initial sample as early as possible and any additional math that's not dependent on either in between. Some recent desktop GPUs can do some juggling where it'll start the shader code for next group of pixels while waiting for the sampler units to finish, but don't expect that. How long a texture takes to "sample" is highly variable, so how many instructions to put between the first and second texture sample isn't a fixed number. It depends on the texture size, format, GPU memory bandwidth, etc.

    So yes, this is a de-optimization you should be aware of. But understand this isn't necessarily going to add multiple milliseconds to your render time, it's unlikely to be even a single millisecond. That stall is going to be measured in microseconds for all but the most egregious cases.
     
    Invertex likes this.
  8. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    ok this is what i ended up with:

    Code (CSharp):
    1. _Splat0("Splat 0",2D) = "white" {}
    2.         _Splat1("Splat 1",2D) = "white" {}
    3.         _Splat2("Splat 2",2D) = "white" {}
    4.         _SplatBump0("Splat Bump 0",2D) = "bump" {}
    5.         _SplatBump1("Splat Bump 1",2D) = "bump" {}
    6.         _SplatBump2("Splat Bump 2",2D) = "bump" {}
    7.         _SplatScale("Splat Map Tilling",float) = 15
    8.         _SplatBumpStrength("Splat Bump Strength", float) = 2
    9. ...
    10. uniform sampler2D _SplatMap;
    11.         uniform sampler2D _MainTex;
    12.         uniform sampler2D _Bump;
    13. UNITY_DECLARE_TEX2D(_Splat0);
    14.         UNITY_DECLARE_TEX2D_NOSAMPLER(_Splat1);
    15.         UNITY_DECLARE_TEX2D_NOSAMPLER(_Splat2);
    16.         UNITY_DECLARE_TEX2D(_SplatBump0);
    17.         UNITY_DECLARE_TEX2D_NOSAMPLER(_SplatBump1);
    18.         UNITY_DECLARE_TEX2D_NOSAMPLER(_SplatBump2);
    19.  
    20. #if ENABLE_SPLATMAP
    21.             fixed4 s = tex2D (_SplatMap, IN.uv_MainTex);
    22.  
    23.             fixed4 splat = fixed4(s.r * UNITY_SAMPLE_TEX2D(_Splat0, IN.uv_MainTex*_SplatScale).rgb, s.r);
    24.             splat += fixed4(s.g * UNITY_SAMPLE_TEX2D_SAMPLER(_Splat1,_Splat0, IN.uv_MainTex*_SplatScale).rgb, s.g);
    25.             splat += fixed4(s.b * UNITY_SAMPLE_TEX2D_SAMPLER(_Splat2,_Splat0, IN.uv_MainTex*_SplatScale).rgb, s.b);
    26.             splat += fixed4(s.a * c.rgb, s.a);
    27.             c = splat;
    28.             fixed3 splatB = s.r * UnpackNormal(UNITY_SAMPLE_TEX2D(_SplatBump0, IN.uv_MainTex*_SplatScale));
    29.             splatB += s.g * UnpackNormal(UNITY_SAMPLE_TEX2D_SAMPLER(_SplatBump1,_SplatBump0, IN.uv_MainTex*_SplatScale));
    30.             splatB += s.b * UnpackNormal(UNITY_SAMPLE_TEX2D_SAMPLER(_SplatBump2,_SplatBump0, IN.uv_MainTex*_SplatScale));
    31.             splatB += s.a * UnpackNormal(tex2D(_Bump, IN.uv_MainTex*_SplatScale));
    32.             splatB = lerp(splatB, fixed3(0, 0, 1), -_SplatBumpStrength + 1);
    33.             b = lerp(b, splatB, 1-_Color.a);_SplatBumpStrength + 1);
    34.             #endif