(3D) D3D11on12 and DirectX 12

Published on April 15, 2018.

I’ve been upgrading the Tabby Engine to catch up on the latest technologies. My goal is eventually to complete a rendering pipeline that uses the new DirectX RayTracing technology, recently at GDC 2018 by Microsoft.

D3D11on12 - Shortcomings

Following the previous post, I attempted to stick with the D3D11on12 layer and start to integrate the simplest DirectX RayTracing sample into the Tabby Engine. Creating new structures for the DirectX RayTracing support proved simple. These structures, though, require quite a few D3D12 sub-structures: Vertex buffers, Index buffers, Shader Resource Views.

I decided to attempt to create a DirectX 12 render target, wrap using the D3D11on12 layer into a DirectX 11 render target. Starting with the render target is a good choice, because it is a lynch-pin in my deferred shading rendering system and cannot be simply duplicated. An example of duplication: to have both DirectX 11 and DirectX 12 Vertex Buffers, I could simply upload the same data twice. That’s not efficient, but it is guaranteed to work. The render target is where the DirectX 11 and DirectX 12 worlds must combine.

Using the Microsoft samples, I was able to create a render-target Texture object, placing it on the DEFAULT heap. It was straight-forward to create the D3D11on12 wrapper for this render-target. Unfortunately, when I tried to create the DirectX 11 ShaderResourceView for this render target, it told me that some flags were missing. I researched this problem and found no solution. The MSDN page on D3D11on12 also hints that they don’t offer a full shader interface.

Sometimes, prototypes fail and it is time to go back to the drawing board. Upgrading the engine to D3D12 sounds like a large but interesting undertaking!

DirectX 12 - First Impressions

Two days later, I upgraded all the Direct3D structures to use the DirectX 12 driver instead of the DirectX 11 driver. With the help of the awesome DirectX Debug Layer, I managed to find and fix all the issues. I’ve now gotten back to generating the same screenshots as before, using the Tabby Engine Visualizer.

From my readings of previous year’s GDC presentations, I knew what to expect. Still, I was pleasantly surprised by the ease of the upgrade. My main issue came with the Descriptor Heaps. Today, I got them working, but I’m still confused about why these Descriptor Heaps are useful and needed. Why can I not create multiple Descriptor Heaps containing constant-buffer-views and attach them to one Graphics Pipeline State Object?

DirectX 12 - Performance

From my previous post, on my development laptop, the Visualizer reported an average of 480 FPS over 1000 frames with the D3D11 runtime. After my savage upgrade to the D3D11 runtime, on the same scene and with the same resolution, the Visualizer reports an average of 47 FPS over 1000 frames. I knew that I was missing many of the performance benefits of D3D12, but I did not expect the difference to be so large.

My first idea for improving the performance is to re-use Signature objects and Graphics Pipeline State Objects. In order to re-use those: I need to be able to identify them uniquely. At CMLabs, we used a lot of hash-values to uniquely identify rendering structures. There is a lot of data to hash, though: the shader code, the signature root parameters, the input assembly layouts and such…

  • Adding a cache for the signatures provided no performance improvement. I does confirm that my test scene uses only four signatures, which is cool.
  • Adding a cache for the graphics pipeline state objects provided a nice performance boost from 47 FPS over 1000 frames to 85 FPS over 1000 frames. My test scene uses 12 graphics pipeline state objects: that’s a few more than I wish.
  • Segmenting the global command list into a bunch of subsequent command lists means less stalling: it boosted performance to 128 FPS over 1000 frames.

This was the last optimization that I can do targeting only the D3D12 code. Every other optimizations that I can conceive (based on the profiler results), requires that I change higher-level code. Changing the higher-level code would benefit the original D3D11 as well, so that becomes an unfair comparison. I think this proves that upgrading blindly to D3D12 will work, but not give decent performance unless the engine is ready for its concepts. For my case, 128 FPS is fine to continue with the ray-tracing tests…