Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Bug 2022.1.5 HDRP shaders - GPU instancing broken?

Discussion in 'High Definition Render Pipeline' started by rzubek, Jun 21, 2022.

  1. rzubek

    rzubek

    Joined:
    Aug 21, 2014
    Posts:
    72
    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:

    Screenshot A 1.png

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

    Screenshot B 1.png
    Screenshot B 2.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:

    Code (CSharp):
    1.         // Injected Instanced Properties (must be included before UnityInstancing.hlsl)
    2.         #if defined(UNITY_HYBRID_V1_INSTANCING_ENABLED)
    3.         #define HYBRID_V1_CUSTOM_ADDITIONAL_MATERIAL_VARS \
    4.         UNITY_DEFINE_INSTANCED_PROP(float, _Emission_Multiplier)
    5.         #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, var)
    6.         #else
    7.         #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var
    8.         #endif
    9.  
    10.         // -- Graph Properties
    11.         CBUFFER_START(UnityPerMaterial)
    12.     ...
    13.         #ifdef UNITY_HYBRID_V1_INSTANCING_ENABLED
    14.         float _Emission_Multiplier_dummy;
    15.         #else
    16.         float _Emission_Multiplier;
    17.         #endif
    18.         CBUFFER_END
    19.  
    Meanwhile in 2022 it gets added to CBUFFER instead, which is clearly incorrect

    Code (CSharp):
    1.         // -- Graph Properties
    2.         CBUFFER_START(UnityPerMaterial)
    3.     ...
    4.         // Hybrid instanced properties
    5.         float _Emission_Multiplier;
    6.         CBUFFER_END
    7.         #if defined(UNITY_DOTS_INSTANCING_ENABLED)
    8.         // DOTS instancing definitions
    9.         UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
    10.             UNITY_DOTS_INSTANCED_PROP(float, _Emission_Multiplier)
    11.         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.
     
  2. rzubek

    rzubek

    Joined:
    Aug 21, 2014
    Posts:
    72
    Bump. Just wondering if anyone else is running into this?

    This seems like a pretty serious regression.
     
  3. WiseAX

    WiseAX

    Joined:
    Dec 28, 2016
    Posts:
    2
    @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.
     
    rzubek likes this.
  4. rzubek

    rzubek

    Joined:
    Aug 21, 2014
    Posts:
    72
    @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.
     
    Last edited: Jul 5, 2022
    WiseAX likes this.
  5. rzubek

    rzubek

    Joined:
    Aug 21, 2014
    Posts:
    72
    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:

    Code (CSharp):
    1. // Hybrid instanced properties
    2. float _EntityID;
    3. CBUFFER_END
    4. #if defined(UNITY_DOTS_INSTANCING_ENABLED)
    5. // DOTS instancing definitions
    6. UNITY_DOTS_INSTANCING_START(MaterialPropertyMetadata)
    7. UNITY_DOTS_INSTANCED_PROP(float, _EntityID)
    8. UNITY_DOTS_INSTANCING_END(MaterialPropertyMetadata)
    9. // DOTS instancing usage macros
    10. #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_DOTS_INSTANCED_PROP_WITH_DEFAULT(type, var)
    11. #else
    12. #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) var
    13. #endif
    14.  
    And replace with something like this:

    Code (CSharp):
    1. // Hybrid instanced properties
    2. //float _EntityID;
    3. CBUFFER_END
    4.  
    5. // HACK: FIXED INSTANCING
    6. UNITY_INSTANCING_BUFFER_START(UserProps)
    7. UNITY_DEFINE_INSTANCED_PROP(float, _EntityID)
    8. UNITY_INSTANCING_BUFFER_END(UserProps)
    9. // DOTS instancing usage macros
    10. #define UNITY_ACCESS_HYBRID_INSTANCED_PROP(var, type) UNITY_ACCESS_INSTANCED_PROP(UserProps, var)
    11.  
    (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? :)
     
    WiseAX likes this.
  6. rzubek

    rzubek

    Joined:
    Aug 21, 2014
    Posts:
    72
    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:
    https://issuetracker.unity3d.com/is...tancing-with-instanced-properties-to-not-work

    (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?)