Texture Array Shader Missing DOTS_INSTANCING_ON Variant

I’m not very familiar with shaders in general, but I’m trying to add a Texture Array to render different textures on my voxel terrain and that requires writing a shader to handle it. I followed the example shown here in the docs:
Unity - Manual: Texture arrays but I’m running into the following DOTS error:

My meshes are generated on the fly, so I don’t get any benefit from instancing (similar to how chunks are rendered in Minecraft).

I mostly just want to get a simple shader set up for using a texture array. Like I said, I’m not very familiar with shading and there aren’t too many resources yet on creating shaders that are DOTS/SRP/URP compatible, so I’m just looking for extra guidance on how to get this simple shader up and running.

Any help will be greatly appreciated!

For the sake of showing what I’ve tried, I tried adding the following #pragma:

#pragma multi_compile _ DOTS_INSTANCING_ON
But then I get the following error:

Which makes even less sense than the previous error. I’ve looked into the CBUFFER_START(UnityPerMaterial) and the UNITY_DOTS_INSTANCED_PROP instructions here: Unity - Manual: DOTS Instancing shaders but it didn’t help with getting rid of the errors (or understanding what the errors mean).

The error message happens if Unity doesn’t find the constant buffers that are required to render with BatchRendererGroup. The shader should define at least one block of DOTS instanced properties similar to this example from the manual (and include the UnityInstancing.hlsl file in order to be able to do it).

UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
    UNITY_DOTS_INSTANCED_PROP(float4, Color)
UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)

Of course, it doesn’t need to be a color, it could also be e.g. a transform matrix.

Typical Unity shaders and Shader Graphs always have at least one already, because the built-in matrices for DOTS_INSTANCING_ON are defined as a built-in DOTS instanced property block, but if you are writing a completely custom shader then you need to define at least one.

1 Like

Thank you for the help. I’m now running into an issue where the shader doesn’t know what UNITY_DOTS_INSTANCING_START is, even though I believe I’m using all the right #pragmas:
8805388--1197541--upload_2023-2-14_7-59-36.png

Shader "GridColonies/SectorShader"
{
    Properties
    {
        _MyArr ("Tex", 2DArray) = "" {}
        _Color ("Base Colour", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma target 4.5
            #pragma vertex vert
            #pragma fragment frag
            // texture arrays are not available everywhere,
            // only compile shader on platforms where they are
            #pragma require 2darray
            #pragma multi_compile _ DOTS_INSTANCING_ON
           
            #include "UnityCG.cginc"

            struct v2f
            {
                float3 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata) // Error on line 29
                UNITY_DOTS_INSTANCED_PROP(float4, _Color)
            UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)

            v2f vert (float4 vertex : POSITION)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(vertex);
                o.uv.xy = (vertex.xy + 0.5);
                o.uv.z = (vertex.z + 0.5);
                return o;
            }
           
            UNITY_DECLARE_TEX2DARRAY(_MyArr);

            half4 frag (v2f i) : SV_Target
            {
                return UNITY_SAMPLE_TEX2DARRAY(_MyArr, i.uv);
            }
            ENDCG
        }
    }
}

Maybe I’m still missing something? I tried adding the CBUFFER_START and end again, but still had the same error with UNITY_DOTS_INSTANCING_START.

That sounds like you’re missing the right include, which should be #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"

1 Like

Unfortunately still getting the same error…

I think the UNITY_DOTS_INSTANCING_START block needs to be wrapped in #ifdef DOTS_INSTANCING_ON so it doesn’t cause compile errors when the keyword is disabled.

1 Like

I’m getting a new error now, which is exciting!

This looks to be an error inside of Unity’s code? It’s pointing to UnityDOTSInstancing.hlsl (line 185)
8805805--1197643--upload_2023-2-14_9-47-4.png

Not sure what a real4 is. Is there anything else I’m missing in terms of compatibility?

8805805--1197640--upload_2023-2-14_9-45-19.png

