Search Unity

Question Index Buffer Append

Discussion in 'General Graphics' started by AlcogGMS, Nov 14, 2022.

  1. AlcogGMS

    AlcogGMS

    Joined:
    May 18, 2016
    Posts:
    24
    Hi, im trying to use :
    Code (CSharp):
    1. void DrawProceduralIndirect(GraphicsBuffer indexBuffer, Matrix4x4 mtr, Material mat, int pass, MeshTopology tplg, ComputeBuffer iargs, int offset);
    I created a GraphicsBuffer for indices:
    Code (CSharp):
    1. m_ResultIndicesBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Append, m_TotalIndices, UINT_SIZE);
    In the shader, I created a uint3 append buffer so that if the culling is successful, send the whole triangle at once, because I did not find another way to synchronize indices so that they do not get into the buffer in the wrong order if you use uint1.

    Code (CSharp):
    1. AppendStructuredBuffer<uint3> _ResultIndicesBuffer;// : register(u1);
    2. ...
    3. void Cull(uint index){
    4.     uint3 idx = _OriginalIndicesBuffer[index];
    5.     if(IsVisible(idx)){
    6.         //InterlockedAdd (iargsBuffer[0], 3U);
    7.         _ResultIndicesBuffer.Append(idx);
    8.     }
    9. }
    Before that, I separately added the number of indexes to the iargs buffer using an InterlockedAdd (+3). But now I'm trying to use CopyCounterValue, and there is a problem with this.
    Due to the fact that I use uint3, I add 3 indexes at once, but Counter only increases by 1 since I call it 1 time.

    So the questions are:

    1) Is it possible to independently increment Counter, or in some way set (maybe using register) so that it increments in increments of 3 ?

    2) For index buffer I use GraphicsBuffer.Target.Append which seems wrong to me (might be wrong) but when I try to create using GraphicsBuffer.Target.Append | GraphicsBuffer.Target.Index then I get the error :
    "Failed to create Compute Buffer UAV, HRESULT: 0x80070057".
    What can I do wrong here?
     
  2. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    675
    I don't have an answer but I have so many questions that it makes my head hurt ;)

    First question: Why couldn't you just do
    uint curIndex;
    InterlockedAdd(iargsBuffer[0], 3U, curIndex);
    _ResultIndicesBuffer[curIndex] = index0;
    _ResultIndicesBuffer[curIndex + 1] = index1;
    _ResultIndicesBuffer[curIndex + 2] = index2;

    Second question: What do you mean with independently increment counter? Append buffers don't have a public counter, as far as I am aware. You could call IncrementCounter manually on a RWStructuredBuffer, but that doesn't buy you anything.

    Third question: Why do you even need an append buffer with a counter? Can't you just call Append(int3)? What do you need the counter for in this case?

    Fourth question:
    Even if there was a counter, couldn't you just count triangles instead of indices?
     
  3. AlcogGMS

    AlcogGMS

    Joined:
    May 18, 2016
    Posts:
    24
    I use a translator, so forgive me if something is wrong)

    Initially, everything worked for me exactly as you wrote. After that, I optimized a little and switched to uint3 in order to load into the buffer only once and not calculate separately the indices for writing to the buffer, and it turned out like this:

    Code (CSharp):
    1. InterlockedAdd(iargsBuffer[0], 3U, curIndex);
    2. _ResultIndicesBuffer[curIndex / 3] = uint3(index0, index1, index2);
    BUT. I set out to do it right and as quickly as possible. And in this case, it confuses me that I have to write to 2 different buffers, which affects performance. I have tens of millions of triangles involved in culling, and this is noticeable. So I decided to use Append buffer. Each time I add an value there, it increments an invisible Counter( without call to IncrementCounter ) which I use later to pass it to the iargsBuffer, so all I'm left with is this:

    Code (CSharp):
    1. _ResultIndicesBuffer[curIndex / 3].Append(uint3(index0, index1, index2));
    That is, there is no need to constantly write to iargsBuffer. The only problem is that my Counter is 3 times behind, that is, if I add 90 indices, then there are only 30 in the counter due to the fact that I add them to one uint3.
    Perhaps there is a way using register to specify the counter step immediately by 3 instead of 1. Or something else ... it's just that the Append buffer does not have the IncrementCounter method


    4) I don't count the triangle because this buffer is used as an IndexBuffer to pass to DrawProceduralIndirect. To use them immediately as SV_VertexID in the vertex shader, so as not to read them from the slower StructuredBuffer. And this is my second problem, since the combination GraphicsBuffer.Target.Append | GraphicsBuffer.Target.Index for some reason gives an error in Unity.
    It's just that with GraphicsBuffer.Target.Append everything works fine. But it seems to me that this is not entirely correct ... Maybe I'm wrong+
     
    Last edited: Nov 15, 2022
  4. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    675
    Don't know why that is, sorry. Looks like a bug indeed.

    How do you do that? Do you do that on the CPU or GPU?
    Can't you just write

    uint numStructs;
    uint stride;
    _ResultIndicesBuffer.GetDimensions(numStructs, stride);
    iargsBuffer[0] = numStructs * 3;
     
  5. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    If you change GraphicsBuffer.Target.Append to GraphicsBuffer.Target.Counter,
    Then in your shader you can do `int index = yourBuffer.IncrementCounter();`

    Which gives you an integer to index into your buffer with:
    yourBuffer[index] = thing;

    Now you are free to call IncrementCounter 3 times in the shader, I think. If you want to.

    Constantly writing to iargsBuffer will be slow. It's much faster to use the hidden counter in the buffer. Because it's in a special GPU cache.
     
  6. AlcogGMS

    AlcogGMS

    Joined:
    May 18, 2016
    Posts:
    24
    i use this in c# seript to pass counter to iargsBuffer:

    Code (CSharp):
    1. commandBuffer.CopyCounterValue(m_IndicesBuffer, m_iargsSBuffer, 0);
    i can use the way you described, but it's not the most performant way, constant writing to iargsBuffer. This maybe can be done by running a second shader with a minimum of units just to copy the number of indexes into iargs. Perhaps it will be faster than writing it all the time in every thread.
     
  7. AlcogGMS

    AlcogGMS

    Joined:
    May 18, 2016
    Posts:
    24
    Unfortunately, it is probably impossible to achieve what I wanted with the help of the Append buffer. By doing Append(uint3) I won't be able to increment in it, and by doing Append(uint) I won't achieve synchronization, and the indexes may be out of order.

    I don't understand why they didn't add IncrementCounter for Append buffers. Or even better - Append (data, incrCount).

    This can be easily solved in other ways, but this approach could be the fastest
     
  8. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    I forgot - change your AppendStructuredBuffer to just StructuredBuffer in your shader too. Then my suggestions ought to work.

    EDIT: ah, except you won’t be guaranteed to get all 3 increment counters as one atomic op.you’d need to just increment once then copy the counter out and multiply by 3 in a second compute shader, then store that final value in the indirect args for the indirect dispatch. As you’ll need that shader to build the indirect args anyway, it seems like it would have no bad perf cost
     
    AlcogGMS likes this.
  9. AlcogGMS

    AlcogGMS

    Joined:
    May 18, 2016
    Posts:
    24
    Thanks Richard, everything works. The second shader solved the problem.
    Tell me if there are any advantages to using Target.Index | Target.Raw instead of Target.Append ? Is there a difference in terms of performance?



    Code (CSharp):
    1. void Cull(uint index){
    2.     uint3 idx = _OriginalIndicesBuffer[index];  
    3.     if(IsVisible(idx)){
    4.         _ResultIndicesBuffer.Append(idx);
    5.     }
    6. }
    7.  
    8. [numthreads(1, 1, 1)]
    9. void CopyCount (){  
    10.     uint num, stride;
    11.     _CopyCounterSource.GetDimensions(num, stride);
    12.     _CopyCounterDestination[0] = num;
    13. }
     
    Last edited: Nov 15, 2022
    c0d3_m0nk3y likes this.
  10. c0d3_m0nk3y

    c0d3_m0nk3y

    Joined:
    Oct 21, 2021
    Posts:
    675
    Interesting. Didn't know about this function.

    I didn't mean constant writing. I did mean with a second shader like above.
    Glad it's working. You are probably missing the "* 3" though.
     
    richardkettlewell likes this.
  11. richardkettlewell

    richardkettlewell

    Unity Technologies

    Joined:
    Sep 9, 2015
    Posts:
    2,285
    I'm not 100% sure sorry. I'm fairly sure that even if there is, it will be small enough that you don't need to worry about it though.