Set counter of append/consume buffer on GPU?

2.3k Views Asked by At

I have an application that uses two compute shaders. Shader #1 produces data x and stores it in A, bound as an Append Buffer. Shader #2 runs on all of x from A, bound as a consume buffer. I would like to extend this by adding Shader #3, which runs on all of x in A, producing y into a new buffer B - but I still want Shader #2 to run on x as before.

That is, I want to run an operation on all elements of an append/consume buffer, but without 'consuming' them.

If I understand correctly, consuming an element just decreases a hidden counter, so I should be able to consume a buffer many times by storing the count when full, and resetting it between the shader invocations that consume from it.

The problem is that my platform - Unity - has only one method for this (SetCounterValue()) which requires me to pass in the new counter value. That is, I need to get the counter value to CPU memory before re-setting it, which will force a synchronisation between the GPU/CPU and reduce performance.

Is it possible to set the counter value of a consume buffer entirely on the GPU?

If not, is it possible to iterate over a consume buffer without using consume?

(It has occured to me to just bind the buffer as a regular UAV resource and iterate that way, but the documentation is ambiguous as to whether this is supported. E.g. does "these resources do not use resource variables." refer to the resource itself, or the view?)

1

There are 1 best solutions below

0
On

The normal way to do this (from what I understand, and I'm definitely no DirectCompute expert!) is to have two buffers - your data buffer, created in Unity as ComputeBufferType.Default buffer and declared in your shader as RWStrucutedBuffer<YourStruct>, and your index buffer, created in Unity as ComputeBufferType.Append and declared in your shader as ConsumeStrucutredBuffer<uint> or AppendStructuredBuffer<uint>.

The index buffer is a list of index references into your main buffer, and specifies which elements are inactive.

You'd also add an int to your data struct called 'isAlive' or something.

Then you'd create three shaders:

  • Your simulation shader, where you do all your work on your main data, which doesn't change from normal, except you have something like if(isAlive < 1) return before you do the bulk of your work.
  • Your index setup shader, where you populate your index buffer with indices (so indexBuffer[932] = 932 etc. (index list is initialised as AppendStructuredBuffer<uint>)
  • Your emit shader, where you 'create' main elements by consuming them from the list of available indices and setting isAlive = true. (index list is initialised as ConsumeStrucutredBuffer<uint>.

You could also, in your simulation shader, declare your index buffer as an Append buffer and, if certain conditions are met, 'kill' your simulation index by setting isAlive = false and appending its index to the index buffer.