PIX on Windows

Performance tuning and debugging for DirectX 12 games on Windows

WinPixEventRuntime

PIX events are used to instrument your game, labeling regions of CPU or GPU work and marking important occurrences.  Including this instrumentation while developing the game can make PIX captures far nicer to work with.

An “event” represents a region of time – so an event has a begin and an end.  A “marker” is used to represent a single point in time.

Installation

The PIX event runtime is distributed as a NuGet package.  If your project builds in Visual Studio, you can use Visual Studio to add this package:

  1. Right-click on the project in Solution Explorer.
  2. Manage NuGet Packages.
  3. Select the Browse tab.
  4. Type “WinPixEventRuntime” into the search box.
  5. Select the package and click Install.
  6. You can now #include “pix3.h” to access the PIX event APIs.

If your project uses some other build technology than Visual Studio, you can manually consume the NuGet package contents:

  1. Click the Download link on https://www.nuget.org/packages/WinPixEventRuntime
  2. Rename the .nupkg file to a .zip extension, then extract its contents.
  3. Include the header file: Include\WinPixEventRuntime\pix3.h
  4. Link with the library:
    • For desktop apps: bin\WinPixEventRuntime.lib
    • For Universal Windows Platform (UWP) apps: bin\WinPixEventRuntime_UAP.lib
  5. Place the corresponding .dll next to your game.

PIX instrumentation is only enabled if one of the preprocessor symbols USE_PIX, DBG, _DEBUG, PROFILE, or PROFILE_BUILD is defined.  Otherwise the marker APIs compile out to nothing, which is typically what you want for final release builds of the game.  It is often useful to maintain an intermediate build flavor for profiling purposes, which is fully optimized and leaves out self-checks such as ASSERT, but does include the PIX marker instrumentation.  To do this, define one of the symbols USE_PIX or PROFILE before including pix3.h.

Note that the Windows SDK provides an older header named pix.h.  This defines similar functions to pix3.h, but is obsolete and not compatible with PIX.

API Reference

PIXBeginEvent

PIXBeginEvent marks the start of a user-defined region of CPU or GPU work.  There are overloads of PIXBeginEvent that support different types of strings and CPU and GPU events. Use PIXEndEvent to mark the end of the region.

// CPU only
void PIXBeginEvent(UINT64 color, char const* formatString, ...)
void PIXBeginEvent(UINT64 color, wchar_t const* formatString, ...)

// GPU and CPU
void PIXBeginEvent(ID3D12CommandList* commandList, UINT64 color, char const* formatString, ...)
void PIXBeginEvent(ID3D12CommandList* commandList, UINT64 color, wchar_t const* formatString, ...)
void PIXBeginEvent(ID3D12CommandQueue* commandQueue, UINT64 color, char const* formatString, ...)
void PIXBeginEvent(ID3D12CommandQueue* commandQueue, UINT64 color, wchar_t const* formatString, ...)

The GPU overloads also start an implicit CPU region.

PIXEndEvent

PIXEndEvent marks the end of a user-defined region of CPU or GPU work. There are overloads of PIXEndEvent that support different types of CPU and GPU events. Note that a GPU region that was started on a command list may be ended on a command list or a command queue, and vice versa.

// CPU only
void PIXEndEvent()
        
// GPU and CPU
void PIXEndEvent(ID3D12CommandList* commandList)
void PIXEndEvent(ID3D12CommandQueue* commandQueue)

The GPU overloads also end an implicit CPU region.

PIXSetMarker

PIXSetMarker inserts a user-defined marker into the CPU or GPU timeline:

 // CPU
void PIXSetMarker(UINT64 color, char const* formatString, ...)
void PIXSetMarker(UINT64 color, wchar_t const* formatString, ...)

// GPU and CPU
void PIXSetMarker(ID3D12GraphicsCommandList* commandList, UINT64 color, char const* formatString, ...)
void PIXSetMarker(ID3D12GraphicsCommandList* commandList, UINT64 color, wchar_t const* formatString, ...)
void PIXSetMarker(ID3D12CommandQueue* commandQueue, UINT64 color, char const* formatString, ...)
void PIXSetMarker(ID3D12CommandQueue* commandQueue, UINT64 color, wchar_t const* formatString, ...)

PIXScopedEvent

The PIXScopedEvent macro has the same overloads as PixBeginEvent, but will automatically issue a matching PixEndEvent call at the end of the C++ code scope where it is used:

