Search code examples
c++windowsshaderdirectxdirectx-11

DirectX 11 - Why constant buffers D3D11_USAGE_DYNAMIC are update by the CPU and not the GPU?


So I'm trying to wrap my head around the D3D11 constant buffers, my current understanding is that constant buffers are used to store data that remains constant for an entire draw call but may change between draw calls, when creating a constant buffer, I do it like this:

D3D11_BUFFER_DESC constant_buffer_description;
constant_buffer_description.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
constant_buffer_description.Usage = D3D11_USAGE_DYNAMIC;    
                                                            
constant_buffer_description.CPUAccessFlags = 0u;            
constant_buffer_description.MiscFlags = 0u;                 
constant_buffer_description.ByteWidth = sizeof( const_buffer );
constant_buffer_description.StructureByteStride = 0u;       
D3D11_SUBRESOURCE_DATA constant_buffer_subresource_data = {};
constant_buffer_subresource_data.pSysMem = &const_buffer;

Later I found out that you need to set the CPUAccessFlags to D3D11_CPU_ACCESS_WRITE so that the CPU can write to this buffer.

Now if you look at the description of D3D11_USAGE_DYNAMIC it states that:

A resource that is accessible by both the GPU (read only) and the CPU (write only). A dynamic resource is a good choice for a resource that will be updated by the CPU at least once per frame

But here we're dealing with hardware accelerated stuff mostly, why is the resource updated by the CPU and read by the GPU? Wouldn't it be faster to read it and update it in the GPU since the GPU is dedicated do to that sort of work?

Like, why use a const buffer instead of structured buffer inside the shader itself?


Solution

  • my current understanding is that constant buffers are used to store data that remains constant for an entire draw call but may change between draw calls

    yes, that is correct, at the beginning of a draw call, the GPU will cache the entire content of a constant buffer to use in the shaders. The caching is the reason why it "remains constant". it's because the shaders are reading from that cache instead of the buffer memory itself. Any change to the buffer during a draw will not affect the cache that the shaders are reading from.

    why is the resource updated by the CPU and read by the GPU? Wouldn't it be faster to read it and update it in the GPU since the GPU is dedicated do to that sort of work?

    If I understood your question correctly, you are asking why the buffer's memory is mapped to the CPU (and CPU updates it with something like memcpy in C++), and not the other way around.

    It's because there is no API in D3D11 to allow the GPU to actively read data from the CPU RAM, it's always the CPU's job to pass data to the GPU.

    If you're REALLY looking for ways to allow GPU to directly read from the CPU RAM (or even from the hard drive), you should look for DirectStorage in DirectX 12 Ultimate.

    Like, why use a const buffer instead of structured buffer inside the shader itself?

    If I understood your question correctly, you are asking what's the difference between using a constant buffer and a structured buffer.

    As I said previously, a constant buffer is cached at the beginning of each draw call. According to my understanding, a shader will read the entire content of the buffer (onto the stack maybe) before running, so it enjoys the best reading speed of it while it's running. So it's best to use it for scenarios where every single shader instance uses most-if-not-all and overlapping-if-not-identical portions of data in this buffer, and that this buffer is not very big, so it doesn't overflow the stack. The camera buffer fits this scenario, it's used in every vertex's calculation, and possibly every pixel in the pixel shader too.

    Structured buffer on the other hand, according to my understanding, is not cached in advance, and will not be read by the shader before it runs, so it will not increase the stack size. It's best to use this for scenarios where each vertex/pixel will access different portions of data, and where the data may be very big. like a texture, or a bone matrices buffer. But that doesn't necessarily mean a structured buffer is "not constant", StructuredBuffer doesn't offer write access. If you are looking for "GPU write" into the structured buffer, you will need RWStructuredBuffer instead, where you bind an unordered access view (instead of a shader resource view) to it.