Few weeks ago, I decided to work with DirectX 11 to be able to render lots of objects in the same scene. I used the helper classes of DirectX Toolkit to help me at first and then I decided to wrap them in my own DirectX library. I included in this library a wrapper for DirectX initialization.
One of the best feature of DirectX 11 is multithreaded rendering, that is to say divide your scene into different parts that will be rendered “at the same time”. All my research was made in the context of my job in 3D R&D at Daneel.
One of the best feature of DirectX 11 is multithreaded rendering, that is to say divide your scene into different parts that will be rendered “at the same time”. All my research was made in the context of my job in 3D R&D at Daneel.
Multithreaded rendering, a new feature of DirectX 11
DirectX 11 is the first version of DirectX which supports full multithreading for objects creation and rendering. I will not talk about creation of objects but only multithreaded rendering in this article. The principle is quite simple. To draw objects on the screen, you should use a Direct3D device context. With other versions of Direct3D, you had just one type of device context, which directly draws vertices on the screen as soon as we tell it to do it: this is the immediate context. In DirectX 11, a new kind of device context exists : the deferred context. This device context was created only to answer to the problematic of multithreaded rendering. Deferred contexts are used to record draw actions to play back later. Deferred contexts can record actions simultaneously.
Source: http://www.gamasutra.com/view/feature/3759/sponsored_feature_introducing_.php?print=1
To be able to have a multithreaded rendering, you should really understand how work deferred contexts and immediate context.
To be able to have a multithreaded rendering, you should really understand how work deferred contexts and immediate context.
Environment setup for multithreading
First of all, I strongly recommend that you work with a Win32 project and not a Windows 8 application (or even DirectX11 application for Windows). Indeed, I began to work with a template of Visual Studio: DirectX11 application. I implemented multithreaded architecture for rendering, and the rendering was working but I couldn't see any change in the performance: not better, not worse, exactly the same. I realized that's because of the project setup. I suppose that the application is forcing few DirectX 11 settings because of the compatibility with other devices like tablets.
Secondly, you should check for driver support. Here is a how-to that explains it step by step.
Secondly, you should check for driver support. Here is a how-to that explains it step by step.
Rendering algorithm
Many algorithms are possible. However, I decided to use the technique recommended in the book Practical rendering and computation with Direct3D 11. For each thread, we create a corresponding deferred context and command list with ID3D11Device::CreateDeferredContext(). So 12 threads, 12 deferred contexts, 12 command lists for instance. I divided my objects to render into as many groups as there are threads. Each command list is recorded, that is to say, we make rendering actions (like drawing) not on the immediate context like we used to do but on a deferred context previously created. Finally, we call ID3D11DeviceContext::FinishCommandList() in order to "close" the command list. It basically says that we no longer have actions to record for this command list. Here is the tutorial from MSDN to record a command list.
After recording command lists, we have to execute them. This job is ONLY the immediate context's. When we want to execute command lists (for instance, at the end of your render function), we just have to call ID3D11DeviceContext::ExecuteCommandList() with the immediate context. That's it, we have a multithreaded rendering.
After recording command lists, we have to execute them. This job is ONLY the immediate context's. When we want to execute command lists (for instance, at the end of your render function), we just have to call ID3D11DeviceContext::ExecuteCommandList() with the immediate context. That's it, we have a multithreaded rendering.
Example
In order to check if my algorithm was working, I decided to do an application with a bunch of settings you can change: number of threads, number of objects, CPU burn, rendering mode, and so on. I read an excellent article about multithreaded rendering in which Alexandre MUTEL talks about DirectX 11 and make some benchmarks with SharpDX, which is a DirectX11 framework made in C#. His sample was pertinent so I decided to do the same but in C++.
My configuration was the next one: Windows 8, NVIDIA GeForce GTX950, 16Go RAM, up to 12 threads, Release x64. Here are the results I had:
My configuration was the next one: Windows 8, NVIDIA GeForce GTX950, 16Go RAM, up to 12 threads, Release x64. Here are the results I had:
Limitations/best practices
As we can see on the first chart above, performance of multithreaded rendering is not magical. It depends a lot on how it was implemented, but also about the number of objects to draw, etc.
Here are best practices that can help you have better performance:
Here are best practices that can help you have better performance:
- be careful about command lists size: not too small, not too big
- use multithreading only if you have lots of objects to draw or if you have CPU overhead
- find the good number of threads to use, too many threads can create a bottleneck and be a lot worse than with a single-threaded program
Binary
You can download the sample I developed here.
mt-dx11.zip |
References
While doing tests about multithreading, I did a lot of research. Lots of articles, papers and books helped me to find the right way to do it.
- Practical rendering and computation with Direct3D 11 by Jason Zink, Matt Pettineo & Jack Hoxley
- Direct3D11 multithreading micro-benchmark in C# with SharpDX by Alexandre MUTEL
- All documentation about multithreaded rendering on MSDN
- Introduction to Multithreaded rendering and the usage of Deferred Contexts in DirectX 11 by Rodrigo B. Pinheiro, Alexandre Valdetaro, Gustavo B. Nunes, Bruno Feijo & Alberto Raposo