Fer
Fer

Reputation: 460

Cannot release Direct3D buffer from swap chain for resize

I am struggling re-sizing a buffer when my windows changes. Every part of the process works as expected for exception of this one.

// in MyGame.h - 
private:
    ComPtr<ID3D11RenderTargetView> gRenderTarget;
...


// handle resize method:
void MyGame::UpdateBackbufferSize(UINT pWidth, UINT pHeight) {

    /* 1. Clear render targets from device context */
    gDeviceContext->OMSetRenderTargets(0, 0, 0);

    /* 2. Release Rendering Target */
    gRenderTarget->Release(); // ! CANNOT ACCESS client.h private Release()

    /* 3. Resize buffer */
    gSwapchain->ResizeBuffers(
        0,
        pWidth,
        pHeight,
        DXGI_FORMAT_UNKNOWN,
        0
        );

    /* 4. Reset the buffer as target view */ 
    ComPtr<ID3D11Texture2D> backBuffer;
    gSwapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), &backBuffer);
    gDevice->CreateRenderTargetView(backBuffer.Get(), nullptr,     &gRenderTarget);
    backBuffer.Get()->Release();

    /* 5. Set the new render target 
    gDeviceContext->OMSetRenderTargets(1, gRenderTarget.GetAddressOf(), nullptr);

    /* 6. Reset view port */
    D3D11_VIEWPORT vp = { 0 };
    vp.Width = pWidth;
    vp.Height = pHeight;
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0;
    vp.TopLeftY = 0;
    gDeviceContext->RSSetViewports(1, &vp);

}

If I try

gRenderTarget->Release(); 

I would get a "cannot access Release() from "RemoveIUnknownBase" ... inaccessible...

I also tried

ID3D11RenderTargetView* rtv = gRenderTarget.Get();
rtv->Release();

but I get some sort of access violation... I don't get it.

This is what is happening:

Right after start up:

enter image description here

After resizing:

enter image description here

View port changes, yet the buffer remains the same.

Besides of @Chuck suggestions, I also got a global ComPtr for the back buffer to call .Reset() on it as well, just in case, right after calling .Reset() on gRenderTarget

Thanks.

Upvotes: 1

Views: 2725

Answers (2)

Chuck Walbourn
Chuck Walbourn

Reputation: 41057

First, if you enabled the Direct3D Debug device, you would've gotten a debugger message which states the problem is that you have outstanding reference counts on your back-buffer. They should all be released so the count is zero before the swap chain is resized.

Second, you are failing to check the HRESULT of COM functions that return them. One of them failed, and you didn't check it so a later pointer was null. Always check the HRESULT value! If it was safe to ignore it, the function would return void. You can do this with the SUCCEEDED macro, the FAILED macro, or for fatal fast fail in programs built with C++ Exception Handling (/EHsc) enabled as is typical in universal Windows apps, you'd use a helper like DX::ThrowIfFailed. See also Error Handling in COM.

Third, you are following best practice by using a smart pointer instead of raw pointers with manual calls to Release. Microsoft::WRL::ComPtr is the natural choice for universal Windows apps. However, you don't ever call Release on them and you certainly never call Get()->Release. If you want to manually release a ComPtr, you use Reset.

The real nut of your problem is that gDeviceContext->OMSetRenderTargets(0, 0, 0); does not unbind the render target. This function takes an array of render targets for dealing with Multiple Render Targets. You are basically telling it "change the depth stencil view to null, but leave the render targets alone".

So with all that mind, you probably want something more like:

/* 1. Clear render targets from device context */
// Clear the previous window size specific context.
ID3D11RenderTargetView* nullViews [] = { nullptr };
gDeviceContext->OMSetRenderTargets(_countof(nullViews), nullViews, nullptr);

/* 2. Release Rendering Target */
gRenderTarget.Reset();
gDeviceContext->Flush();

/* 3. Resize buffer */
HRESULT hr = gSwapchain->ResizeBuffers(
    0,
    pWidth,
    pHeight,
    DXGI_FORMAT_UNKNOWN,
    0
    );

if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
{
    // If the device was removed for any reason, a new device and swap chain will need to be created.
    // TODO!
}
else
{
    DX::ThrowIfFailed(hr);
}

/* 4. Reset the buffer as target view */ 
ComPtr<ID3D11Texture2D> backBuffer;
DX::ThrowIfFailed(gSwapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), &backBuffer));
DX::ThrowIfFailed(gDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, &gRenderTarget));

/* 5. Set the new render target 
gDeviceContext->OMSetRenderTargets(1, gRenderTarget.GetAddressOf(), nullptr);

/* 6. Reset view port */
D3D11_VIEWPORT vp = { 0 };
vp.Width = pWidth;
vp.Height = pHeight;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
gDeviceContext->RSSetViewports(1, &vp);

I recommend you take a look at the Direct3D UWP Game VS template and the DirectX Tool Kit tutorials.

Note that you need to release all uses of the back-buffer before the Flush including references like from Direct2D.

Upvotes: 6

Muhammad Zeeshan
Muhammad Zeeshan

Reputation: 490

It should be

backBuffer->Release();

Upvotes: 0

Related Questions