Search Unity

Bug A strange bug about GPU Instancing on phones with android version 7.0 and below equipped Adreno

Discussion in 'Shaders' started by HatteFox, Sep 1, 2021.

  1. HatteFox

    HatteFox

    Joined:
    Jan 20, 2021
    Posts:
    12
    When i use GPU instancing, i found that there is a problem that is difficult to understand on mobile phones equipped with Qualcomm processors with Android version 7.0 and below.
    It seems to be related to the memory layout,but only happens on the phone types described above.Let's reproduce this problem step by step.
    First, there is a shader for GPU Instancing
    Code (CSharp):
    1. Shader "Unlit/UniformTest"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Transparent" "Queue"="Transparent" }
    10.  
    11.         Pass
    12.         {
    13.             Blend SrcAlpha OneMinusSrcAlpha
    14.             ZWrite Off
    15.             CGPROGRAM
    16.  
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.             #pragma multi_compile_instancing
    20.             #pragma multi_compile _ NO_SPRITE_RENDERER
    21.  
    22.             #include "UnityCG.cginc"
    23.  
    24.             #ifdef UNITY_INSTANCING_ENABLED
    25.  
    26.             UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
    27.                 UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
    28.                 UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
    29.             UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
    30.      
    31.             #define _RenderData    UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
    32.             #define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _MainTexOffsetArray)
    33.  
    34.             #endif
    35.  
    36.             #ifndef UNITY_INSTANCING_ENABLED
    37.             float4 _RenderData;
    38.             float4 _MainTexOffset;
    39.             #endif
    40.  
    41.             struct appdata
    42.             {
    43.                 float4 vertex : POSITION;
    44.                 float2 uv : TEXCOORD0;
    45.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    46.             };
    47.  
    48.             struct v2f
    49.             {
    50.                 float2 uv : TEXCOORD0;
    51.                 float4 vertex : SV_POSITION;
    52.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    53.             };
    54.  
    55.             sampler2D _MainTex;
    56.  
    57.             v2f vert (appdata v)
    58.             {
    59.                 v2f o;
    60.                 UNITY_SETUP_INSTANCE_ID(v);
    61.                 UNITY_TRANSFER_INSTANCE_ID(v, o);
    62.                 #ifdef UNITY_INSTANCING_ENABLED
    63.                 float4 renderData = _RenderData;
    64.                 v.vertex.xy += renderData.zw;
    65.                 o.uv = v.uv * _MainTexOffset.xy + _MainTexOffset.zw;
    66.                 #else
    67.                 o.uv = v.uv;
    68.                 #endif
    69.                 o.vertex = UnityObjectToClipPos(v.vertex);
    70.                 return o;
    71.             }
    72.  
    73.             fixed4 frag (v2f i) : SV_Target
    74.             {
    75.                 UNITY_SETUP_INSTANCE_ID(i);
    76.                 fixed4 col = tex2D(_MainTex, i.uv);
    77.                 #ifdef UNITY_INSTANCING_ENABLED
    78.                 float4 renderData = _RenderData;
    79.                 col.a *= renderData.x;
    80.                 #endif
    81.                 return col;
    82.             }
    83.             ENDCG
    84.         }
    85.     }
    86. }
    87.  
    And there is a program to use this shader to draw 5 quad meshs at once:
    Code (CSharp):
    1. public class DrawTest : MonoBehaviour
    2. {
    3.     public Shader shader;
    4.     public Texture2D tex;
    5.     private Mesh m_mesh;
    6.     private Material m_mat;
    7.     private MaterialPropertyBlock m_block;
    8.     private Matrix4x4[] m_renderArr;
    9.  
    10.     void Start()
    11.     {
    12.         m_mat = new Material(shader);
    13.         m_mat.enableInstancing = true;
    14.         m_mat.SetTexture("_MainTex", tex);
    15.         m_block = new MaterialPropertyBlock();
    16.         m_block.SetVectorArray("_RenderDataArray",
    17.             new Vector4[] {
    18.                 new Vector4(0.5f, 0f, 0f, 0f),
    19.                 new Vector4(0.3f, 0f, 0f, 0f),
    20.                 new Vector4(0.6f, 0f, 0f, 0f),
    21.                 new Vector4(0.8f, 0f, 0f, 0f),
    22.                 new Vector4(1f, 0f, 0f, 0f)
    23.             });
    24.         m_block.SetVectorArray("_MainTexOffsetArray",
    25.             new Vector4[] {
    26.                 new Vector4(1f, 1f, 0.2f, 0f),
    27.                 new Vector4(1f, 1f, 0.4f, 0f),
    28.                 new Vector4(1f, 1f, 0.6f, 0f),
    29.                 new Vector4(1f, 1f, 0.8f, 0f),
    30.                 new Vector4(1f, 1f, 1f, 0f)
    31.             });
    32.         m_renderArr = new Matrix4x4[5];
    33.         m_renderArr[0] = Matrix4x4.Translate(new Vector3(1.5f, 2.5f, -2f));
    34.         m_renderArr[1] = Matrix4x4.Translate(new Vector3(-1.5f, 2.5f, -2f));
    35.         m_renderArr[2] = Matrix4x4.Translate(new Vector3(1.5f, -1f, -2f));
    36.         m_renderArr[3] = Matrix4x4.Translate(new Vector3(-1.5f, -1f, -2f));
    37.         m_renderArr[4] = Matrix4x4.Translate(new Vector3(0f, 0.75f, -2f));
    38.  
    39.         var quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
    40.         m_mesh = quad.GetComponent<MeshFilter>().sharedMesh;
    41.         Destroy(quad);
    42.  
    43.     }
    44.  
    45.     void Update()
    46.     {
    47.         Graphics.DrawMeshInstanced(m_mesh, 0, m_mat, m_renderArr, m_renderArr.Length, m_block);
    48.     }
    49.  
    50.     private float delta;
    51.     private float sliderValue = 0.5f;
    52.     private void OnGUI()
    53.     {
    54.         GUILayout.Space(300f);
    55.         var val = GUILayout.HorizontalSlider(sliderValue, 0f, 1f, GUILayout.Width(200f), GUILayout.Height(50f));
    56.         delta = val - sliderValue;
    57.         if (!Mathf.Approximately(0f, delta))
    58.         {
    59.             Transform camTrans = Camera.main.transform;
    60.             camTrans.position += camTrans.forward * delta * 10f;
    61.             delta = 0f;
    62.         }
    63.         sliderValue = val;
    64.     }
    65.  
    66.     private void OnDestroy()
    67.     {
    68.         Destroy(m_mat);
    69.     }
    70. }
    71.  
    now we see the result,the left top quad is flickering:
    1.gif
    If we change the shader code,move _MainTexOffsetArray to another uniform buffer:
    Code (CSharp):
    1.             #ifdef UNITY_INSTANCING_ENABLED
    2.  
    3.             UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
    4.                 UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)    
    5.             UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
    6.      
    7.             UNITY_INSTANCING_BUFFER_START(AnotherDataPerDraw)
    8.                 UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
    9.             UNITY_INSTANCING_BUFFER_END(AnotherDataPerDraw)
    10.  
    11.             #define _RenderData    UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
    12.             #define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(AnotherDataPerDraw, _MainTexOffsetArray)
    13.  
    14.             #endif
    this time everything is ok:
    2.gif

    if we revert the shader code and add another property between _RenderDataArray and _MainTexOffsetArray:
    Code (CSharp):
    1.             #ifdef UNITY_INSTANCING_ENABLED
    2.  
    3.             UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
    4.                 UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
    5.                 UNITY_DEFINE_INSTANCED_PROP(float2,_FlipArray)
    6.                 UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
    7.             UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
    8.      
    9.             #define _RenderData    UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
    10.             #define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _MainTexOffsetArray)
    11.             #define _Flip         UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw,_FlipArray)
    12.  
    13.             #endif
    this time more quad is flickering:
    3.gif

    If we let fragment shader does not directly use uniform, but uses vertex interpolation,then every thing is OK.I'm not going to show normal pictures here.
    Code (CSharp):
    1. Shader "Unlit/UniformTest"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Transparent" "Queue"="Transparent" }
    10.  
    11.         Pass
    12.         {
    13.             Blend SrcAlpha OneMinusSrcAlpha
    14.             ZWrite Off
    15.             CGPROGRAM
    16.  
    17.             #pragma vertex vert
    18.             #pragma fragment frag
    19.             #pragma multi_compile_instancing
    20.             #pragma multi_compile _ NO_SPRITE_RENDERER
    21.  
    22.             #include "UnityCG.cginc"
    23.  
    24.             #ifdef UNITY_INSTANCING_ENABLED
    25.  
    26.             UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
    27.                 UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
    28.                 UNITY_DEFINE_INSTANCED_PROP(float2,_FlipArray)
    29.                 UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
    30.             UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
    31.      
    32.             #define _RenderData    UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
    33.             #define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _MainTexOffsetArray)
    34.             #define _Flip         UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw,_FlipArray)
    35.  
    36.             #endif
    37.  
    38.             #ifndef UNITY_INSTANCING_ENABLED
    39.             float4 _RenderData;
    40.             float4 _MainTexOffset;
    41.             #endif
    42.  
    43.             struct appdata
    44.             {
    45.                 float4 vertex : POSITION;
    46.                 float2 uv : TEXCOORD0;
    47.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    48.             };
    49.  
    50.             struct v2f
    51.             {
    52.                 float2 uv : TEXCOORD0;
    53.                 // Fragment shader does not directly use uniform variable, but uses vertex interpolation
    54.                 float alpha : SD_ALPHA;
    55.                 float4 vertex : SV_POSITION;
    56.                 UNITY_VERTEX_INPUT_INSTANCE_ID
    57.             };
    58.  
    59.             sampler2D _MainTex;
    60.  
    61.             v2f vert (appdata v)
    62.             {
    63.                 v2f o;
    64.                 UNITY_SETUP_INSTANCE_ID(v);
    65.                 UNITY_TRANSFER_INSTANCE_ID(v, o);
    66.                 #ifdef UNITY_INSTANCING_ENABLED
    67.                 float4 renderData = _RenderData;
    68.                 v.vertex.xy += renderData.zw;
    69.                 o.uv = v.uv * _MainTexOffset.xy + _MainTexOffset.zw;
    70.                 o.vertex = UnityObjectToClipPos(v.vertex);
    71.                 // Fragment shader does not directly use uniform variable, but uses vertex interpolation
    72.                 o.alpha = renderData.x;
    73.                 #else
    74.                 o.uv = v.uv;
    75.                 o.alpha = 1.0;
    76.                 #endif
    77.                 o.vertex = UnityObjectToClipPos(v.vertex);
    78.                 return o;
    79.             }
    80.  
    81.             fixed4 frag (v2f i) : SV_Target
    82.             {
    83.                 UNITY_SETUP_INSTANCE_ID(i);
    84.                 fixed4 col = tex2D(_MainTex, i.uv);
    85.                 #ifdef UNITY_INSTANCING_ENABLED
    86.                 // Fragment shader does not directly use uniform variable, but uses vertex interpolation
    87.                 col.a *= i.alpha;
    88.                 #endif
    89.                 return col;
    90.             }
    91.             ENDCG
    92.         }
    93.     }
    94. }
    Again, in our many tests, only on mobile phones equipped with Qualcomm processors with Android version 7.0 and below had this problem;
    Use RenderDoc to see more details, uniform data is not passed into the fragment Shader.
    I I'm not sure what the reason is. Is it because the vertex shader and fragment shader in the same shader program cannot use uniform data at the same time in Android 7.0 or is this a bug?