[Previous post]
2. Implement Linked List Creation
The OIT11 uses one 2D Texture and three buffers. Linked List OIT requires two buffers, one is a structured buffer which is called "Fragment Link Buffer". The other is a uint buffer which is called "Start Offset Buffer".
- Fragment Link Buffer
contains all fragments. Each fragment has a color, depth value and the index of the next fragment in a linked list. If there is no next fragment, the index will be a magic value, 0xffffffff in this case.
The declaration of a framgent in the pixel shader is written as follows. Color is packed to a uint value.
struct SFragment {
uint uColor;
float fDepth;
};
struct SFragmentLink {
SFragment fragment;
uint uNext;
};
- Start Offset Buffer
contains the index of the first fragment of a linked list at each pixel. It is initialized by a magic value before rendering at every frame.
2-a. Implement shader
Modify OIT_PS.hlsl and implement fragment link buffer creation.
Declare the buffers as follows.
RWStructuredBuffer FLBuffer<SFragmentLink> : register( u0 );
RWByteAddressBuffer StartOffsetBuffer : register( u1 );
Then implement the entry point function. It can be almost same as the code in the slide.
[earlydepthstencil]
void StoreFragments( SceneVS_Output input )
{
uint x = input.pos.x;
uint y = input.pos.y;
uint4 ucolor = saturate( input.color ) * 255;
SFragmentLink element;
element.fragment.uColor = (ucolor.x) | (ucolor.y << 8) | (ucolor.z << 16) | (ucolor.a << 24);
element.fragment.fDepth = input.pos.z;
uint uPixelCount = FLBuffer.IncrementCounter();
uint uIndex = y * g_nFrameWidth + x;
uint uStartOffsetAddress = 4 * uIndex;
uint uOldStartOffset;
StartOffsetBuffer.InterlockedExchange(
uStartOffsetAddress, uPixelCount, uOldStartOffset );
element.uNext = uOldStartOffset;
FLBuffer[uPixelCount] = element;
}
Note that the byte address buffer must be accessed by byte size.
2-b. Add Buffers and UAVs
Add two buffers and their UAVs. The following code is implemented in OIT::OnD3D11ResizedSwapChain function.
descBuf.StructureByteStride = sizeof(float) + sizeof(BYTE) * 4 * 2;
descBuf.ByteWidth = pBackBufferSurfaceDesc->Width * pBackBufferSurfaceDesc->Height * 8 * descBuf.StructureByteStride;
descBuf.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
V_RETURN( pDevice->CreateBuffer( &descBuf, NULL, &m_pFragmentLinkBuffer ));
descBuf.StructureByteStride = 4 * sizeof(BYTE);
descBuf.ByteWidth = pBackBufferSurfaceDesc->Width * pBackBufferSurfaceDesc->Height * descBuf.StructureByteStride;
descBuf.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
V_RETURN( pDevice->CreateBuffer( &descBuf, NULL, &m_pStartOffsetBuffer ));
D3D11_UNORDERED_ACCESS_VIEW_DESC descUAV;
descUAV.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
descUAV.Buffer.FirstElement = 0;
descUAV.Format = DXGI_FORMAT_UNKNOWN;
descUAV.Buffer.NumElements = pBackBufferSurfaceDesc->Width * pBackBufferSurfaceDesc->Height * 8;
descUAV.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_COUNTER;
V_RETURN( pDevice->CreateUnorderedAccessView( m_pFragmentLinkBuffer, &descUAV, &m_pFragmentLinkUAV ) );
descUAV.Format = DXGI_FORMAT_R32_TYPELESS;
descUAV.Buffer.NumElements = pBackBufferSurfaceDesc->Width * pBackBufferSurfaceDesc->Height;
descUAV.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
V_RETURN( pDevice->CreateUnorderedAccessView( m_pStartOffsetBuffer, &descUAV, &m_pStartOffsetUAV ) );
Note that descBuf.BindFlags contains D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE as the original code.
The fragment link buffer must contain all fragments. I specified the same size as the deep frame buffer in OIT11 sample (i.e. 8x screen size). Structured buffers must be created with D3D11_RESOURCE_MISC_BUFFER_STRUCTURED.
The start offset buffer is a screen-sized buffer and the UAV is used as a Byte address buffer. Specify D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS when create.
Specify D3D11_BUFFER_UAV_FLAG_COUNTER so that the UAV of the fragment link buffer has counter support. UAVs for structured buffers must be created with DXGI_FORMAT_UNKNOWN format.
2-c. Implement function
Implement OIT::CreateFragmentAndLink function. It is easy to do it by modifying OIT::FillDeepBuffer because you can use the same constant buffer as the original code.
Clear the start offset buffer by a magic value before rendering.
static const UINT clearValueUINT[1] = { 0xffffffff };
pD3DContext->ClearUnorderedAccessViewUint( m_pStartOffsetUAV, clearValueUINT );
ID3D11UnorderedAccessView* pUAVs[] = {
m_pFragmentLinkUAV,
m_pStartOffsetUAV,
};
UINT anInitIndices[] = { 0, 0 };
pD3DContext->OMSetRenderTargetsAndUnorderedAccessViews( 0, NULL, pDSV, 0, sizeof(pUAVs)/sizeof(pUAVs[0]), pUAVs, anInitIndices );
pD3DContext->PSSetShader( m_pCreateFragmentLinkPS, NULL, 0 );
HRESULT hr;
D3D11_MAPPED_SUBRESOURCE MappedResource;
V( pD3DContext->Map( m_pPS_CB, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedResource ) );
PS_CB* pPS_CB = ( PS_CB* )MappedResource.pData;
pPS_CB->nFrameWidth = m_nFrameWidth;
pPS_CB->nFrameHeight = m_nFrameHeight;
pD3DContext->Unmap( m_pPS_CB, 0 );
pD3DContext->PSSetConstantBuffers( 0, 1, &m_pPS_CB );
pScene->D3D11Render( mWorldViewProjection, pD3DContext );
ID3D11UnorderedAccessView* pUAVsNULL[] = { NULL, NULL, NULL, NULL };
pD3DContext->OMSetRenderTargetsAndUnorderedAccessViews( 0, NULL, pDSV, 0, sizeof(pUAVs)/sizeof(pUAVs[0]), pUAVsNULL, NULL );
Don't forget to initialize the counter value when setting the UAVs.
If you specify [earlydepthstencil] to the shader, you have to disable deth write before rendering. Otherwise some fragments will be rejected by depth test.
[ To be continued... ]