Reputation: 460
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:
After resizing:
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
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