How to use a big constant buffer in DirectX 11.1?

Started by
3 comments, last by Matias Goldberg 7 years, 7 months ago

Among some DirectX 11.1 features, there is one feature about binding a big constant buffer and specify a range in this buffer for updating at each stage. But I cannot find any examples how to configure and use this feature, is there anyone knowing how to do it? Please help me.

Advertisement
The only thing that changes is how you bind it (with *SSetConstantBuffers1 instead of *SSetConstantBuffers). See https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649(v=vs.85).aspx

Just curious, in which scenarios or stages in the rendering pipeline would it be preferable to use a large CB over several smaller ones?

Just curious, in which scenarios or stages in the rendering pipeline would it be preferable to use a large CB over several smaller ones?

This technique (*SSetConstantBuffers1) doesn't have too much benefit outside of simplifying resource management for the driver (a potential reduction of CPU overhead). You can create one buffer resource instead of many (less things for the driver to track), and you can update/map that big buffer in one go instead of having to do many smaller update/map operations. This is especially useful if your engine is structured in such a way where you're able to update the constants for many objects at a single point in the frame (after updating the game logic for all of them, but before drawing any of them).

In the general case, you should generally use as few buffers per draw as possible, as every resource binding incurs CPU overhead... However, if you take that advice to the extreme, ever draw call would use a single cbuffer containing every constant/uniform variable that it requires, which creates a different problem: now the CPU is doing a lot of CBuffer updates -- one very large update per draw... So you also want to use as many buffers per draw as possible in order to reduce the number of cbuffer updates required. Yes, that's two opposite rules of thumb :lol:

This sweet spot in the middle of those two bits of advice generally means separating constants by update frequency -- so a draw might have a cbuffer for things that change once per frame (camera matrices, etc), things that change once per material (colours, scales, etc), and things that change once per mesh (world matrix, etc)...

The main difference is rather in the amount of maps you have to do.

In D3D11, if you have to draw 100 objects each with individual settings you have to options (let's suppose you only want to send a float4):

  1. Create one Const Buffer of 16 bytes. Map it 100 times with MAP_DISCARD.
  2. Create 64 Const Buffer of 16 bytes each. Map each once (still 100 times total). Probably use MAP_DISCARD.
  3. Create one large const buffer (i.e. 64kb); map it once and use baseInstance to index into an array. In shader you would do value[baseInstance]. This adds some GPU overhead (shader now needs to perform an indirection, baseInstance requires a vertex buffer dedicated to instancing because SV_InstanceID always starts from 0); but now you only map once.

Regarding options 1 vs 2; Fabian Giesen had its experience where option 1 beats 2.

D3D11.1 added a 4th option:

  • Create a very large buffer (virtually no upper limit); map it entirely once. When you're done with it; you can bind the particular region you've written to.

This combines the goodies of both worlds: No GPU overhead from option 3 and no CPU overhead from mapping 100 times. You only need to map once. Granted, you now need to bind the buffer 100 times. But in terms of driver overhead, mapping is very expensive and binding a sub region is cheap.

(note that in some cases option 3 may still beat option 4; GPUs are complex parallel machines. However you can now perform option 3 without being restricted to 64kb limit and can use SV_InstanceID now that you can specify where the const buffer starts while binding)

This topic is closed to new replies.

Advertisement