2022.1.5 HDRP shaders - GPU instancing broken?

I have a test project started in 2021.3.2, which I upgraded to 2022.1.5, and now GPU instancing with instanced properties is broken.

Specifically, I have a test project that spawns 20k objects, and a shader graph shader that uses instance properties to modulate their emissive intensity on a per-object basis. The property is defined in the shader blackboard as “shader declaration = hybrid per instance”. The material is set to enable GPU instancing.

This worked correctly in 2021.3, all the instances got GPU instanced, and drawn in a few dozen passes, like so:

8242170--1077882--Screenshot A 1.png


However, after upgrade to 2022.1, this is broken, and each individual building gets drawn in its own draw call:

8222874--1073955--Screenshot B 1.png

The frame debugger message “Non-instanced properties set for instanced shader” is extra confusing, as the instance property is set in the shader graph editor as “hybrid per instance”.

Looking at compiled shader, it looks like the main difference between 2021 and 2022 is that in the old correct version, the instance property got correctly declared using UNITY_DEFINE_INSTANCED_PROP:

        // Injected Instanced Properties (must be included before UnityInstancing.hlsl)
        #if defined(UNITY_HYBRID_V1_INSTANCING_ENABLED)
        #define HYBRID_V1_CUSTOM_ADDITIONAL_MATERIAL_VARS \
        UNITY_DEFINE_INSTANCED_PROP(float, _Emission_Multiplier)
        #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, var)
        #else
        #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var
        #endif

        // -- Graph Properties
        CBUFFER_START(UnityPerMaterial)
    ...
        #ifdef UNITY_HYBRID_V1_INSTANCING_ENABLED
        float _Emission_Multiplier_dummy;
        #else
        float _Emission_Multiplier;
        #endif
        CBUFFER_END

Meanwhile in 2022 it gets added to CBUFFER instead, which is clearly incorrect

        // -- Graph Properties
        CBUFFER_START(UnityPerMaterial)
    ...
        // Hybrid instanced properties
        float _Emission_Multiplier;
        CBUFFER_END
        #if defined(UNITY_DOTS_INSTANCING_ENABLED)
        // DOTS instancing definitions
        UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
            UNITY_DOTS_INSTANCED_PROP(float, _Emission_Multiplier)
        UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)

TLDR: looks like shader graph shaders compiled in 2022.1 break GPU instancing. Is there any fix for this? Because otherwise, that’s a severe performance regression.

Bump. Just wondering if anyone else is running into this?

This seems like a pretty serious regression.

@rzubek Thank you for pointing me toward the Graph Inspector’s ‘Shader Declaration’ setting. Changing this to ‘Hybrid Per Instance’ for my property combined my drawcalls in 2021.31f1 (whereas before I was getting that same “Non-instanced properties set…” reason in the Frame Debugger).

Wish I could be of more help here: after re-building my now functioning 2021 setup in 2022 I do indeed get the same Frame Debug results as you and it is no longer batching same-instance drawcalls. Just to confirm, I am also using GPU Instancing with instanced properties.

It does seem odd that the default behavior would change, but it also looks like a lot of work was done on some new functionality called BatchRendererGroup (BRG) . After a some reading (and very little understanding) it seems plausible that BRG’s implementation in 2022 could have something to do with this behavior change but I’m a Unity / real-time noob and I’m getting in over my head.

1 Like

@WiseAX yeah, it looks like this breaking change is related to the SRP Batcher, which is a related but different beast - as far as I can tell, the SRP batcher attempts to auto-translate different global material properties into instance properties (so that materials that are the same except for, e.g., a different albedo color, could have that property turned into instance properties and rendered in a single pass).

Which sounds neat, although it’s still experimental.

But in the process, they broke standard GPU instancing with instance property blocks.

1 Like

Ok, here’s a terrible hack to work around this broken shader, until they get around to fixing it.

It’s truly terrible, because it makes iterating on the shader graph into a much more painful process.

To restore instanced properties in a shader, one can export the shader code from shader graph (“View Generated Shader” in the .shadergraph file inspector), and then apply the following fixes manually:

Look for section that looks like this:

// Hybrid instanced properties
float _EntityID;
CBUFFER_END
#if defined(UNITY_DOTS_INSTANCING_ENABLED)
// DOTS instancing definitions
UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
UNITY_DOTS_INSTANCED_PROP(float, _EntityID)
UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
// DOTS instancing usage macros
#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(type, var)
#else
#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var
#endif

And replace with something like this:

// Hybrid instanced properties
//float _EntityID;
CBUFFER_END

// HACK: FIXED INSTANCING
UNITY_INSTANCING_BUFFER_START(UserProps)
UNITY_DEFINE_INSTANCED_PROP(float, _EntityID)
UNITY_INSTANCING_BUFFER_END(UserProps)
// DOTS instancing usage macros
#define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_INSTANCED_PROP(UserProps, var)

(Adjusting as needed)

There will be about a dozen of those, one for each pass, each needs to be fixed up. Then don’t forget to reassign your materials to use the new shader instead of the shadergraph one.

Did I say it’s an ugly hack? :slight_smile:

1 Like

Ok, this issue is now officially repro’d as a perf regression bug, which was broken in the transition from 2021 to 2022, and still broken in 2022 and 2023:

Please kindly go to this link and give it a vote up:

(FWIW a big perf regression shouldn’t have a “vote” link on it, it should just get fixed, but I guess that’s the process?)

1 Like