1

I have the following piece of code from Frank Luna's DX12 book:

D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
ThrowIfFailed(md3dDevice->CreateCommandQueue(
    &queueDesc, IID_PPV_ARGS(&mCommandQueue)));
ThrowIfFailed(md3dDevice->CreateCommandAllocator(
    D3D12_COMMAND_LIST_TYPE_DIRECT,
    IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));
ThrowIfFailed(md3dDevice->CreateCommandList(
    0,
    D3D12_COMMAND_LIST_TYPE_DIRECT,
    mDirectCmdListAlloc.Get(), // Associated command allocator
    nullptr, // Initial PipelineStateObject
    IID_PPV_ARGS(mCommandList.GetAddressOf())));
// Start off in a closed state. This is because the first time we
// refer to the command list we will Reset it, and it needs to be
// closed before calling Reset.
mCommandList->Close();

What I'm wondering is why does he use the & operator when passing the Queue ComPtr but uses the GetAddressOf method for the other two?

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • 1
    Documentation is here : https://learn.microsoft.com/en-us/cpp/cppcx/wrl/comptr-class?view=msvc-170#operator-ampersand *This method (operator &) differs from ComPtr::GetAddressOf in that this method releases a reference to the interface pointer. Use ComPtr::GetAddressOf when you require the address of the interface pointer but don't want to release that interface.* – Simon Mourier Aug 17 '23 at 05:38
  • 1
    @SimonMourier and in which use cases would I need a reference and release the interface at the same time? Like for example in the code excerpt, why does the author release the command queue interface? – Francisco José Letterio Aug 18 '23 at 00:56
  • For COM pointers, the operator `&` is equivalent to `ComPtr::ReleaseAndGetAddressOf`. This is the preferred behaviour when filling or re-filling the COM pointer when acquiring resources or re-acquiring them in the event of a device lost event. It would seem that Luna is using `::GetAddressOf` and `::ReleaseAndGetAddressOf` in this context interchangeably, which I agree isn't a good example of best practice. The reason it probably went unnoticed is that it doesn't result in a compile or runtime error in the book's example programs. – Maico De Blasio Aug 18 '23 at 04:11
  • 1
    @FranciscoJoséLetterio - as will all smart pointers, you're not supposed to use the raw pointer beneath (except for "read only" type scenarios like debugging for example). Here mDirectCmdListAlloc and mCommandList have probably never been attached since the methods are called "CreateXXX"), so they are brand new ComPtr, it's not an issue, but it's still bad practise, he should still use &. – Simon Mourier Aug 18 '23 at 06:11

1 Answers1

1

With Microsoft::WRL::ComPtr I prefer the use of ReleaseAndGetAddressOf and GetAddressOf rather than using the operator& overload. There's a few reasons for this.

The older ATL CComPtr when using operator& is equivalent to GetAdddressOf with an assert to assume it was nullptr to being with. The WRL ComPtr uses ReleaseAndGetAddressOf which is the safer default to avoid potential resource leaks, but can also cause confusion when you can end up with 'nullptr' results when you didn't expect it such as trying to pass a pointer-to-a-pointer often used in Direct3D APIs.

This design point was driven by a common pattern in all COM code:

ComPtr<IWICImagingFactory> ifactory;
hr = CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_PPV_ARGS(&ifactory));

With ATL CComPtr, this only works if ifactory is in fact nullptr (always true for a local variable but not necessarily for global or class member variables). The WRL ComPtr will always check for non-null and release it if needed, which is more robust but may be unnecessary as in the case of a local variable.

The choice, however, makes for problems for Direct3D usage of COM interfaces. For example, the following code is NOT going to work correctly with WRL ComPtr (the RTV will be cleared before you try to set it) and will assert with ATL's CComPtr in DEBUG builds.

context->OMSetRenderTargets(1, &m_renderTargetView,
    m_depthStencilView.Get());

You can get it to work as:

context->OMSetRenderTargets(1, m_renderTargetView.GetAddressOf(),
    m_depthStencilView.Get());

But it's probably even clearer to the future reader to do:

auto rt = m_renderTargetView.Get();
context->OMSetRenderTargets(1, &rt, m_depthStencilView.Get());

In the code snippet from Luna above, it's a safer choice to use .ReleaseAndGetAddressOf since the method being called is a creation fucntion and the member variable could already be set. This would avoid memory leaks even when the class is 'misused. I prefer to use .GetAddressOf if I'm initializing a local variable that I know was just created so it's always nullptr.

See this wiki page for more advice.

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81