You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

安卓7.0及以下搭载Adreno机型GPU Instancing闪烁异常问题

高通Adreno GPU Android 7.0及以下版本GPU Instancing渲染异常问题

问题概述

使用GPU Instancing功能时,仅在Android 7.0及以下版本、搭载高通Adreno GPU的移动设备上出现未知渲染异常,疑似与内存布局相关。

完整复现步骤

步骤1:准备GPU Instancing专用Shader

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
        }
    }
}

步骤2:准备批量绘制测试脚本

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);
   }
}

运行测试后可见右上角Quad出现随机闪烁现象

多轮修改验证结果

  • 测试1:将两个instanced属性拆分到独立的Uniform Buffer中,Shader修改如下:
#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

修改后闪烁范围扩大,更多Quad出现渲染异常

  • 测试2:还原Shader后,在同个Uniform Buffer的两个属性中间新增一个属性,修改如下:
#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

修改后闪烁的Quad数量进一步增加

  • 测试3:修改逻辑,片元着色器不再直接访问instanced uniform变量,改为顶点着色器读取后通过插值变量传递给片元,修改后的Shader如下:
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
        }
    }
}

修改后异常完全消失,所有Quad渲染正常

根因咨询

经多轮测试确认,该问题仅出现在Android 7.0及以下版本、搭载高通Adreno GPU的移动设备上,通过RenderDoc抓帧排查可见uniform数据未正确传入片元着色器。目前对根因存疑:

  1. 是否为Android 7.0版本系统限制,同Shader程序的顶点着色器和片元着色器无法同时访问同一块instanced uniform buffer数据?
  2. 是否为高通旧版GPU驱动的已知Bug?

内容的提问来源于stack exchange,提问作者Marsir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:18:03