Search Unity

Multiple CBUFFER in custom instanced shader

Discussion in 'Shaders' started by Cripple, Oct 31, 2016.

  1. Cripple

    Cripple

    Joined:
    Aug 16, 2012
    Posts:
    92
    Hi !

    I am trying to implement instancing in my existing shaders. Everything worked fine as long as was working on standalone ".shader" files.

    But as soon as I tried working on shaders that include custom ".cginc" files I ran into a simple problem :
    Is it possible to have multiple CBUFFERS ?
    I would like to be able to include different ".cginc" in my shader, each containing instanced properties, Is it possible ? If so, how ?

    There seems to be a way of naming different CBUFFERS as we have to give each one a name ("UNITY_INSTANCING_CBUFFER_START( name )"). Although, I don't see any way to access those CBUFFERS by name.

    Thanks.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    You should be able to use multiple instancing cbuffers just fine. By default Unity defines an instancing cbuffer for object to world / world to object matrices, so when you add your own there's already two. So unless there's something hardcoded in Unity it should be fine with multiples instancing cbuffers, there's certainly nothing from the HLSL / GLSL side of things that would prevent it.

    However each cbuffer definition's name does have to be unique, and there's not a way to dynamically append data values into a cbuffer after their first defined that I'm aware of, and it's not something you want to do as each named cbuffer definition has to be consistent across the entire project.

    ie: if you have one shader that has:

    CBUFFER_START(MyCBuffer)
    float4 _MyColor;
    CBUFFER_END


    and another that has:

    CBUFFER_START(MyCBuffer)
    float4 _MyColor;
    int _MyInt;
    CBUFFER_END


    One of those shaders might garbage data as the two cbuffer definitions conflict. Unity might even split out a bunch of errors if you try this (I haven't). Instead you should be doing:

    CBUFFER_START(MyColorCBuffer)
    float4 _MyColor;
    CBUFFER_END

    CBUFFER_START(MyColorAndIntCBuffer)
    float4 _MyColor;
    int _MyInt;
    CBUFFER_END

    And yes I'm using CBUFFER_START() in these examples, but the same applies to UNITY_INSTANCING_CBUFFER_START().
     
    caogtaa and Jason-Michael like this.
  3. Cripple

    Cripple

    Joined:
    Aug 16, 2012
    Posts:
    92
    Thanks for the explanation !

    How about :

    CBUFFER_START(MyColorCBuffer)
    float4 _MyColor;
    CBUFFER_END

    CBUFFER_START(MyIntCBuffer)
    int _MyInt;
    CBUFFER_END

    Can I access them both in the same vertex shader ?
    As there doesn't seem to be a way of accessing a property in a specific CBuffer (by name), I am assuming you shouldn't name two properties the same way even if in different CBuffers ?

    for example :

    CBUFFER_START(MainColorProperties)
    float4 _Color;
    CBUFFER_END

    CBUFFER_START(HighlightColorProperties)
    float4 _Color;
    CBUFFER_END

    Those two _Color properties would overlap, wouldn't they ?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,339
    Your first example would work.
    Your second example would not.

    As far as the shader functions are concerned the cbuffers don't exist. The properties defined inside a cbuffer are just like any other property, there's no functional or usability difference between a float4 _Color; by itself or inside of a CBUFFER so defining multiple _Color values would cause either an error or undefined behavior in the shader.

    It's probably important to understand what a cbuffer is for. It is a way to say how that data is passed from the CPU to the GPU. They're optimized for data that is going to change often (like every frame, or every few frames), and generally going to change at the same time. For example Unity uses a cbuffers to pass the camera matrices and ambient lighting data which is shared by shaders globally.

    For instancing they use a cbuffer because the objects could be moving, and they cull out instances that aren't on screen, so the data can change often. If these are values that are set on a material once, or only rarely changed, there's no reason to have them be cbuffers, and it might even be slower if they are.
     
    See101, MingZi_, Pr0x1d and 1 other person like this.
  5. Cripple

    Cripple

    Joined:
    Aug 16, 2012
    Posts:
    92
    Thanks for the detailed explanation !