Search code examples
directxframebufferrendertarget

How to create render target views on swap chain with multiple frame buffers in directx 11


I'm trying to implement a swap chain with more than 1 back buffer, but I'm having troubles creating render target views for any buffer after the zero-th.

I create my swap chain like so:

IDXGIFactory1* idxgiFactory;
// D3D_CALL is just a macro that throws exception with info on error
D3D_CALL(CreateDXGIFactory1(__uuidof(IDXGIFactory1), &idxgiFactory));

DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferCount = BUFFER_COUNT; // currently 2
sd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 0;
sd.BufferDesc.RefreshRate.Denominator = 0;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | D3D11_BIND_RENDER_TARGET;
sd.OutputWindow = window.GetHandle(); // wrapper for my window
sd.SampleDesc.Count = 4;
sd.SampleDesc.Quality = 1;
sd.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
sd.Windowed = TRUE;

D3D_CALL(idxgiFactory->CreateSwapChain(
    m_HWDevice, // ptr to ID3D11Device
    &sd,
    &m_HWSwapChain));

It was working till now with single frame buffer with swap effect DISCARD, which is outdated and performs poorly according to MSDN.

After I create the swap chain I get the backbuffers and create views like so:

// this is called with buffer index from 0 till BUFFER_COUNT - 1
// 'm_RenderTarget' is simply an array of ID3D11Texture2D, where the size matches BUFFER_COUNT
D3D_CALL(m_HWSwapChain->GetBuffer(bufferIndex, __uuidof(ID3D11Texture2D), (LPVOID*)&m_RenderTarget[bufferIndex]));

// I then attempt to create the RTV like so:
ID3D11RenderTargetView* rtv = NULL;
D3D_CALL(m_HWDevice->CreateRenderTargetView(m_RenderTarget[bufferIndex], NULL, &rtv));

The code about creating render target views works fine for 'bufferIndex' 0, but on index 1 I get following error:

D3D11 ERROR: ID3D11Device::CreateRenderTargetView: A render-target view cannot be made on a read-only resource. (Perhaps a DXGI_SWAP_CHAIN buffer other than buffer 0?) [ STATE_CREATION ERROR #135: CREATERENDERTARGETVIEW_INVALIDDESC]

I assume from this I have to use a D3D11_RENDER_TARGET_VIEW_DESC and fill the D3D11_BUFFER_RTV structure inside? No idea how to setup this though and could not find any examples.

I tried to create RTV with descriptor like so:

D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
ZeroMemory(&rtvDesc, sizeof(rtvDesc));
rtvDesc.Buffer.NumElements = BUFFER_COUNT;
rtvDesc.Buffer.FirstElement = 0;
rtvDesc.Buffer.ElementOffset = size.x * size.y * 4; // dimensions of my back buffer * 4 bytes per pixel
rtvDesc.Buffer.ElementWidth = 4; // 4 bytes per pixel in DXGI_FORMAT_B8G8R8A8_UNORM

This gives error:

D3D11 ERROR: ID3D11Device::CreateRenderTargetView: The ViewDimension in the View Desc incompatible with the type of the Resource. [ STATE_CREATION ERROR #129: CREATESHADERRESOURCEVIEW_INVALIDRESOURCE]

Not sure what I'm missing here.


Solution

  • I had a bit of a misunderstanding how this works. Some key pieces of information I was missing:

    *

    If the swap chain's swap effect is either DXGI_SWAP_EFFECT_SEQUENTIAL or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, only the swap chain's zero-index buffer can be read from and written to. The swap chain's buffers with indexes greater than zero can only be read from; so if you call the IDXGIResource::GetUsage method for such buffers, they have the DXGI_USAGE_READ_ONLY flag set.

    Source: https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-getbuffer

    Another key point is that dx11 manages frame buffers automatically. I was trying to get all frame buffers and write to them accordingly. Apperently I only ever need the 0th frame buffer once, create a view to it and only care about that. The rest will be managed automatically behind the scenes by dx11. That is not the case for dx12.

    Related question: Does the backbuffer that a RenderTargetView points to automagically change after Present?