[D3D12] Mapping a Resource to an address

Started by
7 comments, last by Hodgman 8 years, 6 months ago

Right now I call ID3D12Resource::Map to get a location to copy memory to when updating a cbuffer. Reading Hodgman's reply to this post, he mentions:

When the high level code asks to bind a cbuffer, I memcpy it's contents (from my own malloc'ed RAM) into my D3D12 stack, and then give that stack pointer to d3d as the cbuffer address.

This is the part I'm confused about. How are you able to tell D3D12 where to look for the cbuffer data at? It seems like calling Map gives you a pointer to use, where the driver has allocated that memory.

Advertisement

If I'm not mistaken in the CBV (constant buffer view).

https://msdn.microsoft.com/en-us/library/windows/desktop/dn859358(v=vs.85).aspx#constant_buffer_view

-potential energy is easily made kinetic-

That does look like the job of the D3D12_CPU_DESCRIPTOR_HANDLE that you pass to CreateConstantBufferView. Is he really suggesting calling CreateConstantBufferView for each binding of a cbuffer? And plus, when I call that function, I pass it the result of ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart(), not a memory address...

If I'm not mistaken you can put a CBV directly in the Root Signature.

edit - https://msdn.microsoft.com/en-us/library/windows/desktop/dn899223(v=vs.85).aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/dn903911(v=vs.85).aspx

-potential energy is easily made kinetic-

Those are recommended to be used "only sparingly", and I don't think they let you specify the address of where the buffer gets mapped to on the CPU side anyway.


Those are recommended to be used "only sparingly"

Isn't that because there size, in relation to the smallness of the root signature?


and I don't think they let you specify the address of where the buffer gets mapped to on the CPU side anyway.



Quote


When the high level code asks to bind a cbuffer, I memcpy it's contents (from my own malloc'ed RAM) into my D3D12 stack, and then give that stack pointer to d3d as the cbuffer address.

According to your quote he puts the data from CPU malloc'd RAM onto his "D3D12 stack" and then gives that gpu memory pointer as the cbuffer address.

Or am I misunderstanding your point?

-potential energy is easily made kinetic-

I don't think when he says he's putting the malloc'd RAM into his D3D12 stack that he means he's using a cbuffer inside the root signature.


That does look like the job of the D3D12_CPU_DESCRIPTOR_HANDLE that you pass to CreateConstantBufferView.

BTW I wasn't referring to that I was referring to "cbvDesc.BufferLocation" which is the GPU virtual address of the resource in question for the view.


I don't think when he says he's putting the malloc'd RAM into his D3D12 stack that he means he's using a cbuffer inside the root signature.

I'm not saying the Cbuffer is in the root signature... I'm saying the Cbuffer descriptor could be put in the root signature.

Well hopefully Hodgman will grace us with his presence.

-potential energy is easily made kinetic-


How are you able to tell D3D12 where to look for the cbuffer data at? It seems like calling Map gives you a pointer to use, where the driver has allocated that memory.


That does look like the job of the D3D12_CPU_DESCRIPTOR_HANDLE that you pass to CreateConstantBufferView. Is he really suggesting calling CreateConstantBufferView for each binding of a cbuffer? And plus, when I call that function, I pass it the result of ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart(), not a memory address...
Yeah you put a memory address into a D3D12_CONSTANT_BUFFER_VIEW_DESC, and then D3D copies that into a descriptor for you. When you want to tell D3D to read constants from your memory address, you give it this descriptor. So, "giving a pointer to d3d" == "giving it a descriptor that contains this pointer".

You can try calling CreateConstantBufferView on every bind, it probably actually won't be too bad :) I pretty much do that on consoles, but you're in control of memory management there a lot more, so I can actually get away with doing this without causing any allocations to actually occur (just patching some data in-place...).

What you can do on PC to avoid these allocations is pre-create a whole bunch of descriptors (cbuffer-views) for each 256 byte block of your huge buffer. e.g.

* Create a big buffer (your 'constant stack'), large enough to hold every dynamic constant required in your frame, plus padding... Unfortunately D3D12 mandates that buffers be 256byte aligned.

* Get the base address of the buffer and in a loop that increments this address by 256 bytes, pre-create cbuffer-views/descriptors for each 256 byte block.

* Repeat that loop with different sizes if required (e.g. for 1KB buffers, or 4KB buffers).

* Now when the user needs to supply dynamic constants, write them into your giant buffer at the appropriate offset, and then fetch the pre-created view/desecriptor that corresponds with this offset.

* Give that descriptor to D3D as your cbuffer data.

Next frame, switch to using a different constant-stack buffer. The frame after that, use a fence to ensure the GPU has finished with the previous-previous frame, and then reuse the first constant-stack buffer again.

For non-dynamic cbuffers, don't bother with any of this and just pre-allocate their immutable data, and pre-create their descriptor.

This topic is closed to new replies.

Advertisement