Direct3D 11 Debug API Tricks

Direct3D 11 has some handy debug APIs provided that make tracking down problems much easier. Unfortunately, while the API reference is rather complete and quite useful, I haven't been able to find any good tutorials on actually using them in real code. The following are a few tips on using the API interfaces.

Graphics Debugging

Debugging graphics APIs requires a lot of specialized tools, whether you're using Direct3D or OpenGL. For my personal Direct3D projects I've been quite happy with the Visual Studio 2013 Graphics Debugger. Note that this debugger is only available in Visual Studio 2013 (the version in 2012 is a different, lesser beast) and also that you must be running on Windows 8.1 to use it (2013 will fall back to the old 2012 Graphics Debugger if you're running Windows 8.0 or older). Other tools include PIX (though it won't likely work with the tips in this article; more on that in a bit), NVPerfHUD, AMD PerfStudio, and Intel GPA. For OpenGL projects the options are a bit more limited and usually not as polished, though NVPerfKit and AMD PerfStudio both have GL support.

The problem with these tools by themselves is that they don't make it easy to correlate graphics commands and objects with your native code. You can inspect a vertex buffer but you don't get to easily tell where that buffer came from, for example.

The debug APIs built in to Direct3D 11/11.1 allow the debug layer of Direct3D to record more information about what's going on in your native code. This information can then be used by the graphics debuggers when they display information about API calls or objects. OpenGL does not have an equivalent set of APIs, unfortunately. This article will focus solely on Direct3D 11.

Windows 7, Direct3D 11.1, and PIX

