What's the difference between using GetAddressOf and & in this DX12 code?

157 views Asked by At

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?

1

There are 1 answers

0
Chuck Walbourn On

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.