Debugging D3D11 apps using D3D11On12
PIX is designed for use with Direct3D12 applications. That said, PIX can take advantage of Windows’ ability to convert Direct3D11 API calls into Direct3D12 calls, and thereby allow you to view your Direct3D11 application as if it were Direct3D12. It’s easy to use: just click this check box here before launching your game under GPU capture:
PIX will also work if you’ve used the d3dconfig (available as of Windows May 2020 Update) tool to turn on D3D11On12 yourself via a command like
d3dconfig apps –add MyGame.exe
d3dconfig device force-d3d11on12=true
Either of these mechanisms turns on the D3D11On12 layer to translate D3D11 calls to D3D12 calls. To help you visualize what’s going on, and the implications thereof that we’ll detail below, here’s a simplified view of the various software components in play when you capture your application in PIX using D3D11On12:
Now, this system does work, but it should be pointed out that D3D11 is not PIX’s focus, and hence some features currently work poorly or not at all. We make improvements to PIX all the time, though, and if there’s sufficient interest (as expressed via the mechanisms listed on our support page) we’ll spend more effort on D3D11On12 and improve some of these experiences.
PIX Markers and Extra Events Generated by D3D11On12
The D3D11On12 layer will translate calls to ID3DUserDefinedAnnotation into PIX markers for you, so you don’t need to change your D3D11 code at all. D3D11On12 will prepend “D3D11 Event:” in front of your own strings. Here’s an example of such markers in PIX:
In addition, D3D11On12 will generate its own PIX markers in order to denote the several D3D12 calls that emulate a single D3D11 call. Here are two examples of such markers, showing what happens for subsequent D3D11 calls to UpdateSubresource and DrawIndexed/DrawIndexedInstanced:
As you can see, D3D11On12 inserts many ResourceBarrier calls, which are of course unnecessary in D3D11 since their effects are implicit in D3D11’s design.
A SwapChain’s Present call can turn into different calls depending on the Windows version, and what else is happening in the process. A simple example is:
Here we see a ResourceBarrier being called for the swap chain backbuffer that is about to be presented, and a Signal on one of D3D11On12’s own internal fences. Note that “Present” was called by D3D11On12, but not IDXGISwapChain::Present. This Present is on ID3D12SharingContract, an interface used by DXGI to implement IDXGISwapChain::Present.
Limitation: No Shader Debug Data
D3D11On12 sits below D3D11 itself, and as you can see from the architectural layer diagram above, D3D11 treats D3D11On12 a bit like a display driver. Since display drivers don’t need much of the metadata that travels with a shader blob, D3D11 discards that metadata. Specifically, the PDB data that a shader would have, were it compiled with the /Zi flag, will not survive through to the D3D11On12 layer, and thus isn’t available to PIX.
This has several problematic effects on PIX. The most notable of these is that no shader source code is available, which means no source-level shader debugging. Shader reflection data is also lost, which means that PIX can’t automatically show the format of a constant buffer, or know precisely which resources were accessed by a shader.
Limitation: Shader Debugging
As mentioned above, shader debugging can’t show source code, but there is an additional limitation: D3D11On12 translates DXBC to DXIL. So, while you can debug at the DXIL level, you’ll find it very difficult to make the mental mapping from what’s going on in DXIL back to DXBC and from DXBC to HLSL.
Limitation: Inaccurate Warnings
PIX’s Warnings pane can show false positives under D3D11On12. This is because D3D11On12 has to batch D3D11 calls into D3D12 command lists, and since it has no knowledge of the application’s upcoming call sequence, can only make its best guess about when to flush these command lists. Consequently, you may see warnings such as “Small command lists executed” or “Command list performing no GPU work” that are no fault of your application.
Limitation: Timing Captures Show Almost No GPU Work
D3D11’s infrastructure is not instrumented for GPU timing captures, so the only GPU events you can expect to record are VSYNCs. Since timing captures don’t involve PIX’s API-capture machinery, PIX cannot turn on D3D11On12 itself, so D3D11 markers will only be translated if you have turned on D3D11On12 via d3dconfig.exe, as noted above.
Things that Do Work!
Let’s end this on a high note. You can expect the following major PIX features to work:
- Dr Pix
- Collecting timing data from a GPU capture:
- The Pipeline View:
…as well as all the other usual D3D12 state inspection features of PIX.
Let us know if you use PIX on D3D11 apps and have requests for features you’d like to see work better. We’re always listening.
Thanks for the article.
Question: The d3dconfig tool doesn’t seem to have the ‘force-d3d11on12’ flag. I have the Windows May 2020 v2004 update installed. When I run “d3dconfig device force-d3d11on12=true”, I get the message “Unknown variable: force-d3d11on12”. Is this feature/flag available on a different build? I was able to try out the Force D3D11On12 feature in PIX with no problems.
Excellent article. Thank you.
I’m curious to know why the D3D11On12 layer hooks at the DX11 DDI layer and not at the DX11 API layer (maybe called from the runtime). If it was at the API layer, it could get the shader source.