Capture D3D11 apps with D3D11On12

PIX GPU Captures focus on Direct3D 12 work. To capture a D3D11 app, run the app through D3D11On12 so Windows translates D3D11 device work into D3D12 calls that PIX can record and replay. You then view your D3D11 app in PIX as if it were a D3D12 app.

Use this workflow when you need PIX GPU Capture inspection for a D3D11 renderer. The capture shows the D3D12 work produced by the D3D11On12 translation layer, so the event list and resource state transitions don't always map one-to-one to the original D3D11 calls.

D3D11 isn't the focus of PIX. Most D3D12 inspection features work on D3D11On12 captures, but some features work poorly or not at all. For details, see Limitations and Features that work.

How D3D11On12 capture works

When you capture through D3D11On12, these components sit between your app and the graphics driver:

  1. Your app calls the D3D11 API.
  2. The D3D11 runtime passes the work to D3D11On12. D3D11 treats D3D11On12 much like a display driver.
  3. D3D11On12 translates the D3D11 work into D3D12 calls.
  4. The PIX GPU capturer records those D3D12 calls.
  5. The D3D12 runtime and the graphics driver execute the work.

Because PIX sits below both D3D11 and D3D11On12, it sees only the D3D12 calls that D3D11On12 makes. This placement explains most of the differences between a D3D11On12 capture and a native D3D12 capture.

Capture from the PIX UI

Use the Force D3D11On12 option when PIX launches the app for GPU capture.

  1. On the PIX Welcome page, select Launch Process for the target machine.
  2. Select the Win32 executable or packaged app.
  3. Set Capture Type to GPU Capture.
  4. Make sure Force D3D11On12 is selected. This option is selected by default.
  5. Select Launch, and then take a GPU Capture as usual.

For the full launch flow, see Launch an application from PIX. For command-line launches, use the --force11on12 option described in pixtool.exe.

Turn on D3D11On12 outside a PIX launch

You can also turn on D3D11On12 yourself instead of having PIX do it at launch. PIX GPU Capture works with D3D11 apps when D3D11On12 is turned on this way. This route is also the only way to get D3D11 markers translated in a Timing Capture, because PIX doesn't turn on D3D11On12 itself for Timing Captures.

To turn on D3D11On12 with D3DConfig, add your app to the D3DConfig app list and then set the device option:

d3dconfig apps --add MyGame.exe
d3dconfig device force-d3d11on12=true

To turn on the same setting from PIX:

  1. In PIX, connect to the machine that runs your app.
  2. On the connection toolbar, select Connection Settings.
  3. On the DirectX Options tab, optionally select your app's executable to apply the setting to only that app.
  4. Under Device Options, turn on Force D3D11on12.
  5. Restart your app.

Understand the captured events

The event list in a D3D11On12 capture contains more than the D3D11 calls your app made.

Your PIX markers

Your ID3DUserDefinedAnnotation markers appear in PIX as PIX markers, so you don't need to change your D3D11 code to see them. In PIX, each marker name has the prefix D3D11 Event:, which D3D11On12 adds when it translates the marker. For example, a D3D11 annotation named Draw scene appears in PIX as D3D11 Event: Draw scene.

Markers and calls that D3D11On12 generates

You also see PIX markers that your app didn't create. D3D11On12 often emulates a single D3D11 call with several D3D12 calls, and it adds its own markers to group the D3D12 calls that belong to each D3D11 call. For example, a D3D11 UpdateSubresource call or a DrawIndexed or DrawIndexedInstanced call each appear as a marker that contains the D3D12 calls used to emulate it.

Expect to see many ResourceBarrier calls inside these markers. D3D11 handles resource transitions implicitly, so D3D11On12 adds the barriers that D3D12 requires. These barriers come from the translation layer, not from your app.

Present calls

Your app's IDXGISwapChain::Present call appears in PIX as a set of D3D12 calls. The calls you see depend on the Windows version and on what else is happening in the process. A simple case contains:

  • A ResourceBarrier call for the swap chain back buffer that's about to be presented.
  • A Signal call on one of the internal fences that D3D11On12 owns.
  • A Present call made by D3D11On12.

This Present isn't IDXGISwapChain::Present. It's ID3D12SharingContract::Present, an interface that DXGI uses to implement IDXGISwapChain::Present.

