Search code examples
c++memory-leaksdirect3d12

D3D12 unavoidable leak report


This program:

#include <d3d12.h>
#pragma comment(lib,"d3d12")

int main()
{
    ID3D12Debug *pDebug = NULL;
    D3D12GetDebugInterface(__uuidof(ID3D12Debug),(void**)&pDebug);
    pDebug->EnableDebugLayer();
    pDebug->Release();

    ID3D12Device *pDev = NULL;
    D3D12CreateDevice(NULL,D3D_FEATURE_LEVEL_12_1,__uuidof(ID3D12Device),(void**)&pDev);

    ID3D12DebugDevice *pDebugDevice = NULL;
    pDev->QueryInterface(&pDebugDevice);
    pDev->Release();
    pDebugDevice->ReportLiveDeviceObjects(D3D12_RLDO_DETAIL);
    pDebugDevice->Release();
}

gives this in the debug output:

D3D12 WARNING: Live ID3D12Device at 0x000C6BA8, Refcount: 2 [ STATE_CREATION WARNING #274: LIVE_DEVICE]
D3D12 WARNING:  Live ID3D12RootSignature at 0x000E62E8, Refcount: 0, IntRef: 2 [ STATE_CREATION WARNING #577: LIVE_ROOTSIGNATURE]
D3D12 WARNING:  Live ID3D12PipelineState at 0x0011C3C8, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #572: LIVE_PIPELINESTATE]
D3D12 WARNING:  Live ID3D12PipelineState at 0x001421D8, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #572: LIVE_PIPELINESTATE]
D3D12 WARNING:  Live ID3D12Resource at 0x00138FF8, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #575: LIVE_RESOURCE]
D3D12 WARNING:  Live ID3D12Heap at 0x00144DD8, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #579: LIVE_HEAP]

The debug device is reporting that the D3D12 device I created is still alive even after I release it. I know this is indeed true because the debug device itself is actually the only remaining referrer that is keeping the D3D12 device alive, but that is not a leak from my perspective, as I have correctly released my D3D12 device. This is just pollution to my program's output giving the false indication that I have a bug in my code.

My question is: am I indeed doing something wrong here? or is it a bad behavior in how reporting works in the D3D12 debug device? Any ideas on how to solve it?

Thanks!


Solution

  • You should try using the D3D12_RLDO_IGNORE_INTERNAL flag to ignore those items that have a RefCount of 0 but still have an IntRef.

    I prefer to use the DXGI debug device reporting instead of Direct3D's for the 'clean shutdown' scenario.

    In my DeviceResources implementation, I create the DXGI device as follows:

        m_dxgiFactoryFlags = 0;
    
    #if defined(_DEBUG)
        // Enable the debug layer (requires the Graphics Tools "optional feature").
        //
        // NOTE: Enabling the debug layer after device creation will invalidate the active device.
        {
            ComPtr<ID3D12Debug> debugController;
            if (SUCCEEDED(D3D12GetDebugInterface(IID_PPV_ARGS(debugController.GetAddressOf()))))
            {
                debugController->EnableDebugLayer();
            }
            else
            {
                OutputDebugStringA("WARNING: Direct3D Debug Device is not available\n");
            }
    
            ComPtr<IDXGIInfoQueue> dxgiInfoQueue;
            if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(dxgiInfoQueue.GetAddressOf()))))
            {
                m_dxgiFactoryFlags = DXGI_CREATE_FACTORY_DEBUG;
    
                dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, true);
                dxgiInfoQueue->SetBreakOnSeverity(DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, true);
            }
        }
    #endif
    
        ThrowIfFailed(CreateDXGIFactory2(m_dxgiFactoryFlags, IID_PPV_ARGS(m_dxgiFactory.ReleaseAndGetAddressOf())));
    

    Then when I do shutdown and want to check for 'leaks':

    #ifdef _DEBUG
        {
            ComPtr<IDXGIDebug1> dxgiDebug;
            if (SUCCEEDED(DXGIGetDebugInterface1(0, IID_PPV_ARGS(&dxgiDebug))))
            {
                dxgiDebug->ReportLiveObjects(DXGI_DEBUG_ALL, DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_IGNORE_INTERNAL));
            }
        }
    #endif
    

    For Win32 apps, there's a clear time to do 'clean exit' report leaks. For UWP apps, lifetime is controlled by PLM so you don't ever get a 'clean exit'--the process is just terminated after a suspend. For UWP, the best place to do leak checks is in the "device lost" handler caused by device removal errors.