Search code examples
comdirectx-11

Proper usage of ComPtr in DirectX code


I am writing a DirectX 11 based engine. Where all of the DirectX Com objects are wrapped in Microsoft::WRL::ComPtr. But unfortunately when I am calling ID3D11Debug::ReportLiveDeviceObjects on shutdown, it is reporting more the reference count for more than 1 objects are non-zero.

I am confused, which code actually increasing those reference count preventing teh reference to be zero at the end. For an example, I am providing this example usage of ID3D11ShaderResourceView in my code:

The ID3D11ShaderResourceView was declared in my render target class as member,

Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> m_pShaderResourceView

It was initialized in to nullptr in the constructor:

m_pShaderResourceView(wwNULLPTR)

The interface was created as follows:

device->CreateShaderResourceView(m_pDXTexture.Get(), &shaderResourceDesc, m_pShaderResourceView.ReleaseAndGetAddressOf()); 

The interface was used in GenerateMips function:

deviceContext->GenerateMips(m_pShaderResourceView.Get());

There is a getter function for the interface:

ID3D11ShaderResourceView* GetShaderResourceView() 
{ 
     return m_pShaderResourceView.Get(); 
}

and at last the interface was reset in desctructor:

m_pShaderResourceView.Reset();

At the end I am getting at least 8 reference in live object summary.

My questions are:

Which part of my code is actually increasing the reference count? Why calling Reset is not making the reference zero? Probably I am missing something?

Thanks.


Solution

  • ID3D11Debug::ReportLiveDeviceObjects is a very useful debugging tool, but it does have some quirks. Specifically a few objects have lives you don't directly control and are part of the ID3D11Device itself. You can't get rid of the references without closing the device, at which point you can't get a live objects report.

    In the DXUT for Direct3D 11 framework, I get it down as much as possible by calling ClearState and then Flush on the immediate context just before calling report live objects.

    Even in a 'clean' exit you will still have a few lingering "live" objects:

    D3D11 WARNING: Live ID3D11Device at 0x025CA03C, Refcount: 5 [ STATE_CREATION WARNING #441: LIVE_DEVICE]
    D3D11 WARNING:  Live ID3D11Context at 0x02831030, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #2097226: LIVE_CONTEXT]
    D3D11 WARNING:  Live ID3DDeviceContextState at 0x02828038, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #3145742: LIVE_DEVICECONTEXTSTATE]
    D3D11 WARNING:  Live ID3D11BlendState at 0x005B766C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #435: LIVE_BLENDSTATE]
    D3D11 WARNING:  Live ID3D11DepthStencilState at 0x0283ECAC, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #436: LIVE_DEPTHSTENCILSTATE]
    D3D11 WARNING:  Live ID3D11RasterizerState at 0x027006F4, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #437: LIVE_RASTERIZERSTATE]
    D3D11 WARNING:  Live ID3D11Sampler at 0x0270082C, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #434: LIVE_SAMPLER]
    D3D11 WARNING:  Live ID3D11Query at 0x005BB904, Refcount: 0, IntRef: 1 [ STATE_CREATION WARNING #438: LIVE_QUERY]
    D3D11 WARNING: Live                  ID3D11Context :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    D3D11 WARNING: Live         ID3DDeviceContextState :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    D3D11 WARNING: Live               ID3D11BlendState :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    D3D11 WARNING: Live        ID3D11DepthStencilState :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    D3D11 WARNING: Live          ID3D11RasterizerState :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    D3D11 WARNING: Live                  ID3D11Sampler :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    D3D11 WARNING: Live                    ID3D11Query :      1 [ STATE_CREATION WARNING #422: LIVE_OBJECT_SUMMARY]
    

    These are the 'default' objects used by the runtime itself. Ideally the debug runtime would ignore them when generating this report, but it doesn't.

    That said, as you have 8 instead of 5 live objects, you may have something lingering on. First try the ClearState and Flush to make sure you aren't having anything kept alive by the virtue of being bound to the pipeline or due to deferred destruction. If there are still objects left, the next step in finding them is to make use of "Debug Object Naming" so you can figure out what objects you control are still alive. The debug naming can be done using either a macro or a template:

    Template

    template<UINT TNameLength>
    inline void SetDebugObjectName(_In_ ID3D11DeviceChild* resource, _In_z_ const char (&name)[TNameLength])
    {
        #if defined(_DEBUG)
           resource->SetPrivateData(WKPDID_D3DDebugObjectName, TNameLength - 1, name);
        #else
            UNREFERENCED_PARAMETER(resource);
            UNREFERENCED_PARAMETER(name);
        #endif
    }
    

    Macro

    #ifdef _DEBUG
    
    inline void DXUT_SetDebugName( _In_ ID3D11DeviceChild* pObj,
        _In_z_ const CHAR* pstrName )
    {
        if ( pObj )
            pObj->SetPrivateData( WKPDID_D3DDebugObjectName,
                (UINT)strlen(pstrName), pstrName );
    }
    #else
    #define DXUT_SetDebugName( pObj, pstrName )
    #endif
    

    You need to link to dxguid.lib to get the symbol WKPDID_D3DDebugObjectName. "WKPDID" stands for Well Known Private Data ID. It's ironic since it isn't all that 'well known'.

    See Object Naming and Direct3D SDK Debug Layer Tricks