PIXScopedEvent(UINT64 color, char const* formatString, ...)
PIXScopedEvent(UINT64 color, wchar_t const* formatString, ...)
PIXScopedEvent(ID3D12GraphicsCommandList* commandList, UINT64 color, char const* formatString, ...)
PIXScopedEvent(ID3D12GraphicsCommandList* commandList, UINT64 color, wchar_t const* formatString, ...)
PIXScopedEvent(ID3D12CommandQueue* commandQueue, UINT64 color, char const* formatString, ...)
PIXScopedEvent(ID3D12CommandQueue* commandQueue, UINT64 color, wchar_t const* formatString, ...)

PIXReportCounter

PIXReportCounter allows a custom value to be graphed in System Monitor or timing captures.

void PIXReportCounter(wchar_t const* name, float value)

Note that PIXReportCounter does not has a char const* overload.

PIXNotifyWakeFromFenceSignal

Timing captures can show when a thread wakes up as the result of a fence being signaled. This needs some help from the application in the form of PIXNotifyWakeFromFenceSignal. This notifies PIX that an event handle was set as a result of a D3D12 fence being signaled. The event specified must have the same handle value as the handle used in ID3D12Fence::SetEventOnCompletion.

void PIXNotifyWakeFromFenceSignal(HANDLE event)

Color

The color parameter controls how the event will be displayed in timeline lanes when it appears in the PIX timing capture user interface.  Suitable values can be obtained from one of these helpers, or you can pass in a raw DWORD noting that the format is ARGB and the alpha channel value must be 0xff:

// Returns a color for a PIX event or marker from the specified red, green and blue values
 INT PIX_COLOR(BYTE r, BYTE g, BYTE b)

// Returns an arbitrary color value for the given index.
// PIX allocates a unique color for each index.
UINT PIX_COLOR_INDEX(BYTE i)

Formatted Strings

In order to minimize instrumentation overhead, the PIXBeginEvent and PIXSetMarker functions directly save their format string and format parameters instead of formatting the string at runtime. Formatting is then done when reading the capture file in PIX. Use 16-byte aligned strings (preferable) or 8-byte aligned strings to get the best performance. To print a char* or wchar_t* as a pointer using %p format specifier, cast the pointer to void* when passing it to these functions.

Other functions

PIX on Windows supports programmatic capture APIs (such as PIXBeginCapture) for GPU Captures. Please see this page for mode details.

ABI Usage

Most of the implementation of PIX markers is in the header file with the DLL providing support functionality. The stable interface though is in the header file. If for some reason it is necessary to use a stable ABI then these entry points are available. These have a stronger compatability guarantee than the other WinPixEventRuntime.dll exports.

Most users should continue to use pix3.h. This will provide richer and more optimized functionality.

The source snippet below is subject to the MIT license

Use LoadLibrary to load the dll and GetProcAddress to access the exports:

typedef void(WINAPI* BeginEventOnCommandList)(ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);
typedef void(WINAPI* EndEventOnCommandList)(ID3D12GraphicsCommandList* commandList);
typedef void(WINAPI* SetMarkerOnCommandList)(ID3D12GraphicsCommandList* commandList, UINT64 color, _In_ PCSTR formatString);

HMODULE module = LoadLibrary(L"WinPixEventRuntime.dll");

BeginEventOnCommandList pixBeginEventOnCommandList = (BeginEventOnCommandList)GetProcAddress(module, "PIXBeginEventOnCommandList");
EndEventOnCommandList   pixEndEventOnCommandList   = (EndEventOnCommandList)GetProcAddress(module, "PIXEndEventOnCommandList");
SetMarkerOnCommandList  pixSetMarkerOnCommandList  = (SetMarkerOnCommandList)GetProcAddress(module, "PIXSetMarkerOnCommandList");

Error handling and more careful resource management is left as an exercise for the reader.

Release History

  • 1.0.220810001 – Support for new programmatic APIs (HUD control, 11On12). Support for BeginEventOnCommandQueue (and similar) ABIs
  • 1.0.220124001 – Support for WinPixEventRuntime’s ABI (BeginEventOnCommandList etc.) in UWP.
  • 1.0.210209001
  • 1.0.200127001
  • 1.0.190604001 – Fix compatability with old versions of PIX and the debug layer, fix building with /permissive-
  • 1.0.190510001 – Add ABI exports to DLL
  • 1.0.190425002 – ARM64 support
  • 1.0.181206001 – Add PIXNotifyWakeFromFenceSignal

Further Reading