无法从交换链释放 Direct3D 缓冲区以调整大小
Cannot release Direct3D buffer from swap chain for resize
当我的 windows 发生变化时,我正在努力调整缓冲区大小。该过程的每个部分都按预期工作,但此部分除外。
// 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();
我会得到一个 "cannot access Release() from "RemoveIUnknownBase" ...无法访问...
我也试过了
ID3D11RenderTargetView* rtv = gRenderTarget.Get();
rtv->Release();
但是我遇到了某种访问冲突...我不明白。
这是正在发生的事情:
启动后:
调整大小后:
查看端口改变,但缓冲区保持不变。
除了@Chuck 的建议之外,我还得到了一个全局 ComPtr 用于后缓冲区调用 .Reset(),以防万一,在 gRenderTarget
上调用 .Reset() 之后
谢谢。
应该是
backBuffer->Release();
首先,如果您 启用了 Direct3D Debug device,您会收到一条调试器消息,指出问题是您的 back-buffer。它们都应该被释放,以便在调整交换链大小之前计数为零。
其次,您没有检查 return 它们的 COM 函数的 HRESULT。其中一个失败了,你没有检查它所以后面的指针是空的。
始终检查 HRESULT 值! 如果忽略它是安全的,该函数将 return 失效。您可以使用 SUCCEEDED
宏、FAILED
宏或在使用 C++ 异常处理 (/EHsc
) 构建的程序中实现致命快速失败,这在通用 Windows 应用程序,你会使用像 DX::ThrowIfFailed. See also Error Handling in COM.
这样的助手
第三,您遵循最佳实践,使用智能指针而不是手动调用 Release
的原始指针。 Microsoft::WRL::ComPtr 是通用 Windows 应用程序的自然选择。 但是,您永远不会对它们调用 Release
,您当然也永远不会调用 Get()->Release
。如果你想手动释放一个ComPtr,你使用Reset
.
您的问题的真正要点是 gDeviceContext->OMSetRenderTargets(0, 0, 0);
不会 取消绑定渲染目标。此函数采用一组渲染目标来处理多个渲染目标。你基本上是在告诉它 "change the depth stencil view to null, but leave the render targets alone".
所以考虑到所有这些,您可能想要更像的东西:
/* 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.
请注意,您需要在 Flush
之前释放对 back-buffer 的所有使用,包括来自 Direct2D 的引用。
当我的 windows 发生变化时,我正在努力调整缓冲区大小。该过程的每个部分都按预期工作,但此部分除外。
// 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();
我会得到一个 "cannot access Release() from "RemoveIUnknownBase" ...无法访问...
我也试过了
ID3D11RenderTargetView* rtv = gRenderTarget.Get();
rtv->Release();
但是我遇到了某种访问冲突...我不明白。
这是正在发生的事情:
启动后:
调整大小后:
查看端口改变,但缓冲区保持不变。
除了@Chuck 的建议之外,我还得到了一个全局 ComPtr 用于后缓冲区调用 .Reset(),以防万一,在 gRenderTarget
上调用 .Reset() 之后谢谢。
应该是
backBuffer->Release();
首先,如果您 启用了 Direct3D Debug device,您会收到一条调试器消息,指出问题是您的 back-buffer。它们都应该被释放,以便在调整交换链大小之前计数为零。
其次,您没有检查 return 它们的 COM 函数的 HRESULT。其中一个失败了,你没有检查它所以后面的指针是空的。
始终检查 HRESULT 值! 如果忽略它是安全的,该函数将 return 失效。您可以使用 SUCCEEDED
宏、FAILED
宏或在使用 C++ 异常处理 (/EHsc
) 构建的程序中实现致命快速失败,这在通用 Windows 应用程序,你会使用像 DX::ThrowIfFailed. See also Error Handling in COM.
第三,您遵循最佳实践,使用智能指针而不是手动调用 Release
的原始指针。 Microsoft::WRL::ComPtr 是通用 Windows 应用程序的自然选择。 但是,您永远不会对它们调用 Release
,您当然也永远不会调用 Get()->Release
。如果你想手动释放一个ComPtr,你使用Reset
.
您的问题的真正要点是 gDeviceContext->OMSetRenderTargets(0, 0, 0);
不会 取消绑定渲染目标。此函数采用一组渲染目标来处理多个渲染目标。你基本上是在告诉它 "change the depth stencil view to null, but leave the render targets alone".
所以考虑到所有这些,您可能想要更像的东西:
/* 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.
请注意,您需要在 Flush
之前释放对 back-buffer 的所有使用,包括来自 Direct2D 的引用。