Search Unity

Inconsistent access to StructuredBuffer

Discussion in 'Shaders' started by carcasanchez, Mar 27, 2020.

  1. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Hello there,
    I am having trouble with a very silly operation. I am wroking with DrawMeshInstancedIndirect, and I need to access a StructuredBuffer that contains different colors for different mesh instances.

    Code (CSharp):
    1. float4 modifyColorByElementIndirect(float4 color, float id, uint instanceID) {
    2.  
    3.     uint index = instanceID / _NumJoints; //_NumJoints an uint, and it value is 15
    4.  
    5.     return colorsBuffer[index];
    6.  
    7.     };
    Overall works well, but something strange happens:
    upload_2020-3-27_11-20-26.png
    See that flicker effect in the waist piece? As far as I have found, It is mixing the correct color with the previous color in the buffer (that corresponds to the chest mesh of the previous human, from left to right).

    If I debug the Index variable as color, I get this:
    upload_2020-3-27_11-23-5.png
    Which leads me to think that the instanceID / _NumJoints is giving inconsistent results, probably due to precision problems.

    The result I need from that division is the floor-ed result, so just an int division with truncated decimals should do the work. For a reason I am not aware of, it doesn't.

    I have tried different combinations (casting both to float, only the result, etc), but none seems to do the job.

    (I have debuged the Color buffer creation, and seems to be correct, so the problem is in the buffer access)
     
  2. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Ok, I have isolated the problem: for a reason I cannot understand, instanceID (that comes from SV_InstanceID) gives unconsistent results.
    If I do:
    upload_2020-3-30_16-43-58.png

    To paint each instance with a color, the result is:
    upload_2020-3-30_16-45-10.png
    Thats a lot of flicker! Why it is InstanceId giving such weird results, if it is supposed to be an uint?
     
  3. Olmi

    Olmi

    Joined:
    Nov 29, 2012
    Posts:
    1,553
    Is this partially done in a compute shader? I'm don't know how you have implemented your system, but are you sure you don't have a race condition somewhere? Things getting written to same location by different threads or something like that?
     
  4. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Hello,
    No, I am not using Compute Buffers nor Multithreading here. I think the issue has to come from the way I am accessing instance id. Let me show you the (almost) complete shader:

    input structs:
    Code (CSharp):
    1.  
    2. struct VertexData
    3. {
    4.     float4 vertex    : POSITION;  // The vertex position in model space.
    5.     float3 normal    : NORMAL;    // The vertex normal in model space.
    6.     float4 tangent   : TANGENT;   // The tangent vector in Model Space (used for normal mapping).
    7.     float4 texcoord  : TEXCOORD0; // The first UV coordinate.
    8.     float4 texcoord1  : TEXCOORD1; //Needed for global ilumination
    9.     float4 texcoord2  : TEXCOORD2; //
    10.     uint instanceID : SV_InstanceID;
    11. };
    12.  
    13. struct Input
    14. {
    15.     float texIndex;
    16.     float2 textureCoords: TEXCOORD0;
    17.     uint instanceID;
    18.  };
    Vertex shader:
    Code (CSharp):
    1.  
    2. void vert(inout VertexData i, out Input o)
    3. {    
    4.     o.instanceID = i.instanceID;
    5.  
    6.      #ifdef SHADER_API_D3D11      
    7.      billboardVertex bv = computeBillboardVertexIndirect(i.vertex, i.texcoord, i.instanceID);
    8.      i.vertex.xyz = bv.modelPos;
    9.  
    10.  
    11.      o.textureCoords = bv.uv.xy;
    12.       o.texIndex = bv.uv.z;
    13.  
    14.      #endif
    15.  }
    Surface shader:
    Code (CSharp):
    1.  
    2. void surf(Input IN, inout SurfaceOutputStandard o)
    3. {
    4.  
    5.       float4 outColor = UNITY_SAMPLE_TEX2DARRAY(_Sampler0, float3(IN.textureCoords, IN.texIndex));
    6.       if (outColor.a < _Cutout) discard;
    7.      
    8. #ifdef SHADER_API_D3D11      
    9.        outColor = modifyColorByElementIndirect(outColor, outNormal.a * 64.0, IN.instanceID);
    10. #endif
    11.         o.Albedo = outColor;
    12. }
    The part that confuses me is that, in the Vertex shader's function computeBillboardVertexIndirect, I make the same operation than in modifyColorByElementIndirect (instanceID/numJoints, to know where in the buffer is the right data). The difference is that in the first function, it gives me no errors, and in the second function, it fails.

    Also, I have tried to make the operation in the vertex shader, right at the top, and passing the result to the surface as an uint, but it also fails. However, the body parts that flicker are different if I use this approach.
    Could be, if it is not that I am accessing wrongly to Instance ID, that the variable is not being properly sent to the surface shader?