Невозможно освободить буфер 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.

Так должно быть

backBuffer->Release();
Другие вопросы по тегам