Shader error in ‘GridColonies/SectorShader’: unrecognized identifier ‘real4’ at /###/###/###/Library/PackageCache/com.unity.render-pipelines.core@14.0.3/ShaderLibrary/UnityDOTSInstancing.hlsl(185) (on d3d11)
Compiling Subshader: 0, Pass: <Unnamed Pass 0>, Vertex program with DOTS_INSTANCING_ON
Platform defines: SHADER_API_DESKTOP UNITY_ENABLE_DETAIL_NORMALMAP UNITY_ENABLE_REFLECTION_BUFFERS UNITY_LIGHTMAP_FULL_HDR UNITY_LIGHT_PROBE_PROXY_VOLUME UNITY_PBS_USE_BRDF1 UNITY_SPECCUBE_BLENDING UNITY_SPECCUBE_BOX_PROJECTION UNITY_USE_DITHER_MASK_FOR_ALPHABLENDED_SHADOWS
Disabled keywords: SHADER_API_GLES30 UNITY_ASTC_NORMALMAP_ENCODING UNITY_COLORSPACE_GAMMA UNITY_FRAMEBUFFER_FETCH_AVAILABLE UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS UNITY_HARDWARE_TIER1 UNITY_HARDWARE_TIER2 UNITY_HARDWARE_TIER3 UNITY_LIGHTMAP_DLDR_ENCODING UNITY_LIGHTMAP_RGBM_ENCODING UNITY_METAL_SHADOWS_USE_POINT_FILTERING UNITY_NO_DXT5nm UNITY_NO_FULL_STANDARD_SHADER UNITY_NO_SCREENSPACE_SHADOWS UNITY_PBS_USE_BRDF2 UNITY_PBS_USE_BRDF3 UNITY_PRETRANSFORM_TO_DISPLAY_ORIENTATION UNITY_UNIFIED_SHADER_PRECISION_MODEL UNITY_VIRTUAL_TEXTURING

real4 is a compatibility typedef for values that could potentially be compiled to 16-bit arithmetic types on some platforms (as opposed to float4 which will always be a full 32-bit float).

If you’re getting an error about that, then you’re probably missing more includes. If you’re working with URP, you could try this include:

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

And if you’re not working with URP, then perhaps try this:

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Version.hlsl"
1 Like

Found out some more information based on your help:

  1. The URP way of using Texture Arrays is to use the new texture array macros
  2. I needed to remove the #include UnityCG.cginc due to duplicate definitions (Redefinition of “_Time” for example)

TEXTURE2D_ARRAY() instead of UNITY_DECLARE_TEX2DARRAY()
SAMPLE_TEXTURE2D_ARRAY() instead of UNITY_SAMPLE_TEX2DARRAY()

Which results in this modification of the original code:

TEXTURE2D_ARRAY(_MyArr);
SAMPLER(sampler_MyArr);

half4 frag (v2f i) : SV_Target
{
    return SAMPLE_TEXTURE2D_ARRAY(_MyArr, sampler_MyArr, i.uv.xy, i.uv.z);
}

At this point I only have one remaining issue it seems. I couldn’t find any information on this elsewhere, but I’m getting this error now:

Here is the full code I have so far with your help:

