Jump to content
  • Advertisement
Sign in to follow this  
Funkymunky

DX12 Multi-threaded deferred setup

This topic is 901 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

My current setup is a forward renderer, I build a command list to make render calls and then copy upload-heap data to default-heap data (mostly MVPs and such).  Then I switch to a different "frame" and do the same thing while the first set of calls get processed.  I have a fence at the beginning of each frame to make sure that all the data has been copied for the new frame before I begin making new draw calls.  I rarely have to wait on it because of the dual-frame setup.

 

I started thinking about how to do things for a deferred render.  It's going to need to wait for the G Buffer textures to be generated each frame.  Even if I spread it out to 3 frames, I can't think of a setup where I don't end up waiting for either the default-heap copy of MVP data or the G-Buffer texture data.  It seems like that's going to be inherent in a setup where I need to render a texture as an input to another texture, but I was wondering if anyone had any advice on how to maximize the GPU usage for a deferred renderer with DirectX 12.

Share this post


Link to post
Share on other sites
Advertisement

It's going to need to wait for the G Buffer textures to be generated each frame.  Even if I spread it out to 3 frames, I can't think of a setup where I don't end up waiting for either the default-heap copy of MVP data or the G-Buffer texture data.  It seems like that's going to be inherent in a setup where I need to render a texture as an input to another texture

There's no need to go from 2 frames to 3... You need two frames to cover up the latency of CPU->GPU communication. Generating a texture is GPU->GPU communication, and if it's done with a single command queue, then those commands are all done in serial so there's no need for synchronization, so there's no need for fences or extra buffering.

All you need is to issue a resource barrier to transition the texture from a render-target to a shader-resource.

Share this post


Link to post
Share on other sites

For what reason are you uploading constants to a DEFAULT heap rather than reading them directly from the UPLOAD heap? I assume by "MVP" (ModelViewProjection) you're referring to your dynamic constant data that is regenerated every frame?

Share this post


Link to post
Share on other sites

I was under the impression that was the right way to do things.  Make draw calls, then copy in the new data to a Map'd region in an Upload heap, and then call CopyBufferRegion to move it to a Default heap.

Share this post


Link to post
Share on other sites

For "read once" (ie, not read again on the next frame) dynamic data such as constants it's not worth copying it over to the GPU. Just leave the data in the UPLOAD heap and read it from there.

Share this post


Link to post
Share on other sites

I thought it was slower if the GPU pulls data from an Upload Heap.  With my current scheme I never actually end up waiting on a fence because I render a different frame and upload to a different Heap location while the first set of operations processes.  So wouldn't I just be slowing things down if I didn't copy stuff into a Default Heap?

Edited by Funkymunky

Share this post


Link to post
Share on other sites

If it were slow for the shader to read from it, it would be slow to copy from it ;)

 

However, you must use different pieces of memory for each potentially concurrent frame.  Otherwise, you might end up overwriting the memory halfway through.  You will always need a fence at some point just to ensure that this can never happen, limiting yourself to N concurrent frames (2 = double buffering, 3 = triple buffering, etc.).

 

And, of course, the part of system memory that's fast for the GPU to access is small, and will have to be shared across N frames, so for complex scenes you will have to upload a certain amount of the dynamic data.  That being said, careful use of the DMA queue will allow you to mitigate the latency involved.

Edited by Boreal Games

Share this post


Link to post
Share on other sites

All the reads the GPU performs from the UPLOAD heap are cached reads, so it's not as if every vertex or pixel that reads a particular constant is pulling it across the PCI-E bus. The first thread to do so will miss the cache, fetch it across the bus and every future read in the draw will likely just read it directly from the cache.

 

As Boreal Games suggests, you can still continue your (good) approach of rendering the next frame on the CPU while the GPU renders the previous. You simply need to put the next frame's constants in an area of your UPLOAD heap (or different heap entirely) not used by the previous frame.

Share this post


Link to post
Share on other sites

For "read once" (ie, not read again on the next frame) dynamic data such as constants it's not worth copying it over to the GPU. Just leave the data in the UPLOAD heap and read it from there.

Actually on GCN performing a Copy via a Copy queue allows GCN to start copying the data from bus to the GPU using its DMA engines while it does other work (like rendering the current frame); which might result in higher overall performance (particularly if bound by the bus or latency is an issue).

 

However it hurts all other GPUs which don't have a DMA Engine (particularly Intel integrated GPUs and AMD APUs which don't need this transfer at all and takes away precious bandwidth)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!