Advertisement

Descriptors and heaps

Started by March 08, 2018 09:39 PM
7 comments, last by CortexDragon 6 years, 10 months ago

Hi,
I am moving from DX11 to DX12 and I started fighting with texture resources. I provide only an simplified example.

Let's say, I have this root parameters:


CD3DX12_DESCRIPTOR_RANGE diffuseTextureTable;
diffuseTextureTable.Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 16, 0, 0);

CD3DX12_ROOT_PARAMETER signatureParameters[3];
signatureParameters[0].InitAsConstantBufferView(0); // Per draw
signatureParameters[1].InitAsConstantBufferView(1); // Per frame
signatureParameters[2].InitAsDescriptorTable(1, &diffuseTextureTable, D3D12_SHADER_VISIBILITY_PIXEL); // 16

My shader has one per draw constant buffer, one per frame buffer and table of 16 SRVs.
Every model can have up to 16 diffuse textures.

Well, the question is, how can I set textures for a model before rendering ? I read that changing heaps is costly. That's the only thing I thought that's the way.
Do i have to really load all textures into memory in initial time ? What If i can want to load some textures after initial time ? Is it possible to copy new texture descriptors into heap cheaply ?

Thank you very much :-)

DirectX 11, C++

19 minutes ago, wh1sp3rik said:

Well, the question is, how can I set textures for a model before rendering ? I read that changing heaps is costly.

Changing heaps is costly, yes. So you likely want to allocate as many textures as possible in the same heap.

As long as all the textures are in the same heap, there's no reason to change heaps for each model. You can instead change just the descriptor table (you can think of a descriptor table as pointing to a specific sub-range within a heap), to point at the textures of the next object.

Or, depending how similar your objects are, you might define a single descriptor table that maps across all of your textures, and then use dynamic indexing in the shader to reference those belonging to the current model.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Advertisement

There is ID3D12Device::CopyDescriptorsSimple() method, which copies descriptors from one heap to another. Beware that this is a CPU operation, so it will not be synchronized with the GPU timeline. This means that by the time you call ExecuteCommandLists() you already need the whole gpu descriptor heap be populated with descriptors that the GPU will want to use and keep them there until the GPU finishes using them.

34 minutes ago, swiftcoder said:

Changing heaps is costly, yes. So you likely want to allocate as many textures as possible in the same heap.

As long as all the textures are in the same heap, there's no reason to change heaps for each model. You can instead change just the descriptor table (you can think of a descriptor table as pointing to a specific sub-range within a heap), to point at the textures of the next object.

Or, depending how similar your objects are, you might define a single descriptor table that maps across all of your textures, and then use dynamic indexing in the shader to reference those belonging to the current model.

So let's say, I can make a huge heap of 2000 SRV descriptors. If I load a texture after initial, can I just call "CreateShaderResourceView" on free handle ? How fast is that ?

Also it seems, this command will run on CPU timeline, can I use it even GPU is using that heap but not using the descriptor ?

Can I call "CreateShaderResourceView" on handle that already exists ? ( and GPU is not using that descriptor ) ?

Thanks !

 

 

 

DirectX 11, C++

36 minutes ago, wh1sp3rik said:

If I load a texture after initial, can I just call "CreateShaderResourceView" on free handle ?

Yes, provided you don't expose them to the shaders via a descriptor table before they are populated.

36 minutes ago, wh1sp3rik said:

Also it seems, this command will run on CPU timeline, can I use it even GPU is using that heap but not using the descriptor ?

As long as you don't write any descriptors the active shaders can currently see (i.e. is not referred to by any descriptor table in the root signature), it should be fine. 

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I make a non-shader-visible heap (not bound to the device / no switching cost) where I call CreateShaderResourceView once for each texture, pre-creating the descriptors required for each of my textures.

Then i also have a shader-visible heap (bound to the device), where I allocate descriptor-tables using a ring-buffer technique. I then populate the tables with CopyDescriptors (copying the pre-created ones into the tables that are required for each draw).

Advertisement

Thanks for answers,

so, when i load a model with its textures, I should make a heap for model textures, which is not visible to a shader.
When I am going to render my model, I have to copy my descriptors to the main heap, and actually "append" them after existing descriptors in the main heap. Then, i have to pass ,where my descriptors starts to the model shader.

if I hit the end of the main heap, I will start from begining and rewrite existing ones.

Am I correct ? :)

DirectX 11, C++

Instead of using a ring buffer approach for the descriptorheap, I use a linked list on the cpu that initially stores a link for each element number in my texture region of the descriptorheap (the link knows its element number)

When I want a descriptor record , I pop the first link from this list. The link knows its recordnumber in the descriptorheap.

When I want to release a texture I return its link to the list so its descriptor record is now free for someone else to grab.

It gets more complicated if you want to grab links or release links from different cpu threads as you have to use interlocked linked lists.

 

This topic is closed to new replies.

Advertisement