Shader "GridColonies/SectorShader"
{
    Properties
    {
        _MyArr ("Tex", 2DArray) = "" {}
        _Color ("Base Colour", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Pass
        {
            HLSLPROGRAM
            #pragma target 4.5
            #pragma vertex vert
            #pragma fragment frag
            // texture arrays are not available everywhere,
            // only compile shader on platforms where they are
            #pragma require 2darray
            #pragma multi_compile _ DOTS_INSTANCING_ON
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"

            struct v2f
            {
                float3 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            #ifdef DOTS_INSTANCING_ON
                UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
                    UNITY_DOTS_INSTANCED_PROP(float4, _Color)
                UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
                #define _Color UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, _Color)
            #endif

            v2f vert (float4 vertex : POSITION)
            {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, vertex);
                o.uv.xy = (vertex.xy + 0.5);
                o.uv.z = (vertex.z + 0.5);
                return o;
            }
          
            TEXTURE2D_ARRAY(_MyArr);
            SAMPLER(sampler_MyArr);

            half4 frag (v2f i) : SV_Target
            {
                return SAMPLE_TEXTURE2D_ARRAY(_MyArr, sampler_MyArr, i.uv.xy, i.uv.z);
            }
            ENDHLSL
        }
    }
}

I investigated this for a bit. The reason why your shader didn’t work is that it didn’t set up the instance ID correctly, and as a result the shader compiler removed the DOTS instancing code as unused. This is why Unity thought that there is no DOTS instancing in the shader.

The version below should work:

            struct VertexInput
            {
                float4 pos : POSITION0;
                #if UNITY_ANY_INSTANCING_ENABLED
                uint instanceID : INSTANCEID_SEMANTIC;
                #endif
            };

            struct v2f
            {
                float3 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                #if UNITY_ANY_INSTANCING_ENABLED
                uint instanceID : CUSTOM_INSTANCE_ID;
                #endif
            };

            #ifdef DOTS_INSTANCING_ON
                UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
                    UNITY_DOTS_INSTANCED_PROP(float4, _Color)
                UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
                #define _Color UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, _Color)
            #endif

            v2f vert(VertexInput v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                float4 vertex = v.pos;
                o.vertex = mul(UNITY_MATRIX_MVP, vertex);
                o.uv.xy = (vertex.xy + 0.5);
                o.uv.z = (vertex.z + 0.5);
                return o;
            }

            TEXTURE2D_ARRAY (_MyArr);
            SAMPLER (sampler_MyArr);

            half4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);

                return SAMPLE_TEXTURE2D_ARRAY(_MyArr, sampler_MyArr, i.uv.xy, i.uv.z);
            }
1 Like

That worked perfectly! Thank you so much for your help with this JussiKnuuttila, you’ve been fantastic.

Here’s the resulting product after configuring the shader:

And the source code in case anyone else is interested:

Shader "GridColonies/SectorShader"
{
    Properties
    {
        _MyArr ("Tex", 2DArray) = "" {}
        _Color ("Base Colour", Color) = (1, 1, 1, 1)
    }
    SubShader
    {
        Pass
        {
            HLSLPROGRAM
            #pragma target 4.5
            #pragma vertex vert
            #pragma fragment frag
            // texture arrays are not available everywhere,
            // only compile shader on platforms where they are
            #pragma require 2darray
            #pragma multi_compile _ DOTS_INSTANCING_ON
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"

            struct VertexInput
            {
                float4 pos : POSITION0;
                float4 tex : TEXCOORD0;
                #if UNITY_ANY_INSTANCING_ENABLED
                uint instanceID : INSTANCEID_SEMANTIC;
                #endif
            };

            struct v2f
            {
                float3 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                #if UNITY_ANY_INSTANCING_ENABLED
                uint instanceID : CUSTOM_INSTANCE_ID;
                #endif
            };

            #ifdef DOTS_INSTANCING_ON
                UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
                    UNITY_DOTS_INSTANCED_PROP(float4, _Color)
                UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
                #define _Color UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(float4, _Color)
            #endif

            v2f vert (VertexInput v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);

                o.vertex = mul(UNITY_MATRIX_MVP, v.pos);
               
                // Texture uvs
                o.uv.xy = v.tex.xy;

                // Texture array index
                o.uv.z = v.tex.z;

                // Height for tiling the texture
                o.uv.y *= v.tex.w;

                return o;
            }
           
            TEXTURE2D_ARRAY(_MyArr);
            SAMPLER(sampler_MyArr);

            half4 frag (v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);

                return SAMPLE_TEXTURE2D_ARRAY(_MyArr, sampler_MyArr, i.uv.xy, i.uv.z);
            }
            ENDHLSL
        }
    }
}
1 Like