GPUInstancing + Texture2dArray Issue

Hi! I’ve been throwing myself at this problem for the last few days but I can’t find enough information to work out where I’m going wrong.

I’m trying to reduce draw calls on a project by using GPU Instancing which was simple enough but this broke down as soon as I had multiple materials. To work around this I found a few good references about using “Material Property Blocks” (Rendering 19) to help batch the same objects together. I have this working perfectly for ints and floats within the script / shader but when trying to pass custom textures this broke down. I’ve been looking at Tikkub’s post here:

https://www.reddit.com/r/Unity3D/comments/6uueox/gpu_instancing_texture2darray/

They use a 2D texture array to batch textures. After trying the same thing I don’t get the same result, the objects aren’t batching anymore. Would love some advice on what I’ve missed.

Image below showing, No textures 22 calls, with textures 742 calls.

Game Object Script

using UnityEngine;
public class GPUInstancingTest : MonoBehaviour
{
    public Transform mesh;
    public Material material;
    private MaterialPropertyBlock Props;

    public Color Color1, Color2;
    public Color emissionColor;
    public int instances = 5000;
    public float radius = 50f;

    public Texture2D[] textures;
    int textureWidth = 1024;
    int textureHeight = 1024;

    void Start()
    {
        Props = new MaterialPropertyBlock();

        Texture2DArray textureArray = new Texture2DArray(textureWidth, textureHeight, textures.Length, TextureFormat.RGBA32, false);
        //textureArray.Apply(false, true); //Didn't fix compression issue.

        for (int i = 0; i < textures.Length; i++)
        {
            Graphics.CopyTexture(textures[i], 0, 0, textureArray, i, 0);
        }

        //Comment out below line. Everything batches but no textures at set.
        Props.SetTexture("_MainTex", textureArray);

        for (int i = 0; i < instances; i++)
        {
            Props.SetColor("_Color", Color1);
            Props.SetFloat("_Glossiness", 0.0f);
            Props.SetFloat("_Metallic", 5.0f);
            Props.SetFloat("_OcclusionStrength", 0.0f);
            Props.SetColor("_EmissionColor", emissionColor);
            Props.SetInt("_TextureIndex", 1);

            Transform t = Instantiate(mesh);
            t.localPosition = Random.insideUnitSphere * radius;
            t.SetParent(transform);
            t.GetComponent<MeshRenderer>().SetPropertyBlock(Props);
        }

        for (int i = 0; i < instances; i++)
        {
            Props.SetColor("_Color", Color2);
            Props.SetFloat("_Glossiness", 1.0f);
            Props.SetFloat("_Metallic", 0.3f);
            Props.SetFloat("_OcclusionStrength", 0.0f);
            Props.SetInt("_TextureIndex", 0);

            Transform r = Instantiate(mesh);
            r.localPosition = Random.insideUnitSphere * radius;
            r.SetParent(transform);
            r.GetComponent<MeshRenderer>().SetPropertyBlock(Props);
        }

        for (int i = 0; i < instances; i++)
        {
            Props.SetColor("_Color", Color.green);
            Props.SetFloat("_Glossiness", 0.5f);
            Props.SetFloat("_Metallic", 1.0f);
            Props.SetFloat("_OcclusionStrength", 0.0f);

            Transform k = Instantiate(mesh);
            k.localPosition = Random.insideUnitSphere * radius;
            k.SetParent(transform);
            k.GetComponent<MeshRenderer>().SetPropertyBlock(Props);
        }
    }
}

Shader

Shader "Custom/Example" {

    Properties{

        //---Core Properties---//
        [Header(Core Properties)]

        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Textures", 2DArray) = "" {}

        _Glossiness("Roughness Strength", Range(0.0, 2.0)) = 1.0
        _Metallic("Metallic Strength", Range(0.0, 2.0)) = 1.0
        _OcclusionStrength("Occlusion Strength", Range(0.0, 2.0)) = 1.0

        [Header(Emissive)]
        _EmissionColor("Emission Color", Color) = (0,0,0)
    }

        SubShader
        {
            Tags
            {
                "Queue" = "Geometry"
                "RenderType" = "Opaque"
            }

            LOD 300
            ZWrite On
              
            CGPROGRAM
            #pragma surface surf Standard fullforwardshadows addshadow
            #pragma target 3.5
            #include "UnityCG.cginc"
           
            UNITY_DECLARE_TEX2DARRAY(_MainTex);

            UNITY_INSTANCING_BUFFER_START(Props)

            half _Glossiness;
            half _Metallic;
            half _OcclusionStrength;
           
            float4 _Color;

            float _TextureIndex;

            fixed4 _EmissionColor;

            UNITY_INSTANCING_BUFFER_END(Props)

            struct Input
            {
                float2 uv_MainTex;
            };

            void surf(Input IN, inout SurfaceOutputStandard o)
            {
                fixed4 c = UNITY_SAMPLE_TEX2DARRAY(_MainTex, float3(IN.uv_MainTex, UNITY_ACCESS_INSTANCED_PROP(Props, _TextureIndex))) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
                fixed4 e = UNITY_ACCESS_INSTANCED_PROP(Props, _EmissionColor);
               
                o.Albedo = c.rgb;

                o.Emission = c.rgb;
                o.Emission *= e.rgb;

                o.Smoothness = (1 - UNITY_ACCESS_INSTANCED_PROP(Props, _Glossiness));

                o.Metallic = UNITY_ACCESS_INSTANCED_PROP(Props, _Metallic);

                o.Occlusion = UNITY_ACCESS_INSTANCED_PROP(Props, _OcclusionStrength);
            }
            ENDCG

        }
            FallBack "Diffuse"
}

What does the Frame Debugger say? (It has a message about why each batch was broken)

Hi richardkettlewell, thank you for the response.

The Frame Debugger says “Non-instanced properties set for instanced shader.”

I’m unsure why the Texture2DArray can’t be instanced. Both Tikkub and my favorite snail bgolus confirmed this should work.

“If you want to use multiple textures you need to use a Texture2DArray you construct yourself, and set a per-instance index.” - bgolus

Interesting. Could you submit a bug report with a reproduction project? We can then take a closer look.
I can’t see any obvious problem with your code, but maybe someone else reading this will spot something. But, in case the problem is in Unity’s code, the bug report will help us fix it :slight_smile:

Reply here with the case number once you have it. Thanks!

Hi richardkettlewell, thank you again.

As requested I’ve submitted a bug report, the case number is: 1186136

1 Like

Hi Richard, I’m not sure if I’m looking in the wrong place but I’ve not heard anything since posting the bug report. Do you know how long it will take to resolve the issue?

Hey, I’ve contacted the developer who will be working on this issue. Hopefully they will be able to provide an update.

Thank you Richard