Sample of 2D Triangle Rotation in C++ AMP

In my previous blog post on D3D interoperability with C++ AMP we looked at the APIs. In this blog post I will show a sample that uses those APIs

The attached zipped file, contains a Visual Studio 11 project of DirectX 3D sample which displays the rotation animation of a 2D triangle object.

The sample uses two of the interoperability APIs direct3d::create_accelerator_view() and direct3d:: get_buffer() to share the updated vertex positions computed in the C++ AMP code with the vertex shader in the D3D code. To keep it simple, we don’t use a geometry shader. The vertex shader and pixel shader are pretty simple as they only return the vertex positions and the color without further transformation.

In ComputeEngine.h file, the C++ AMP code is encapsulated in an AMP_compute_engine class. The DxInterOp.{h,cpp} and DxInterOpPsVs.hlsl contain the D3D code.

The C++ AMP code computes the rotation transformation to get the updated vertex positions using C++ AMP parallel_for_each () . The D3D code initializes the GUI window, creates and renders the vertex shader and the pixel shader. 

Here is the relevant code snippet using the 2 interoperability APIs:

AMP_compute_engine(ID3D11Device* d3ddevice)`` : m_accl_view(create_accelerator_view(d3ddevice))
{}

HRESULT get_data_d3dbuffer(void** d3dbuffer) const
{
return get_buffer(*m_data)->QueryInterface(__uuidof(ID3D11Buffer),
(LPVOID*)d3dbuffer);
}

To use the updated vertex position data in the vertex shader, the D3D code creates a shader resource view on the returned D3D buffer. In the VS11 Beta, the D3D buffer underling C++ AMP array is implemented as (RW)ByteAddressBuffer. This is important to know when creating the shader resource view. It also requires adjustments in the vertex shader when accessing the corresponding buffer.

Here is the code snippet to create the shader resource view for the VS11 Beta:

HRESULT CreateComputeShader() { … // Bind a resource view to the CS buffer D3D11_BUFFER_DESC descBuf; ZeroMemory( &descBuf, sizeof(descBuf) ); g_pVertexPosBuffer->GetDesc( &descBuf ); D3D11_SHADER_RESOURCE_VIEW_DESC DescRV; ZeroMemory( &DescRV, sizeof( DescRV ) ); DescRV.Format = DXGI_FORMAT_R32_TYPELESS`` ; DescRV.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX; DescRV.BufferEx.Flags = DX11_BUFFEREX_SRV_FLAG_RAW; DescRV.Buffer.FirstElement = 0; DescRV.Buffer.NumElements = descBuf.ByteWidth / sizeof(int); RETURN_IF_FAIL( g_pd3dDevice->CreateShaderResourceView( g_pVertexPosBuffer, &DescRV, &g_pVertexPosBufferRV ) ); }

The following code snippet is the vertex shader:

ByteAddressBuffer posBufVS : register( t0); float4 VS( uint id : SV_VERTEXID ) : SV_POSITION { float f0 = asfloat(posBufVS.Load(id*8+0)); float f1 = asfloat(posBufVS.Load(id*8+4)); return float4(f0, f1, 0.0f, 1.0f ); }

Where 8 equals to the sizeof(Vertex2D). Vertex2D is the element type used in the C++ AMP code defining the array to hold the vertex position.

struct Vertex2D { XMFLOAT2 Pos; // size of 8 bytes };

On every frame, the vertex position of the rotated triangle is computed in the C++ AMP code. The vertex shader and the pixel shader use the shared data to render the triangle.

So I hope you agree that replacing the compute shader in a DirectX 3D application with C++ AMP features can simplify your effort, and with the C++ AMP interoperability you can integrate C++ AMP code and DirectX 3D code seamlessly without sacrificing the benefits of resource sharing. Obviously, without the interoperability APIs, you would have had to copy-back-and-forth through CPU memory to communicate the data between the compute and rendering shaders.

You can download the sample in the zipped file, build and play it. I would love to hear your comments below.

DxInterOp.zip