Search Unity

Instance ID in shader?

Discussion in 'Shaders' started by rrh, Oct 26, 2017.

  1. rrh

    rrh

    Joined:
    Jul 12, 2012
    Posts:
    331
    In this thread someone says "use the instance id in your shaders to index into the arrays."

    Is there really a way to access the instance id as a integer value in the shader?

    On this page about GPU Instancing it refers to UNITY_INSTANCE_ID but if I try using that in my shader I get an error. Same for SV_InstanceID which it mentions.

    The UNITY_SETUP_INSTANCE_ID() function is working fine for me, but I don't know how to access the actual ID. I'm on 2017.2 so I'm not out of date.

    I guess my thought is, if I have some attributes I want to vary, I could have it changed based on the ID without making additional properties for the MaterialPropertyBlock.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    That page you linked to is the documentation for 5.4. The macros for instancing have changed since then. UNITY_INSTANCE_ID is now UNITY_VERTEX_INPUT_INSTANCE_ID.
    https://docs.unity3d.com/Manual/GPUInstancing.html

    If you want to access the actual id, it's stored in the local variable unity_InstanceID after UNITY_SETUP_INSTANCE_ID(?) is called.

    Also what all of the macros are and what they do are defined in the UnityInstancing.cginc file. You can look at that by downloading the built in shader source here:
    https://unity3d.com/get-unity/download/archive
     
    Thomas-Mountainborn likes this.
  3. rrh

    rrh

    Joined:
    Jul 12, 2012
    Posts:
    331
    Okay, UNITY_GET_INSTANCE_ID(v) does what I want. I overlooked it as first because it gives me an "undeclared identifier" error but if I ignore that and run it anyways, it does what I want. Thanks.
     
  4. Aaron-Meyers

    Aaron-Meyers

    Joined:
    Dec 8, 2009
    Posts:
    305
    I've got a shader I'm working on that I'm using with Graphics.DrawMeshInstanced() and it draws my instances just fine (I'm using UNITY_SETUP_INSTANCE_ID() at the top of my vertex shader), but when I try to access unity_InstanceID after that, I get an undeclared identifier. I thought it was supposed to be globally accessible... any idea what's happening here?
     
  5. Aaron-Meyers

    Aaron-Meyers

    Joined:
    Dec 8, 2009
    Posts:
    305
    alright, I wrapped the line using unityInstanceID in a #ifdef UNITY_INSTANCING_ENABLED and now it doesn't complain about an undeclared indentifier.

    Am I dense? Not sure why this should work because that is the only thing I changed and I didn't do anything that would have added a #define anywhere. Maybe I don't really understand preprocessor macros?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    If UNITY_INSTANCING_ENABLED isn't defined, a lot of macros and functions related to instancing don't do anything. Most of the time they're literally blank. Accessing the unity_InstanceID variable directly in that case will cause the error since it was never defined, as it is only defined when UNITY_INSTANCING_ENABLED is also defined.

    So what defines that define? It's part of the #pragma multi_compile_instancing mentioned in that link about instancing. If you're using a surface Shader, that pragma is added automatically. Technically I believe that multi compile enables one of a few other defines, and the UNITY_INSTANCING_ENABLED gets defined in one of the cginc files when any of the ones from the multi compile is enabled, but that's not important.
     
    SgtOkiDoki likes this.
  7. Aaron-Meyers

    Aaron-Meyers

    Joined:
    Dec 8, 2009
    Posts:
    305
    I'm still a little confused because the only difference was wrapping the line that uses unity_InstanceID in the #ifdef, but hey... it's working now so I'm just gonna roll with it.

    Can I assume that unity_InstanceID will begin at 0 and go up to however many instances I'm drawing in that frame?
     
  8. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    An #ifdef is a block of code that will be included or omitted from the shader entirely based on a define. That define can come from the shader code itself (#define PIZZA 1), or from the compiler (like which API is in use), or from Unity (like which version of unity is in use, or those #pragma multi_compile lines). When using #pragma multi_compile or shader_feature, Unity will actually compile multiple different versions of the shader (variants) that it will switch between depending on when needed. This can mean you have several million possible versions of a "single" shader.

    So, if you have a bit of code like:

    // near the start of the CGPROGRAM block
    #pragma multi_compile _ PIZZA
    #ifdef PIZZA
    float _pizzaRadius;
    #endif

    // in a surf, vert, of frag function
    float radius = _pizzaRadius;


    You'll get an error as _pizzaRadius only exists in the variant that PIZZA is defined for, but Unity will compile one version with out PIZZA defined and one with. Instead you need to do this:

    // near the start of the CGPROGRAM block
    #pragma multi_compile _ PIZZA
    #ifdef PIZZA
    float _pizzaRadius;
    #endif

    // in a surf, vert, of frag function
    #ifdef PIZZA
    float radius = _pizzaRadius;
    #else
    float radius = 0;
    #endif


    In that case it'll work because it's not trying to access the _pizzaRadius when it doesn't exist.

    Roughly, yes. Unity has a unity_BaseInstanceID it adds to the unity_InstanceID that I don't know the purpose of, but I assume there will always be an instance of "0" rendered at some point in the frame.
     
    Aaron-Meyers likes this.
  9. Aaron-Meyers

    Aaron-Meyers

    Joined:
    Dec 8, 2009
    Posts:
    305
    Thanks for the detailed explanation!
     
  10. zeroyao

    zeroyao

    Unity Technologies

    Joined:
    Mar 28, 2013
    Posts:
    169
    Mostly it's like what @bgolus said. Thank you very much for that!

    unity_BaseInstanceID is used where a single instancing array can be shared between multiple draws. This only happens when you use individual prefabs in the scene and let unity renderloop automatically do instancing for you. In that case, imagine you have 100 cubes and 100 spheres in the scene sharing the same material. They will be batched together and unity figures out it is more efficient to upload the instancing array (via a constant buffer update) only once with 200 instances of data, and issue two draws out of it - the first will draw with baseInstanceID being 0, and the second will draw with baseInstanceID being 100. But any way, the instanceID is mostly reset for each draw, so it won't accumulate over a frame or whatever time period. It is only unique for unity to address the instancing array.
     
    VictorKs likes this.
  11. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    242
    Sorry for necro-posting but is this why the instancing CBuffer is dynamicIndexed? And is that bad for performance versus immediate indexed? I guess if we render our Meshes from script with DrawMeshInstanced we can get rid of the baseInstanceID right? Or is it not worth the hasle?