The Direct3D 11.1 runtime is the only version to support some of these debug APIs. Some parts of Direct3D 11.1 (including these debug APIs) have been backported to Windows 7 via a Platform Update so you aren't strictly required to run Windows 8. Unfortunately, the Platform Update that enables these pieces of 11.1 also broke PIX. Any development work done on Windows 7 wanting to make use of these features will need to use an alternate tool, like the Visual Studio Graphics Debugger or one of the IHV's tools. I definitely suggest upgrading to Windows 8.1 (you can make it feel like Windows 7 via Classic Shell if you're really not into the Metro/Modern UI) to get at the newer Visual Studio 2013 Graphics Debugger. This gives you complete Direct3D 11.2 features as well, if you plan to experiment with those any.

Direct3D and COM

Before delving into the APIs themselves, I'd like to give a quick refresher on Direct3D's API itself. Direct3D is a set of COM interfaces. All COM interfaces have a number of features in common from the base COM model. The first thing to remember is that COM is a reference-counted API. Every object acquired via COM needs to have its Release method called to destroy it rather than the delete operator. The AddRef method can be used to increase the reference count if a COM object reference needs to be shared. Microsoft's CComPtr smart pointer class is a good thing to use to make sure that AddRef and Release are called automatically when appropriate.

The second thing to remember is that every COM object has one or more interfaces. The root interface of all COM interfaces is IUnknown. It's this interface that provides the AddRef and Release methods. It also provides the QueryInterface method. The QueryInterface method allows developers to access any of the interfaces a COM object supports given only a pointer to another interface on that object. This feature is going to be required to access some of the Direct3D debug functionality. Remember that QueryInterface gives back an interface that already has a reference count of 1, so it's necessary to call Release on any interfaces retrieved via QueryInterface to avoid object leaks.

Direct3D Debug Interfaces

There are three primary interfaces I'm going to go over in this article. The first is ID3DUserDefinedAnnotation. The second is ID3D11Debug. The third is ID3D11DeviceChild which is not technically a debug interface but one of its features we'll be using is only enabled when using the Debug Layer. There are other debug interfaces in Direct3D such as IDXGIDebug which I will not be going over. Also of importance is enabling the Direct3D Debug Layer.

Debug Layer

When creating a device, Direct3D allows you to create a Debug Layer that enables most of these debugging features. They are not available if the Debug Layer is not in use. The Debug Layer is enabled by providing the D3D11_CREATE_DEVICE_DEBUG flag to D3D11CreateDeviceAndSwapChain/D3DCreateDevice, like so:

const D3D_FEATURE_LEVEL requested_feature_levels[] = {
  D3D_FEATURE_LEVEL_11_1,         // Always ask for 11.1 (or higher) if available
  // other fallbacks
};
const UINT num_requested_feature_levels = 1;

HRESULT hr = D3D11CreateDeviceAndSwapChain(
  nullptr,
  D3D_DRIVER_TYPE_HARDWARE,
  nullptr,
  D3D11_CREATE_DEVICE_DEBUG,      // Set this to enable the Debug Layer!
  requested_feature_levels,
  num_requested_feature_levels,
  D3D11_SDK_VERSION,
  swap_chain_description,
  &pSwapChain,
  &pDevice,
  &active_feature_level,
  pContext);

I'm going to assume the reader is familiar with the use of the rest of the arguments to D3D11CreateDeviceAndSwapChain. If not, it's covered by most tutorials or the MSDN documentation.

The ID3D11Debug Interface

The ID3D11Debug interface (MSDN is a fairly small interface. The primary method I'm interested in is ReportLiveObjects (MSDN).

Direct3D Leak Warnings

You may have seen output when shutting down your application or when release your device handle that looks something like this:

D3D11 WARNING: Process is terminating. Using simple reporting. Please call ReportLiveObjects() at runtime for standard reporting. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Producer at 0x000000F8DD02D270, Refcount: 4. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING:  Live Object at 0x000000F8DD02F640, Refcount: 1. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING:  Live Object at 0x000000F8DD045980, Refcount: 7. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING:  Live Object at 0x000000F8DD0439D0, Refcount: 1. [ STATE_CREATION WARNING #0: UNKNOWN]

Usually there'll be many more lines. The error points you the function ReportLiveObjects, which is the API we'll be looking at here. These errors can be a bit confusing because the errors contain a bunch of object addresses (note that those are 64-bit addresses in the sample output above if they look unfamiliar) without telling you much else. At the very least it would be handy to know what kinds of objects they are. Are they buffers? Textures? Shaders? The ReportLiveObjects method will show us those details.

Acquiring an ID3D11Debug Interface

The first thing necessary is to actually acquire an ID3D11Debug interface. First, note that this is an interface on the ID3D11Device object and not on the ID3D11Context object. The other interface we'll be going over later lives on the context object, but not this one.

Acquiring the interface is fairly straight forward. We just need to call QueryInterface on the device object to get the debug interface, if available.

CComPtr pDebug; HRESULT hr = pDevice->QueryInterface(IID_PPV_ARGS(&pDebug));

You can check hr for failure or just check if pDebug is non-null. Note that you can avoid the use of IID_PPV_ARGS if you're prefer to spell out the arguments to QueryInterface yourself (MSDN) or you can use ComQIPtr if you'd prefer something even more automatic (MSDN).

Using ID3D11Debug

Once you have this interface, you can call of the ID3D11Debug methods on it, including ReportLiveObjects. You can call this method at any time to get a list of all live objects which may be a feature you'd want to enable on request for debugging. It's especially useful to call at the end of your graphics device shutdown code or at the end of your application's lifetime in order to supplement the debug Direct3D object leak detector code.

Calling the method is simple. It takes a single argument describing the format of the output we want. We'll ask for the most detailed output using the D3D11_RLDO_DETAIL flag.

// manually release all your other Direct3D resource handles
// remember to call Release() on these if you're not using CComPtr
pSwapChain = nullptr;
pContext = nullptr;
pDevice = nullptr;

// dump output only if we actually grabbed a debug interface
if (pDebug != nullptr)
{
  pDebug->ReportLiveObjects(D3D11_RLDO_DETAIL);
  pDebug = nullptr;
}

That's it. It'll dump out a list of all live objects including their type, reference counts, and the custom name debug information (I'll show you how to set that in the next section). You'll see expanded output like:

D3D11 WARNING: Live ID3D11Device at 0x000000F8DD02D190, Refcount: 5 [ STATE_CREATION WARNING #441: LIVE_DEVICE]
D3D11 WARNING:  Live ID3D11Context at 0x000000F8DD02F640, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #2097226: LIVE_CONTEXT]
D3D11 WARNING:  Live ID3DDeviceContextState at 0x000000F8DD045980, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #3145742: LIVE_DEVICECONTEXTSTATE]
D3D11 WARNING:  Live ID3D11BlendState at 0x000000F8DD0439D0, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #435: LIVE_BLENDSTATE]
D3D11 WARNING:  Live ID3D11DepthStencilState at 0x000000F8DD050F70, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #436: LIVE_DEPTHSTENCILSTATE]
D3D11 WARNING:  Live ID3D11RasterizerState at 0x000000F8DD051190, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #437: LIVE_RASTERIZERSTATE]
D3D11 WARNING:  Live ID3D11Sampler at 0x000000F8DD0514B0, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #434: LIVE_SAMPLER]
D3D11 WARNING:  Live ID3D11Query at 0x000000F8DD051730, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #438: LIVE_QUERY]
D3D11 WARNING:  Live IDXGISwapChain at 0x000000F8DD051910, Refcount: 0 [ STATE_CREATION WARNING #442: LIVE_SWAPCHAIN]
D3D11 WARNING:  Live ID3D11Texture2D at 0x000000F8DD052280, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #425: LIVE_TEXTURE2D]
D3D11 WARNING:  Live ID3D11RenderTargetView at 0x000000F8DD052860, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #428: LIVE_RENDERTARGETVIEW]
D3D11 WARNING:  Live ID3D11PixelShader at 0x000000F8DD055830, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #432: LIVE_PIXELSHADER]
D3D11 WARNING:  Live ID3D11VertexShader at 0x000000F8DD055C00, Name: /Shaders/basic.hlsl, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #430: LIVE_VERTEXSHADER]
D3D11 WARNING:  Live ID3D11InputLayout at 0x000000F8DD066FD0, Refcount: 1, IntRef: 0 [ STATE_CREATION WARNING #433: LIVE_INPUTLAYOUT]

There are four different types of entries in the log output above, three of which are false positives to be wary of. The first type includes the first line reporting that ID3D11Device is still alive. Remember that ID3D11Debug is an interface on the device object. In order to generate the above report you must have an ID3D11Debug interface which means that the report will always show the device has still being alive. It's not a leak but the debug output can be confusing since it's always labelled as a "warning."

The second kind of entry above includes the second line about ID3D11Context. Notice that it's Refcount (application references) is zero but it's IntRef (internal references) is not. This is a resource owned by the ID3D11Device. A number of the other resources list above also have non-zero internal references but zero application references. These are resources that will be cleaned up properly once the ID3D11Debug interface is released.

The third kind of entry includes the second to last line about an ID3D11VertexShader. Note that both its Refcount and IntRef entries are zero. This is a properly cleaned up and released resource which the debug interface hasn't yet cleaned up. It can safely be ignored.

The final kind of entry is the one that we're interested in as it's not a false positive. That would be the final line in the example output about the ID3D11InputLayout. Note that its Refcount is non-zero meaning that our application did not release its handle properly. Knowing the type of the object can help narrow down the causes (in this case given my renderer's architecture I know exactly that I forgot to clean up my LayoutManager data). In other cases, additional debug information can be attached like in the vertex shader's case in order to help pinpoint what the resource is, where it was created, and who's responsible for it.

One final note is that calling the ReportLiveObjects method does not disable the normal leak output. If you have any actual leaks then you will also see the original nearly-useless output. However, if all your leaks are false positives then they'll be released once the ID3D11Debug interface is released and the default leak detector will not print out any additional warnings.

The ID3D11DeviceChild Interface

Every resource type created by an ID3D11Device is a child of this interface. The ID3D11DeviceChild interface has a few useful methods. The primary one we're interested in SetPrivateDate (MSDN). This method allows us among other things to assign a name to the object that will show up in many graphics debuggers.

This method requires a GUID to identify the piece of data we're going to set. Direct3D provides a constant named WKPDID_D3DDebugObjectName that includes the GUID used to set debug names. Note that you need to link against dxguid.lib in order to use this constant, or you could copy the GUID into your application directly. Setting a name is fairly easy. I use a simple wrapper function to allow setting the name on any object easily:

void SetDebugName(ID3D11DeviceChild* child, const std::string& name)
{
  if (child != nullptr && name != nullptr)
    child->SetPrivateData(WKPDID_D3DDebugObjectName, name.size(), name.c_str());
}

And that's that. Calling the method will let you set names on objects. In the log output in the previous section you can see how I assigned the path a shader was loaded from in order to more easily identify the shader. Another good use would be to set the filename, mesh index, and vertex count for any vertex buffers created for models loaded from disk. It's handy to see output like:

obj:37,/Models/foo.mesh (submesh #7, 5832 vertices),D3D11 Buffer,,23328,,0,0,0,0,1

In a graphics debugger when inspecting the data objects in use. Without that extra name (the obj:37,/Models/foo.mesh (submesh #7, 5832 vertices) part), it's much more difficult to tell which resource is which. You can name any device resource you can think of, including buffers, textures, shaders, input layouts, render targets, resource views, samplers, and even context objects. I recommend naming every object you can with as descriptive of a name as possible.

The ID3DUserDefinedAnnotation Interface

This interface is not quite as useful as the ID3D11DeviceObject one above though I do find it far more useful than ID3D11Debug. This interface the ability to tag events (allowing you to group related API calls together when using a graphics debugger) as well as setting markers (events that don't enclose a group of API calls).

The primary use of this interface is to tag related sets of state change and draw call API invocations. For instance, it can be used to easily annotate that a group of calls are all related to drawing a particular model, a particular render queue, a particular instance batch, or so on. The events can be nested.

Acquiring an ID3DUserDefinedAnnotation Interface

Acquiring this interface is not different than acquiring any other COM interface. However, do note that this interface exists on the Direct3D context object and not the device. Also note that this interface requires Direct3D 11.1 either via Windows 8 or the Windows 7 Platform Update.

CComPtr pUserDefinedAnnotation; HRESULT hr = pContext->QueryInterface(IID_PPV_ARGS(&pUserDefinedAnnotation));

It's probably safest to not assume this call will succeed since many developers still prefer to use Windows 7.

Events

Events group other API calls. It lets you see output something like:

> Clearing screen
  ClearRenderTargetView(obj:9)
  ClearDepthStencilView(obj:10)
> Drawing frame 78
  > Drawing foo.mesh
    IASetVertexBuffers(obj:15)
    VSSetShader(obj:16)
    PSSetShader(obj:17)
    Draw
  > Drawing bar.mesh
    > Drawing submesh 1
      IASetVertexBuffers(obj:21)
      VSSetShader(obj:22)
      PSSetShader(obj:23)
      Draw
    > Drawing submesh 2
      IASetVertexBuffers(obj:27)
      VSSetShader(obj:28)
      PSSetShader(obj:29)
      Draw

This information is invaluable when trying to sort out why some particular call is happening or what exactly is being rendered when something goes wrong.

Adding these annotations is quite simple. To begin an event grouping some API calls, call BeginEvent (MSDN). Once the group is done, call EndEvent (MSDN). You must remember to call EndEvent after calling BeginEvent or you'll quickly run into errors.

Note that both BeginEvent and EndEvent take wide character strings, meaning you must use UTF-16. This is annoying in itself given all the problems with 16-bit Unicode encodings. It's doubly annoying since the SetPrivateData method gone over in the previous section does not use wide character strings. You'll likely want some utility functions to convert between your application's native character set and UTf-8/UTf-16 such as MultiByteToWideChar and WideCharToMultiByte, both of which support UTF-8 on modern incarnations of Windows (which you must be using anywhere to use Direct3D 11). I find it handy to use wrapper functions to check pointer validity and enforce balancing of the calls, but the direct way is easy enough to use:

if (pUserDefinedAnnotation != nullptr) pUserDefinedAnnotation->BeginEvent(L"Drawing mesh"); pContext->VSSetVertexShader(pVertexShader); pContext->IASetInputLayout(pInputLayout); pContext->IASetVertexBuffers(0, 1, &pVertexBuffer, stride, offset); pContext->IASetIndexBuffer(pIndexBuffer, DXGI_FORMAT_R16_UINT, 0); pContext->DrawIndexed(vertex_count, 0, 0); if (pUserDefinedAnnotation != nullptr) pUserDefinedAnnotation->EndEvent();

Not much to it. Remember that you can nest these calls if you want. Just be sure to balance every BeginEvent with an EndEvent.

Markers

A marker is like an event, but it doesn't have group calls. It just adds an entry to the draw call list in debugging tools, essentially like a log message but interleaved with other API calls in the debug tools. Like BeginEvent it requires a wide character string. It's also easy to use by just calling SetMarker (MSDN). I also use a wrapper normally, but directly using it isn't particularly complicated:

if (!disable_some_feature)
  draw_something_fancy();
else if(pUserDefinedAnnotation != nullptr)
  pUserDefinedAnnotation(L"Skipping fancy render because the required feature is disabled");

And that's all there is to it.

Summary

All three of these debug APIs have come in very handy for me. The object names and the user-annotated events in particular have been indispensable. I hope this article helps you integrate use of these features easily into your own projects.

Note that not all of these features are supported by all debugging tools. I really do strongly urge you to use Visual Studio 2013 on Windows 8.1 to get at the latest incarnation of Microsoft's Graphics Debugger functionality. It supports the above APIs as well as shader debugging (you can step through the shaders line by line like y ou can with native code). I've also found it to be much more stable and easier to figure out than most of the IHV's tools, though some of the IHV tools do have better performance analysis support.

As a graphics developer, you should make yourself as comfortable as possible with every tool at your disposal. The Visual Studio Graphics Debugger, the IHV tools, the APIs described in this article, and the other debug APIs available are all important to keep in your graphics toolbox. Proper use of these tools can greatly enhance your productivity at developing and debugging graphical applications and games.

Sean Middleditch

Read more posts by this author.