Reputation: 119
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
Shader "Unlit/UniformTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ NO_SPRITE_RENDERER
#include "UnityCG.cginc"
#ifdef UNITY_INSTANCING_ENABLED
UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
#define _RenderData UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
#define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _MainTexOffsetArray)
#endif
#ifndef UNITY_INSTANCING_ENABLED
float4 _RenderData;
float4 _MainTexOffset;
#endif
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
sampler2D _MainTex;
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
#ifdef UNITY_INSTANCING_ENABLED
float4 renderData = _RenderData;
v.vertex.xy += renderData.zw;
o.uv = v.uv * _MainTexOffset.xy + _MainTexOffset.zw;
#else
o.uv = v.uv;
#endif
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
fixed4 col = tex2D(_MainTex, i.uv);
#ifdef UNITY_INSTANCING_ENABLED
float4 renderData = _RenderData;
col.a *= renderData.x;
#endif
return col;
}
ENDCG
}
}
}
And there is a program to use this shader to draw 5 quad meshs at once:
public class DrawTest : MonoBehaviour
{
public Shader shader;
public Texture2D tex;
private Mesh m_mesh;
private Material m_mat;
private MaterialPropertyBlock m_block;
private Matrix4x4[] m_renderArr;
void Start()
{
m_mat = new Material(shader);
m_mat.enableInstancing = true;
m_mat.SetTexture("_MainTex", tex);
m_block = new MaterialPropertyBlock();
m_block.SetVectorArray("_RenderDataArray",
new Vector4[] {
new Vector4(0.5f, 0f, 0f, 0f),
new Vector4(0.3f, 0f, 0f, 0f),
new Vector4(0.6f, 0f, 0f, 0f),
new Vector4(0.8f, 0f, 0f, 0f),
new Vector4(1f, 0f, 0f, 0f)
});
m_block.SetVectorArray("_MainTexOffsetArray",
new Vector4[] {
new Vector4(1f, 1f, 0.2f, 0f),
new Vector4(1f, 1f, 0.4f, 0f),
new Vector4(1f, 1f, 0.6f, 0f),
new Vector4(1f, 1f, 0.8f, 0f),
new Vector4(1f, 1f, 1f, 0f)
});
m_renderArr = new Matrix4x4[5];
m_renderArr[0] = Matrix4x4.Translate(new Vector3(1.5f, 2.5f, -2f));
m_renderArr[1] = Matrix4x4.Translate(new Vector3(-1.5f, 2.5f, -2f));
m_renderArr[2] = Matrix4x4.Translate(new Vector3(1.5f, -1f, -2f));
m_renderArr[3] = Matrix4x4.Translate(new Vector3(-1.5f, -1f, -2f));
m_renderArr[4] = Matrix4x4.Translate(new Vector3(0f, 0.75f, -2f));
var quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
m_mesh = quad.GetComponent<MeshFilter>().sharedMesh;
Destroy(quad);
}
void Update()
{
Graphics.DrawMeshInstanced(m_mesh, 0, m_mat, m_renderArr, m_renderArr.Length, m_block);
}
private float delta;
private float sliderValue = 0.5f;
private void OnGUI()
{
GUILayout.Space(300f);
var val = GUILayout.HorizontalSlider(sliderValue, 0f, 1f, GUILayout.Width(200f), GUILayout.Height(50f));
delta = val - sliderValue;
if (!Mathf.Approximately(0f, delta))
{
Transform camTrans = Camera.main.transform;
camTrans.position += camTrans.forward * delta * 10f;
delta = 0f;
}
sliderValue = val;
}
private void OnDestroy()
{
Destroy(m_mat);
}
}
now we see the result,the right top quad is flickering:
If we change the shader code,move _MainTexOffsetArray to another uniform buffer:
#ifdef UNITY_INSTANCING_ENABLED
UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
UNITY_INSTANCING_BUFFER_START(AnotherDataPerDraw)
UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
UNITY_INSTANCING_BUFFER_END(AnotherDataPerDraw)
#define _RenderData UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
#define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(AnotherDataPerDraw, _MainTexOffsetArray)
#endif
if we revert the shader code and add another property between _RenderDataArray and _MainTexOffsetArray:
#ifdef UNITY_INSTANCING_ENABLED
UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
UNITY_DEFINE_INSTANCED_PROP(float2,_FlipArray)
UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
#define _RenderData UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
#define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _MainTexOffsetArray)
#define _Flip UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw,_FlipArray)
#endif
this time more quad is flickering:
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.
Shader "Unlit/UniformTest"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ NO_SPRITE_RENDERER
#include "UnityCG.cginc"
#ifdef UNITY_INSTANCING_ENABLED
UNITY_INSTANCING_BUFFER_START(CustomDataPerDraw)
UNITY_DEFINE_INSTANCED_PROP(float4, _RenderDataArray)
UNITY_DEFINE_INSTANCED_PROP(float2,_FlipArray)
UNITY_DEFINE_INSTANCED_PROP(float4,_MainTexOffsetArray)
UNITY_INSTANCING_BUFFER_END(CustomDataPerDraw)
#define _RenderData UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _RenderDataArray)
#define _MainTexOffset UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw, _MainTexOffsetArray)
#define _Flip UNITY_ACCESS_INSTANCED_PROP(CustomDataPerDraw,_FlipArray)
#endif
#ifndef UNITY_INSTANCING_ENABLED
float4 _RenderData;
float4 _MainTexOffset;
#endif
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
// Fragment shader does not directly use uniform variable, but uses vertex interpolation
float alpha : SD_ALPHA;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
sampler2D _MainTex;
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
#ifdef UNITY_INSTANCING_ENABLED
float4 renderData = _RenderData;
v.vertex.xy += renderData.zw;
o.uv = v.uv * _MainTexOffset.xy + _MainTexOffset.zw;
o.vertex = UnityObjectToClipPos(v.vertex);
// Fragment shader does not directly use uniform variable, but uses vertex interpolation
o.alpha = renderData.x;
#else
o.uv = v.uv;
o.alpha = 1.0;
#endif
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
fixed4 col = tex2D(_MainTex, i.uv);
#ifdef UNITY_INSTANCING_ENABLED
// Fragment shader does not directly use uniform variable, but uses vertex interpolation
col.a *= i.alpha;
#endif
return col;
}
ENDCG
}
}
}
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?
Upvotes: 2
Views: 1185