Невозможно освободить буфер Direct3D из цепочки подкачки для изменения размера
Я пытаюсь изменить размер буфера, когда мои окна меняются. Каждая часть процесса работает, как и ожидалось, за исключением этого.
// 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();
Я хотел бы получить "не могу получить доступ к Release() из"RemoveIUnknownBase" ... недоступный...
Я тоже пробовал
ID3D11RenderTargetView* rtv = gRenderTarget.Get();
rtv->Release();
но я получаю какое-то нарушение доступа... я не понимаю.
Вот что происходит:
Сразу после запуска:
После изменения размера:
Просмотр изменений порта, но буфер остается прежним.
Помимо предложений @Chuck, я также получил глобальный ComPtr для заднего буфера для вызова.Reset() для него, на всякий случай, сразу после вызова.Reset() для gRenderTarget
Благодарю.
2 ответа
Во-первых, если вы включили устройство Direct3D Debug, вы бы получили сообщение отладчика, в котором говорится, что проблема в том, что у вас есть выдающиеся подсчеты ссылок в вашем обратном буфере. Все они должны быть освобождены, так что счетчик будет равен нулю, прежде чем размер цепи свопа будет изменен.
Во-вторых, вы не можете проверить HRESULT функций COM, которые их возвращают. Один из них не удался, и вы не проверяли его, поэтому более поздний указатель был нулевым. Всегда проверяйте значение HRESULT! Если было безопасно игнорировать это, функция вернула бы void. Вы можете сделать это с SUCCEEDED
макрос, FAILED
макрос, или для фатального быстрого сбоя в программах, созданных с C++ Exception Handling (/EHsc
), как обычно в универсальных приложениях Windows, вы должны использовать помощника, такого как DX:: ThrowIfFailed. Смотрите также Обработка ошибок в COM.
В-третьих, вы следуете передовой практике, используя умные указатели вместо необработанных указателей с ручными вызовами Release
, Microsoft::WRL::ComPtr - естественный выбор для универсальных приложений Windows. Тем не менее, вы никогда не звоните Release
на них, и вы, конечно, никогда не звоните Get()->Release
, Если вы хотите вручную разблокировать ComPtr, вы используете Reset
,
Настоящая гайка вашей проблемы в том, что gDeviceContext->OMSetRenderTargets(0, 0, 0);
не отменяет привязку цели рендеринга Эта функция принимает массив целей рендеринга для работы с несколькими целями рендеринга. Вы в основном говорите: "измените представление трафарета глубины на ноль, но оставьте цели рендеринга в покое".
Таким образом, со всем этим разумом, вы, вероятно, хотите что-то вроде:
/* 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.
Обратите внимание, что вам нужно прекратить все использования заднего буфера до того, как Flush
в том числе ссылки, как из Direct2D.