无法从交换链中释放Direct3D缓冲区以调整大小

时间:2016-01-10 22:02:27

标签: resize uwp directx-11 direct3d11

当我的窗户改变时,我正在努力重新调整缓冲区的大小。除了这个过程外,该过程的每个部分都按预期工作。

// 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);

}

如果我尝试

gRenderTarget->Release(); 

我会得到一个“无法从”RemoveIUnknownBase“访问Release()......无法访问......

我也试过

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

但是我遇到了某种违规行为......我不明白。

这就是发生的事情:

启动后立即:

enter image description here

调整大小后:

enter image description here

查看端口更改,但缓冲区保持不变。

除了@Chuck建议之外,我还有一个全局ComPtr用于后台缓冲区,也可以在它上面调用.Reset(),以防万一,在gRenderTarget上调用.Reset()之后

感谢。

2 个答案:

答案 0 :(得分:3)

首先,如果您启用了Direct3D Debug device ,您将收到一条调试器消息,指出问题是您的后台缓冲区上有未完成的引用计数。它们都应该被释放,因此在交换链调整大小之前计数为零。

其次,您未能检查返回它们的COM函数的HRESULT。其中一个失败了,你没有检查它,所以后来的指针是null。 始终检查HRESULT值!如果忽略它是安全的,该函数将返回void。您可以使用SUCCEEDED宏,FAILED宏或使用C ++异常处理(/EHsc)构建的程序中的致命快速失败来执行此操作,这在通用Windows应用程序中是典型的,您'使用像DX::ThrowIfFailed这样的助手。另请参阅Error Handling in COM

第三,您遵循最佳做法,使用智能指针而不是手动调用Release的原始指针。 Microsoft::WRL::ComPtr是通用Windows应用程序的自然选择。 但是,您永远不会在其上拨打Release,而且您绝对不会致电Get()->Release 。如果要手动释放ComPtr,请使用Reset

问题的真正原因是gDeviceContext->OMSetRenderTargets(0, 0, 0); 取消绑定渲染目标。此函数采用一系列渲染目标来处理多个渲染目标。您基本上是在告诉它“将深度模板视图更改为null,但保留渲染目标”。

所有这一切,你可能想要更像的东西:

/* 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);
  

我建议您查看Direct3D UWP Game VS模板和DirectX Tool Kit tutorials

请注意,您需要在Flush之前释放后缓冲区的所有用法,包括来自Direct2D的引用。

答案 1 :(得分:0)

应该是

backBuffer->Release();