8
$\begingroup$

With directX12 they introduced heap descriptors. A way for us to describe the table for resources we wanted to send to the shaders. I'm admittedly very new at computer graphics and only tinkered a bit in directX11. I have not toyed with instancing or any more complicated things right now so I have a object for every mesh that has these defintions.

Microsoft::WRL::ComPtr<ID3D12Resource> mVertexBuffer;
D3D12_VERTEX_BUFFER_VIEW mVertexBufferView;

// Index Buffer
Microsoft::WRL::ComPtr<ID3D12Resource> mIndexBuffer;
D3D12_INDEX_BUFFER_VIEW mIndexBufferView;

// World View Projection Constant Buffer
Microsoft::WRL::ComPtr<ID3D12Resource> mWVPConstantBuffer;
WVPData mWVPData;      
UINT8* mMappedWVPBuffer;

// Directional Light Constant Buffer
Microsoft::WRL::ComPtr<ID3D12Resource> mDirLightConstantBuffer;
DirLightData mDirLightData;
UINT8* mMappedDirLightBuffer;

// CBVHeap
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> mCbvHeap;
UINT mCbvDescriptorSize;

A vertex buffer, index buffer, the cbvHeap, and two constant buffers. One for the transformation matrices and one for the directional light data.

I wasn't really sure what the cbvHeap (constant buffer view heap) was really doing. I just knew how to use it to my content to the screen. So I experimented. I took the cbvHeap and mCbvDescriptorSize out of the mesh object and put it in the scene object (where the array of meshes were contained) and then used the same cbvheap across all my meshes. This did not work as it gave one consistent color across the whole mesh. (ie: The same constant buffer was used for every mesh giving them the same diffuse and ambient light data.) What exactly is the descriptor heap doing that caused this? And are my definitions right per mesh?

$\endgroup$

1 Answer 1

10
$\begingroup$

In DX12, a descriptor is a small record, basically a pointer, that tells the GPU where to find some data such as a constant buffer. Since each object is going to have its own constant buffer data with its own particular transforms, lighting/material properties, etc., each object also has to end up with a separate set of descriptors to point to its individual data.

There are a couple of ways to set up descriptors in DX12. First, you can add commands in a command list that update descriptors directly in the root table. For example, you can use SetGraphicsRootConstantBufferView() to set up descriptors for an object's constant buffers, then draw the object. Repeat for multiple objects. These descriptors will be saved in the command list, and will get applied in-order as the command list is executed on the GPU.

The other way to do it is use descriptor heaps. A descriptor heap is basically a buffer that stores an array of descriptors. With this approach, the command list doesn't save descriptors for you; you have to manage them yourself. If you overwrite any descriptors before the GPU consumes them, then you'll get wrong results. This sounds like what's happening in your program: you're likely overwriting the same descriptor in the heap every time you issue an object's draw commands on the CPU, so by the time the GPU executes the command list, only the last descriptor written is still there, and that descriptor gets applied to all of the objects.

To fix this, you have to allocate enough space in the descriptor heap to store all the descriptors you're going to use for the frame. Write all the objects' descriptors to the heap. Then when drawing each object, point the shader at the that object's data using SetGraphicsRootDescriptorTable() with an offset handle that points to the spot in the heap where that object's descriptors live.

As long as you're still just trying to learn and get a simple app running, I would probably stick with the root table approach as much as possible. For a more serious high-performance app, there are probably optimizations you can do with descriptor heaps, such as keeping all the constants and textures for a given material together so you can reuse them across many objects and bind them all in one call. You'd probably also want to avoid having a constant buffer and descriptor heap per object, but rather aggregate many objects' data into a few large buffers; this will be more efficient when you have lots of objects.

It's also worth noting that a serious 3D app will want to have multiple frames' command lists in flight at the same time, to obtain CPU/GPU parallelism, and this will require constant buffers and descriptor heaps to be lengthened to store multiple frames' worth of data. Again, this is something you can gloss over for learning/bringup purposes by just building a single command list at a time, submitting it, and waiting for it to finish before going on to the next frame.

$\endgroup$
2
  • $\begingroup$ The problem with either method (set constant buffer GPU address with SetGraphicsRootConstantBufferView() or using secondary lookups to the descriptors in the root table) is that if there are not enough GPU addresses available to render all objects then the application developer must implement a strategy to re-use constant buffer memory. The application may have to close and submit the current command list (Close() then Reset()) which means you have to duplicate the state (PSO and other state) into a new command list. How should an application use constant buffer memory in a ring buffer strateg $\endgroup$
    – user5241
    Commented Oct 6, 2016 at 0:59
  • $\begingroup$ Looks like SE cut off the post in the conversion to comment. It continues with "...y? In the past drivers would notice no-overwrite was set or rename the memory if the previous memory was busy. How do you implement these strategies in DX12?" $\endgroup$ Commented Oct 6, 2016 at 8:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.