Дополнительная ссылка на буфер цепочки обмена в ID3D11On12Device::AcquireWrappedResources()

Я заинтересован в использовании библиотеки "Direct 3D 11 on 12", но у меня возникают проблемы при изменении размера окна. В частности, я изменяю пример приложения DirectX 12 для Visual Studio.

Я создаю ID3D11On12Device сразу после того, как образец создает ID3D12CommandQueue:

ComPtr<ID3D11Device> d3d11Device;
IUnknown* queues[] = { m_commandQueue.Get() };
DX::ThrowIfFailed(D3D11On12CreateDevice(m_d3dDevice.Get(), D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, queues, 1, 0, d3d11Device.GetAddressOf(), m_d3d11DeviceContext.GetAddressOf(), nullptr));
DX::ThrowIfFailed(d3d11Device.As(&m_d3d11On12Device));

Затем, когда образец создает свои целевые представления рендеринга, я добавил создание обернутого ID3D11Resource:

for (UINT n = 0; n < c_frameCount; n++)
{
    // Visual studio template calls m_swapChain->GetBuffer() and m_d3dDevice->CreateRenderTargetView() here
    D3D11_RESOURCE_FLAGS d3d11Flags = { D3D11_BIND_RENDER_TARGET };
    DX::ThrowIfFailed(m_d3d11On12Device->CreateWrappedResource(m_renderTargets[n].Get(), &d3d11Flags, D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&m_wrappedBackBuffers[n])));
    rtvDescriptor.Offset(m_rtvDescriptorSize);

    ...
    // m_renderTargets[n]->SetName(), etc.
}

Затем, после создания остальных ресурсов D3D12, я тестирую ID3D11On12Device::AcquireWrappedResources() и ID3D11On12Device::ReleaseWrappedResources() (просто в качестве теста, чтобы попытаться сделать код максимально простым)

ID3D11Resource* resources[] = { m_wrappedBackBuffers[0].Get() };
m_d3d11On12Device->AcquireWrappedResources(resources, 1);
m_d3d11On12Device->ReleaseWrappedResources(resources, 1);

Пока что все работает, как и ожидалось. Проблема, однако, возникает при изменении размера окна. В частности, изменение размера вызывает IDXGISwapChain3::ResizeBuffers(). Когда это происходит, ResizeBuffers() возвращает ошибку, и на консоли отображается следующее сообщение:

DXGI ERROR: IDXGISwapChain::ResizeBuffers: Swapchain cannot be resized unless all outstanding buffer references have been released. [ MISCELLANEOUS ERROR #19: ]

Я пытался очистить ссылки m_wrappedBackBuffers перед ResizeBuffers():

for (UINT n = 0; n < c_frameCount; n++)
{
    m_renderTargets[n] = nullptr;
    m_wrappedBackBuffers[n] = nullptr;
}

но это, кажется, не имеет никакого эффекта. Я также пытался использовать Flush() и ClearState() ID3D11DeviceContext, но они, похоже, либо не действуют, либо выдают следующую ошибку:

D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists: A command list, which writes to a swapchain back buffer, may only be executed when that back buffer is the back buffer will be presented during the next call to Present*. Such a back buffer is also referred to as the "current back buffer". [ STATE_SETTING ERROR #907: EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE]
D3D12: Removing Device.

Закомментирование вызовов ID3D11On12Device::AcquireWrappedResources() и ID3D11On12Device:: ReleaseWrappedResources () заставляет ResizeBuffers () возвращаться успешно; но кажется, что вызов этих двух функций необходим, чтобы вообще иметь возможность использовать библиотеку.

Кажется, что где-то, ID3D11On12Device или ID3D11DeviceContext держится за ссылку на буферы цепочки подкачки, но я не смог найти никакой документации о том, как сбросить эту ссылку, не разрушив все устройство.

Эта проблема на GitHub, похоже, та же самая, что и у меня; однако ответ включает "вызов SetTarget (nullptr) для контекста D2D", но в этом проекте я вообще не касался Direct2D (а шаблон Visual Studio уже вызывает WaitForGpu() перед IDXGISwapChain3::ResizeBuffers()).

Образец Microsoft вообще не включает изменение размера буфера подкачки. Запуск образца приводит к тому, что цепочка обмена растягивается, чтобы соответствовать окну.

Я не нашел ничего об изменении размера в документации Microsoft.

1 ответ

Решение

Проблема, с которой вы столкнулись, заключается в том, что методы AcquireWrappedResource и ReleaseWrappedResource в конечном итоге ставят в очередь некоторую работу в непосредственном контексте D3D11. Семантика D3D11On12 требует, чтобы API Flush() явно вызывался, когда вы хотите перейти с D3D11 на D3D12, чтобы гарантировать, что все команды в очереди правильно записаны в списке команд D3D12, который затем закрывается и отправляется.

Если я правильно понял ваше описание, и вы просто вызываете Acquire / Release один раз после создания обернутого ресурса, тогда ваша проблема должна заключаться в простом вызове Flush() после Release(). Это гарантирует, что команды, которые ссылаются на обратный буфер 0, отправляются только тогда, когда обратный буфер 0 является текущим задним буфером цепочки обмена, что решает ошибку:

D3D12 ERROR: ID3D12CommandQueue::ExecuteCommandLists: A command list, which writes to a swapchain back buffer, may only be executed when that back buffer is the back buffer will be presented during the next call to Present*. Such a back buffer is also referred to as the "current back buffer". [ STATE_SETTING ERROR #907: EXECUTECOMMANDLISTS_WRONGSWAPCHAINBUFFERREFERENCE]

Затем, когда вы будете готовы изменить размер, следуйте инструкциям в разделе "Очистка" документации MS3 D3D11On12:

  1. Отпустите все ссылки на ресурс D3D11, включая любые виды, созданные на нем.
  2. Вызовите ID3D11DeviceContext::Flush() в непосредственном контексте.

После того, как вы выполнили обе эти вещи, вы сможете изменить размер цепочки обмена без каких-либо затяжных ссылок и без окончательного выполнения Flush(), вызывающего отправку команд в неподходящее время.

Другие вопросы по тегам