DirectX Leak Debugging
Yo my peps,
I have put together a small tutorial on how to use the ID3D11Debug interface to debug memory leaks caused by not properly releasing DirectX objects.
So I recently discovered some warning messages in my C++ DirectX application that are printed to the debug output (not the console) when exiting the program.
(Note: due to a faulty WordPress plugin, some code snippets may be broken with random dashes and tab characters, it should still be comprehensible though)
It looked 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 0x007464B0, Refcount: 4. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x007476F8, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A07580, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A07264, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A10ACC, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A11204, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A1295C, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A11944, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A11B60, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A120C4, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A133BC, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A1373C, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A14A2C, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A14CB4, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A153AC, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A16D7C, Refcount: 1. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A22B7C, Refcount: 1. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x0C17154C, Refcount: 1. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A26904, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A2A2CC, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x0C1687F4, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x0C16BE0C, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object at 0x01A1ADC4, Refcount: 0. [ STATE_CREATION WARNING #0: UNKNOWN]
D3D11 WARNING: Live Object : 22 [ STATE_CREATION WARNING #0: UNKNOWN]
DXGI WARNING: Live Producer at 0x006AE3A8, Refcount: 4. [ STATE_CREATION WARNING #0: ]
DXGI WARNING: Live Object at 0x006AEA38, Refcount: 2. [ STATE_CREATION WARNING #0: ]
DXGI WARNING: Live Object : 1 [ STATE_CREATION WARNING #0: ]
Ok, so live objects refer to objects which hasn’t been properly released (i.e. memory leaks). Most D3D interface classes such as ID3D11DeviceContext and ID3D11Buffer have a -Release function. Indeed, every interface inheriting from the abstract base class IUknown has it (which is mostly all interfaces returned by D3D11 functions).
So how does this help? Well mostly, it doesn’t. It does say that we could acquire more information by calling ReportLiveObjects() , but it’s not really that straightforward. Nevertheless, I will guide you through how it works:
Prerequisites
The first requirement is that you create the D3D device with the D3D11_CREATE_DEVICE_DEBUG flag, preferably only when in debug mode:
printf("Creating contextn");
Result = D3D11CreateDeviceAndSwapChain(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
#if defined(_DEBUG)
D3D11_CREATE_DEVICE_DEBUG,
#else
0,
#endif
&FeatureLevelRequired,
1,
D3D11_SDK_VERSION,
&SwapChainDesc,
&SwapChain,
&Device,
&FeatureLevelAchieved,
&DeviceContext
);
CHECKERROR
Result is simply a HRESULT used to store the success or failure, and CHECKERROR is a macro handling it.
The critical lines here are marked. Notice how we differ from a debug project and a release project. In the production build we don’t want to create a debug device (we should have figured out all leaks by then and it is also a unecessary performance hit).
The resulting device, here specified with &Device now has a debug interface attached to it.
Reporting live objects
In order to display some valuable debug information we now need to query the debug interface, and call its ReportLiveDeviceObjects(D3D11_RLDO_FLAGS Flags) function.
The querying is done with Device-QueryInterface , specifying the uuid of ID3D11Debug and a pointer to the output pointer, similarly to many of the DirectX11 functions, resulting in a ID3D11Debug* . Consider the example code below:
ID3D11Debug* DebugDevice = nullptr;
HRESULT Result = Device−>QueryInterface(__uuidof(ID3D11Debug), reinterpret_cast<void**>(&DebugDevice));
CHECKERROR
With this, assuming it succeeds, we now have debug device. The reporting of live object is then simply the action of calling a function of it:
Result = DebugDevice−>ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
CHECKERROR
This will output information to the debug output. D3D11_RLDO_DETAIL specifies that we want a more verbose output, while the D3D11_RLDO_SUMMARY is similar to the automatic output at the start of this post. Don’t forget to release the debug device afterwards.
My code:
void RenderSystem::ReportLiveObjects()
{
#ifdef _DEBUG
ID3D11Debug* DebugDevice = nullptr;
HRESULT Result = Device−QueryInterface(__uuidof(ID3D11Debug), reinterpret_cast − **(&DebugDevice));
CHECKERROR
Result = DebugDevice−ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
CHECKERROR
SAFE_RELEASE(DebugDevice);
#endif
}
( SAFE_RELEASE is a macro that just checks if the pointer is valid before calling Release ).
Interpreting the output
Sample output:
D3D11 WARNING: Live ID3D11Device at 0x0074643C, Refcount: 6 [ STATE_CREATION WARNING #441: LIVE_DEVICE]
D3D11 WARNING: Live ID3D11Context at 0x007476F8, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #2097226: LIVE_CONTEXT]
D3D11 WARNING: Live ID3DDeviceContextState at 0x01A07580, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #3145742: LIVE_DEVICECONTEXTSTATE]
D3D11 WARNING: Live ID3D11BlendState at 0x01A07264, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #435: LIVE_BLENDSTATE]
D3D11 WARNING: Live ID3D11DepthStencilState at 0x01A10ACC, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #436: LIVE_DEPTHSTENCILSTATE]
D3D11 WARNING: Live ID3D11RasterizerState at 0x01A11204, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #437: LIVE_RASTERIZERSTATE]
D3D11 WARNING: Live ID3D11Sampler at 0x01A1295C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #434: LIVE_SAMPLER]
D3D11 WARNING: Live ID3D11Query at 0x01A11944, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #438: LIVE_QUERY]
D3D11 WARNING: Live IDXGISwapChain at 0x01A11B60, Refcount: 0 [ STATE_CREATION WARNING #442: LIVE_SWAPCHAIN]
D3D11 WARNING: Live ID3D11Texture2D at 0x01A120C4, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #425: LIVE_TEXTURE2D]
D3D11 WARNING: Live ID3D11RenderTargetView at 0x01A133BC, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #428: LIVE_RENDERTARGETVIEW]
D3D11 WARNING: Live ID3D11Texture2D at 0x01A1373C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #425: LIVE_TEXTURE2D]
D3D11 WARNING: Live ID3D11DepthStencilView at 0x01A14A2C, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #429: LIVE_DEPTHSTENCILVIEW]
D3D11 WARNING: Live ID3D11DepthStencilState at 0x01A14CB4, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #436: LIVE_DEPTHSTENCILSTATE]
D3D11 WARNING: Live ID3D11RasterizerState at 0x01A153AC, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #437: LIVE_RASTERIZERSTATE]
D3D11 WARNING: Live ID3D11VertexShader at 0x01A16D7C, Refcount: 1, IntRef: 0 [ STATE_CREATION WARNING #430: LIVE_VERTEXSHADER]
D3D11 WARNING: Live ID3D11PixelShader at 0x01A22B7C, Refcount: 1, IntRef: 0 [ STATE_CREATION WARNING #432: LIVE_PIXELSHADER]
D3D11 WARNING: Live ID3D11InputLayout at 0x0C17154C, Refcount: 1, IntRef: 0 [ STATE_CREATION WARNING #433: LIVE_INPUTLAYOUT]
D3D11 WARNING: Live ID3D11Buffer at 0x01A26904, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #423: LIVE_BUFFER]
D3D11 WARNING: Live ID3D11Buffer at 0x01A2A2CC, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #423: LIVE_BUFFER]
D3D11 WARNING: Live ID3D11Buffer at 0x0C1687F4, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #423: LIVE_BUFFER]
D3D11 WARNING: Live ID3D11Buffer at 0x0C16BE0C, Refcount: 0, IntRef: 0 [ STATE_CREATION WARNING #423: LIVE_BUFFER]
D3D11 WARNING: Live ID3D11Texture2D at 0x01A1ADC4, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #425: LIVE_TEXTURE2D]
It reports the similar values as before, such as Refcount and the hex address, but the most important new addition is the that the type is specified. This will help significantly in narrowing down the resources.
I’ve highlighted the important lines here. As you can see, I haven’t released a vertex buffer, an index buffer and an input layout.
The first line is an important notice; it represents the top-level ID3D11Device created at start. In order to even query the debug interface, a valid ID3D11Device pointer must exist. All other resources can be released ahead of calling ReportLiveDeviceObjects , but as the ID3D11Device is not released, it still reports that it is live (which it is).
Another important factor is to differentiate between Refcount and IntRef. IntRef is the internal reference count which are held internally by DirectX objects. For example, ID3D11Context at line 2 has no Refcount as it has already been released, but it is still reference internally (most likely by the ID3D11Device ) because we specified one during creation.
As such, we only need to worry about live objects which has a Refcount greater than 0. From here on you will have to traverse your code, find instances of the specified objects, and check if they are indeed released upon destruction.
Conclusion
You now have learned how to use the debug device feature of DirectX to find, debug and fix memory leaks caused by DirectX related objects.
参考
[DX11调试]检查D3D对象是否释放:ReportLiveObjects()的用法