Capture from application code

Use PIXForceD3D11On12() when the application loads the GPU capturer itself instead of being launched by PIX. Call it after WinPixGpuCapturer.dll is loaded and before the app creates its D3D11 device.

#define USE_PIX
#include <Windows.h>
#include <pix3.h>

void EnablePixD3D11GpuCapture()
{
    HMODULE gpuCapturer = PIXLoadLatestWinPixGpuCapturerLibrary();
    if (gpuCapturer == nullptr)
    {
        return;
    }

    HRESULT forceResult = PIXForceD3D11On12();
    if (FAILED(forceResult))
    {
        return;
    }

    CreateD3D11DeviceAndSwapChain(); // Application-defined device creation.
}

If your app delay-loads D3D11.dll, load it before you call PIXForceD3D11On12(). The PIX hook path looks up D3D11CreateDevice and D3D11CreateDeviceAndSwapChain in the loaded D3D11.dll module. Devices created before the call aren't converted to D3D11On12.

For the rest of the programmatic capture setup, see Programmatic capture and PIXForceD3D11On12.

Requirements

  • Use a GPU Capture workflow. The PIX UI option and pixtool --force11on12 launch mode are GPU-capture settings, and pixtool rejects --force11on12 with --timing.
  • Load WinPixGpuCapturer.dll before PIXForceD3D11On12() when the app enables capture from code. PIXLoadLatestWinPixGpuCapturerLibrary() is the recommended loader.
  • Call PIXForceD3D11On12() before creating the D3D11 device that you want to capture.
  • Run on a device and Windows configuration where D3D11On12CreateDevice can create a D3D11On12 device over a D3D12 device. PIX creates a D3D12 device for the selected adapter and requests at least Direct3D feature level 11.0.
  • Build the pix3.h call path for a supported WinPixEventRuntime architecture with USE_PIX enabled. Without USE_PIX, PIXForceD3D11On12() compiles to a success-valued no-op stub.

Limitations

  • The capture shows translated D3D12 work, not your original D3D11 calls. PIX creates a D3D12 device for the adapter and passes it to D3D11On12CreateDevice, so every call you see in the capture comes from D3D11On12.
  • D3D11 devices that DXGI creates internally for hybrid-adapter presents aren't converted to D3D11On12. A capture that contains work from multiple D3D12 devices can fail to replay, so PIX leaves those DXGI devices on their original path.
  • PIXForceD3D11On12() returns a failure HRESULT if the GPU capturer export isn't available or PIX can't install the D3D11 creation hooks. Check the return value before you rely on the capture path.

No shader source or reflection data

In a D3D11On12 capture:

  • PIX can't show shader source code, so you can't debug shaders at the source level.
  • PIX doesn't have shader reflection data, so it can't automatically show the layout of a constant buffer or know precisely which resources a shader accessed.

This happens even if you compile your shaders with /Zi. D3D11 treats D3D11On12 like a display driver, and because display drivers don't need most of the metadata that travels with a shader blob, D3D11 discards the shader PDB data before it reaches D3D11On12 and PIX.

Shader debugging at the DXIL level only

You can still debug shaders, but only at the DXIL level, because D3D11On12 translates your DXBC shaders to DXIL. It's hard to map what happens in the DXIL back to the original DXBC, and from DXBC back to HLSL.

Inaccurate warnings

The Warnings view can report false positives for D3D11On12 captures, such as Small command lists executed or Command lists performing no GPU work, that your app didn't cause. D3D11On12 batches D3D11 calls into D3D12 command lists, and because it doesn't know what calls your app makes next, it can only guess when to flush those command lists. For more information about warnings, see Find issues with warnings.

Timing Captures show almost no GPU work

A Timing Capture of a D3D11 app shows almost no GPU work. The only GPU events you can expect it to record are VSYNCs, because the D3D11 infrastructure isn't instrumented for Timing Captures.

Force D3D11On12 is a GPU Capture setting, and PIX can't turn on D3D11On12 for a Timing Capture. Your D3D11 markers appear in a Timing Capture only if you turned on D3D11On12 yourself, as described in Turn on D3D11On12 outside a PIX launch.

To measure GPU work for a D3D11 app, take a GPU Capture through D3D11On12 and collect timing data from it.

Features that work

These major PIX features work with D3D11On12 captures: