Programmatic capture

Use the pix3.h APIs from the WinPixEventRuntime when your application needs to start, stop, or configure captures from code. The WinPixEventRuntime API reference covers the marker and event APIs in the same header; this article focuses on capture control.

Prepare your project

Before you call the capture APIs, complete these setup steps.

  1. Install PIX on the target PC.
  2. Add the WinPixEventRuntime to your application.
  3. Build for x64 or ARM64.
  4. Define USE_PIX, or use a debug or profile build configuration that lets pix3.h define USE_PIX for you.
  5. Include Windows.h, and then include pix3.h.
#include <Windows.h>
#include <pix3.h>

When USE_PIX isn't enabled for a supported architecture, most capture APIs compile to S_OK no-op stubs. The loader helpers return nullptr, PIXIsAttachedForGpuCapture() returns false, PIXOpenCaptureInUI() returns 0, and PIXGetCaptureState() returns 0. Some unsupported Windows API partitions use E_NOTIMPL stubs instead.

Load only the capturer DLL for the capture type that you use. If both WinPixGpuCapturer.dll and WinPixTimingCapturer.dll are loaded, PIXEndCapture() can't know which capture type you started. It tries to end GPU capture first and only tries Timing capture for specific GPU failures.

Capture API summary

API Use it for Notes
PIXLoadLatestWinPixGpuCapturerLibrary() GPU capture setup. Loads WinPixGpuCapturer.dll from the latest installed PIX version when USE_PIX is enabled.
PIXLoadLatestWinPixTimingCapturerLibrary() Timing capture setup. Loads WinPixTimingCapturer.dll from the latest installed PIX version when USE_PIX is enabled.
PIXBeginCapture() and PIXBeginCapture2() Start an immediate GPU or Timing capture. Pass exactly one of PIX_CAPTURE_GPU or PIX_CAPTURE_TIMING on Windows. Other PIX_CAPTURE_* values return E_NOTIMPL.
PIXEndCapture() Stop a capture started by PIXBeginCapture(). discard is ignored for GPU captures. Timing captures save when discard is FALSE and discard data when discard is TRUE.
PIXGpuCaptureNextFrames() Queue a present-to-present GPU capture. This is equivalent to the GPU capture button or hotkey path. Pass a non-null file name.
PIXSetTargetWindow() Choose which window starts and stops present-to-present GPU captures. Use it for multi-window tools and editors.
PIXSetHUDOptions() Control the GPU capture HUD. Use PIX_HUD_SHOW_ON_ALL_WINDOWS, PIX_HUD_SHOW_ON_TARGET_WINDOW_ONLY, or PIX_HUD_SHOW_ON_NO_WINDOWS.
PIXForceD3D11On12() Capture D3D11 through D3D11On12. Call it before creating the D3D11 device.
PIXIsAttachedForGpuCapture() Check whether the process is attached to the PIX UI for GPU capture. Returns false if the GPU capturer DLL isn't loaded or doesn't match the header.
PIXOpenCaptureInUI() Open a capture file through Windows file association. Returns the ShellExecuteW result, not an HRESULT.
PIXGetCaptureState() Compatibility flag query. The current Windows WinPixEventRuntime implementation always returns 0, so don't use it to detect a live Windows capture.

For marker, event, color, counter, and memory-event APIs from the same runtime, see the WinPixEventRuntime API reference.

Load the capturer DLLs

GPU capture APIs require WinPixGpuCapturer.dll. Load it before creating D3D12 devices when the application isn't launched through PIX or pixtool.

void EnableGpuCapture()
{
    HMODULE gpuCapturer = PIXLoadLatestWinPixGpuCapturerLibrary();
    if (gpuCapturer == nullptr)
    {
        // Inspect GetLastError() and continue without programmatic GPU capture.
    }
}

Timing capture APIs require WinPixTimingCapturer.dll. Timing capture collection also requires performance logging permissions. Run the process as administrator, or use an account in the local Performance Log Users group and reboot.

void EnableTimingCapture()
{
    HMODULE timingCapturer = PIXLoadLatestWinPixTimingCapturerLibrary();
    if (timingCapturer == nullptr)
    {
        // Inspect GetLastError() and continue without programmatic Timing capture.
    }
}

The loader helpers search installed PIX versions under Program Files\Microsoft PIX. On success, they return a module handle with LoadLibrary-style reference-count semantics. Call FreeLibrary() if you need to unload the DLL.

Take a GPU capture immediately

