Saturday 28 December 2013

Updating Vertex Buffers in DX11: StreamOut vs Compute

Benchmarking an update of 125,000 vertices (xyz float) using double-buffered StreamOut compared to compute with a single buffer.

The compute C++ code is:
 dx11::setUnorderedAccessView(effect,"targetVertexBuffer",vertexBuffer.unorderedAccessView);
 ApplyPass(pContext,effect->GetTechniqueByName("move_particles_compute")->GetPassByIndex(0));
 pContext->Dispatch(5,5,5);

The StreamOut C++ code is:

 pContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );
 pContext->IASetInputLayout( m_pVtxDecl );
    
 ID3D11Buffer *pBuffer;
 pBuffer    =vertexBuffer.vertexBuffer;
 UINT stride   = sizeof(vec3);
 UINT offset[1]   = { 0 };
 pContext->IASetVertexBuffers( 0, 1, &pBuffer,&stride, offset );

 // Point to the correct output buffer
 pContext->SOSetTargets( 1, &m_pVertexBufferSwap, offset );

 // draw
 D3DX11_TECHNIQUE_DESC techDesc;
 techniqueMoveParticles->GetDesc( &techDesc );
 techniqueMoveParticles->GetPassByIndex(0)->Apply(0,pContext);

 pContext->Draw(125000 , 0 );

 // Get back to normal
 pBuffer    = NULL;
 pContext->SOSetTargets( 1, &pBuffer, offset );

 // Swap buffers
 ID3D11Buffer* pTemp  = m_pVertexBufferSwap;
 m_pVertexBufferSwap  = vertexBuffer.vertexBuffer;
 vertexBuffer.vertexBuffer = pTemp;

And the results: the compute operation takes 0.014ms, the geometry shader one takes 0.16ms. But: it turns out that only StreamOut is actually updating all of the vertices. Compute updates about 8000, then silently gives up.

Turning on Debug using DirectX Control Panel, we learn that passing a vertex buffer's UAV to a shader as a structured buffer is NOT supported. So StreamOut is actually the only option here.

Tuesday 26 November 2013

UnorderedAccessView of a vertex buffer

Here's how to create a vertex buffer that you can write with a compute shader:

D3D11_BUFFER_DESC desc =
    {
        40000*sizeof(vec3)
        ,D3D11_USAGE_DEFAULT
        ,D3D11_BIND_VERTEX_BUFFER|D3D11_BIND_UNORDERED_ACCESS
        ,0  // NoCPU access
 ,0  // not D3D11_RESOURCE_MISC_BUFFER_STRUCTURED - why?
 ,sizeof(vec3)   //StructureByteStride
 };
    m_pd3dDevice->CreateBuffer(&desc,NULL,&m_pVertexBuffer);


    D3D11_UNORDERED_ACCESS_VIEW_DESC uav_desc;
    ZeroMemory(&uav_desc, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
    uav_desc.Format   =DXGI_FORMAT_R32_FLOAT;    // Must be this format, or INVALID_ARG will occur
    uav_desc.ViewDimension  =D3D11_UAV_DIMENSION_BUFFER;
    uav_desc.Buffer.NumElements  =40000;
    V_CHECK(m_pd3dDevice->CreateUnorderedAccessView(m_pVertexBuffer, &uav_desc, &unorderedAccessView));

Managed C++/CLI references

In Visual Studio, after adding a reference to a managed .dll, you need to unload the project, edit the .vcxproj, then:

1. Change the absolute to relative paths, using environment variables.
2. Remove the version information, otherwise it will refuse to recognize updates.