Unordered Access Views in DirectX 11/12

Started by
1 comment, last by Riko Ophorst 7 years, 6 months ago

I'm building a small general-purpose renderer in DirectX 12 at the moment. I however don't have experience with UAVs in any way, so I'm kinda hoping someone can give me a good explanation of what it is and how I can best make use of a UAV.

DirectX 12 also introduces a so-called counter buffer, which I don't quite understand what it's purpose is and how it should be used. Like, what does it even do?

I'm right now also assuming that a UAV can only be used in things such as "StructuredBuffers" and "ByteAddressBuffers" but I don't quite understand what those are either.

Up to this point I always thought that data is data and that there isn't much complicated processes going on when you are trying to read data, however now with UAVs it seems that my entire idea behind data on the GPU is faulty.

Can anybody give me a run down on UAVs, what they are used for and how you should use them in shaders? Also, what are counter buffers?

Advertisement
I'm right now also assuming that a UAV can only be used in things such as "StructuredBuffers" and "ByteAddressBuffers" but I don't quite understand what those are either.

Nope, they're for SRV's that have been created from buffer resources (as opposed to the more common case of SRV's that have been created from texture resources).

These are simply for when you want a shader to be able to perform random-access reads from a buffer resource.

As an example, AMD GPU's do not actually have very much in the way of "input assembler" hardware any more. When you bind a vertex buffer to the IA stage, the driver is actually patching your vertex shader code to contain some Buffer/StructuredBuffer variables, and automagically binds SRV's for your vertex buffers to the VS stage. The magic (driver-generated) VS code can then read the vertex attributes out of the buffers as required.

If you ever need to read data out of a buffer in a shader, you can use Buffer HLSL variables too. Buffer is for simple types, like float4 / R32G32B32A32_FLOAT, while StructuredBuffer is for structs (like cbuffers). If you ever need to bind a massive array of data to a shader -- like a skeleton for vertex skinning, then buffers are probably a better choice than cbuffers.

Lastly ByteAddressBuffers simply let you read 32-bit values without any automatic format decoding -- the DIY buffer view.

UAV's are for RWStructuredBuffers and RWByteAddressBuffers, so best to understand the above use case first :)

UAV's are similar to SRV's, but in HLSL you use these RW* types, and bind them to u# registers instead of t# registers. The big difference is that you can also write to these resource views!

e.g. normally in a shader you sample texels from a texture -- but a UAV lets you write data into the texture, such as:

myTexture[x,y] = float4(1,0,0,0);

Also, what are counter buffers

In D3D11, a UAV can be created with a counter attached to it (it's handled internally by D3D), but in D3D12 it's a bit more manual to implement this, hence the extra type in D12 (you make two buffers - one for the UAV to read/write data from, and one for the counter)...

The counters are used by AppendBuffers and ConsumeBuffers. These are buffers that you can write data to / read data from like a stack. E.g. if a shader is spawning particles, it can push them into an append buffer. The counter keeps track of how many particles were pushed.

Later on, a particle rendering shader can use a consume buffer to pop items from that stack (and reduce the counter).

These are used in places where shaders should generate a variable amount of data.

I'm right now also assuming that a UAV can only be used in things such as "StructuredBuffers" and "ByteAddressBuffers" but I don't quite understand what those are either.

Nope, they're for SRV's that have been created from buffer resources (as opposed to the more common case of SRV's that have been created from texture resources).

These are simply for when you want a shader to be able to perform random-access reads from a buffer resource.

As an example, AMD GPU's do not actually have very much in the way of "input assembler" hardware any more. When you bind a vertex buffer to the IA stage, the driver is actually patching your vertex shader code to contain some Buffer/StructuredBuffer variables, and automagically binds SRV's for your vertex buffers to the VS stage. The magic (driver-generated) VS code can then read the vertex attributes out of the buffers as required.

If you ever need to read data out of a buffer in a shader, you can use Buffer HLSL variables too. Buffer is for simple types, like float4 / R32G32B32A32_FLOAT, while StructuredBuffer is for structs (like cbuffers). If you ever need to bind a massive array of data to a shader -- like a skeleton for vertex skinning, then buffers are probably a better choice than cbuffers.

Lastly ByteAddressBuffers simply let you read 32-bit values without any automatic format decoding -- the DIY buffer view.

UAV's are for RWStructuredBuffers and RWByteAddressBuffers, so best to understand the above use case first :)

UAV's are similar to SRV's, but in HLSL you use these RW* types, and bind them to u# registers instead of t# registers. The big difference is that you can also write to these resource views!

e.g. normally in a shader you sample texels from a texture -- but a UAV lets you write data into the texture, such as:

myTexture[x,y] = float4(1,0,0,0);

Also, what are counter buffers

In D3D11, a UAV can be created with a counter attached to it (it's handled internally by D3D), but in D3D12 it's a bit more manual to implement this, hence the extra type in D12 (you make two buffers - one for the UAV to read/write data from, and one for the counter)...

The counters are used by AppendBuffers and ConsumeBuffers. These are buffers that you can write data to / read data from like a stack. E.g. if a shader is spawning particles, it can push them into an append buffer. The counter keeps track of how many particles were pushed.

Later on, a particle rendering shader can use a consume buffer to pop items from that stack (and reduce the counter).

These are used in places where shaders should generate a variable amount of data.

Thanks a lot for the clarification! Kudos!

This topic is closed to new replies.

Advertisement