Use PIXBeginCapture(PIX_CAPTURE_GPU, ...) and PIXEndCapture(FALSE) when your code knows the exact region to capture.

void CaptureStartupFrame()
{
    PIXCaptureParameters captureParameters = {};
    captureParameters.GpuCaptureParameters.FileName = L"C:\\captures\\startup.wpix";

    HRESULT beginCaptureResult = PIXBeginCapture(PIX_CAPTURE_GPU, &captureParameters);
    if (SUCCEEDED(beginCaptureResult))
    {
        RenderStartupFrame(); // Application-defined rendering work.
        HRESULT endCaptureResult = PIXEndCapture(FALSE);
        (void)endCaptureResult;
    }
}

Set GpuCaptureParameters.FileName to a .wpix path. When the application is attached to the PIX UI, PIX can provide the output file. When the application isn't attached to the PIX UI, pass a non-empty file name.

PIXEndCapture(TRUE) doesn't discard a GPU capture on Windows. The GPU capture path ignores discard.

Queue the next GPU frames

Use PIXGpuCaptureNextFrames() when you want the same present-to-present behavior as the GPU capture button or hotkeys. Pass a non-null file name. Use a non-empty path unless the application is attached to the PIX UI, and check the returned HRESULT.

HRESULT QueueNextGpuFrames()
{
    return PIXGpuCaptureNextFrames(L"C:\\captures\\next-frames.wpix", 3);
}

Use PIXSetTargetWindow() before queueing a capture when a multi-window application has one window that should control capture start and stop.

HRESULT QueueEditorViewportCapture(HWND renderWindow)
{
    HRESULT targetWindowResult = PIXSetTargetWindow(renderWindow);
    if (FAILED(targetWindowResult))
    {
        return targetWindowResult;
    }

    return PIXGpuCaptureNextFrames(L"C:\\captures\\editor-viewport.wpix", 1);
}

Configure the GPU capture HUD

Use PIXSetHUDOptions() to control where the GPU capture HUD appears.

PIXSetHUDOptions(PIX_HUD_SHOW_ON_TARGET_WINDOW_ONLY);

Valid values are:

  • PIX_HUD_SHOW_ON_ALL_WINDOWS.
  • PIX_HUD_SHOW_ON_TARGET_WINDOW_ONLY.
  • PIX_HUD_SHOW_ON_NO_WINDOWS.

Use PIXIsAttachedForGpuCapture() when your code should only show capture UI after PIX has attached for GPU capture.

bool ShouldShowCaptureControls()
{
    return PIXIsAttachedForGpuCapture();
}

Capture D3D11 through D3D11On12

Call PIXForceD3D11On12() before creating the D3D11 device if you need PIX to capture a D3D11 application through the D3D11On12 mapping layer. For more information, see Capture D3D11 apps.

void CreateCapturableD3D11Device()
{
    PIXForceD3D11On12();
    CreateD3D11Device(); // Application-defined device creation.
}

Take a Timing capture

Use PIXBeginCapture(PIX_CAPTURE_TIMING, ...) after loading WinPixTimingCapturer.dll.

void CaptureLevelLoad()
{
    PIXCaptureParameters captureParameters = {};
    captureParameters.TimingCaptureParameters.FileName = L"C:\\captures\\loading.wpix";
    captureParameters.TimingCaptureParameters.CaptureGpuTiming = TRUE;
    captureParameters.TimingCaptureParameters.CaptureCallstacks = TRUE;
    captureParameters.TimingCaptureParameters.CaptureCpuSamples = TRUE;
    captureParameters.TimingCaptureParameters.CpuSamplesPerSecond = 4000;
    captureParameters.TimingCaptureParameters.CaptureFileIO = TRUE;
    captureParameters.TimingCaptureParameters.CapturePageFaultEvents = TRUE;

    HRESULT beginCaptureResult = PIXBeginCapture(PIX_CAPTURE_TIMING, &captureParameters);
    if (SUCCEEDED(beginCaptureResult))
    {
        LoadLevel(); // Application-defined workload.
        HRESULT endCaptureResult = PIXEndCapture(FALSE);
        (void)endCaptureResult;
    }
}

FileName must be non-null, and the parent directory must exist. If CaptureCpuSamples is TRUE, CpuSamplesPerSecond must be 1000, 4000, or 8000. CaptureStorage must be PIXCaptureParameters::PIXCaptureStorage::Memory or PIXCaptureParameters::PIXCaptureStorage::FileCircular on Windows.

For long-running Timing captures, use FileCircular with MaximumToolingMemorySizeMb.

void CaptureLongRunningWorkload()
{
    PIXCaptureParameters captureParameters = {};
    captureParameters.TimingCaptureParameters.FileName = L"C:\\captures\\long-run.wpix";
    captureParameters.TimingCaptureParameters.CaptureStorage = PIXCaptureParameters::PIXCaptureStorage::FileCircular;
    captureParameters.TimingCaptureParameters.MaximumToolingMemorySizeMb = 1024;

    HRESULT beginCaptureResult = PIXBeginCapture(PIX_CAPTURE_TIMING, &captureParameters);
    if (SUCCEEDED(beginCaptureResult))
    {
        RunWorkload(); // Application-defined workload.
        HRESULT endCaptureResult = PIXEndCapture(FALSE);
        (void)endCaptureResult;
    }
}

Pass TRUE to PIXEndCapture(TRUE) to discard a Timing capture instead of saving it.

Set Timing capture parameters

PIXCaptureParameters::TimingCaptureParameters contains these fields on Windows.

Field Use
FileName Output .wpix file path. This field is required.
MaximumToolingMemorySizeMb Circular-file size limit when CaptureStorage is FileCircular.
CaptureStorage Memory for the default mode or FileCircular for PC circular capture. MemoryCircular isn't supported on Windows.
CaptureGpuTiming Include GPU timing data.
CaptureCallstacks Include callstacks for context-switch and ready-thread events.
CaptureCpuSamples Include CPU samples.
CpuSamplesPerSecond CPU sampling rate. Use 1000, 4000, or 8000 when CPU sampling is enabled.
CaptureFileIO Include file I/O events with stacks.
CaptureVirtualAllocEvents Include virtual allocation events with stacks.
CaptureHeapAllocEvents Include heap allocation events with stacks.
CaptureXMemEvents Not supported on Windows.
CapturePixMemEvents Include PIX memory allocation events with stacks.
CapturePageFaultEvents Include page fault events with stacks.
CaptureVideoFrames Not supported on Windows. Don't set it.

PIXCaptureParameters::GpuTraceParameters is also present in the shared union, but it isn't supported on Windows.

Use capture flags correctly

PIXBeginCapture() and PIXGetCaptureState() use the PIX_CAPTURE_* flags from pix3.h. Pass exactly one capture type to PIXBeginCapture() on Windows. Don't combine PIX_CAPTURE_GPU and PIX_CAPTURE_TIMING; they use different members of the PIXCaptureParameters union, and the combined flag value returns E_NOTIMPL.

Flag Windows programmatic capture support
PIX_CAPTURE_GPU Supported by PIXBeginCapture().
PIX_CAPTURE_TIMING Supported by PIXBeginCapture().
PIX_CAPTURE_FUNCTION_SUMMARY Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_FUNCTION_DETAILS Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_CALLGRAPH Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_INSTRUCTION_TRACE Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_SYSTEM_MONITOR_COUNTERS Defined for compatibility. Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_VIDEO Defined for compatibility. Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_AUDIO Defined for compatibility. Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_GPU_TRACE Defined for compatibility. Not implemented by Windows PIXBeginCapture().
PIX_CAPTURE_RESERVED Reserved.

On Windows, PIXBeginCapture2() dispatches only PIX_CAPTURE_GPU and PIX_CAPTURE_TIMING. Any other flag returns E_NOTIMPL.

Open a capture in PIX

After your application writes a capture file, call PIXOpenCaptureInUI() to open it through its Windows file association. This opens PIX when .wpix files are associated with PIX.

HINSTANCE openResult = PIXOpenCaptureInUI(L"C:\\captures\\loading.wpix");
if (reinterpret_cast<INT_PTR>(openResult) <= 32)
{
    // ShellExecuteW failed.
}

Use IDXGraphicsAnalysis only for legacy integrations

Prefer the pix3.h capture APIs for new code. Legacy integrations can still use IDXGraphicsAnalysis from DXProgrammableCapture.h after PIX has attached to the process for GPU capture.

#include <DXProgrammableCapture.h>

void CaptureWithLegacyGraphicsAnalysis()
{
    Microsoft::WRL::ComPtr<IDXGraphicsAnalysis> graphicsAnalysis;
    HRESULT result = DXGIGetDebugInterface1(0, IID_PPV_ARGS(&graphicsAnalysis));
    if (SUCCEEDED(result))
    {
        graphicsAnalysis->BeginCapture();
        RenderFrame(); // Application-defined rendering work.
        graphicsAnalysis->EndCapture();
    }
}