Accessing Resource Handles and Device Handle

Although writing device or draw API specific code is discouraged, access is sometimes necessary when device spe­cific information is required. An example of this is if a plug-in wishes to perform some operations using GPU com­pute.

The draw API specific information can be obtained by accessing a handle to a resource. Handles can be accessed at the following levels:

The recommended practice is for a plug-in to create all of the required resources via the API and extract the appropriate GPU references.

As long as the plug-in holds on to a reference to the API wrapper, there is a reference to it and it should not disappear. Due to resource management handling, it is always best to re-query the handle in case it has changed. Resource handles should not change within a single frame render.

It is the responsibility of the plug-in to manage any resources which are created directly on the GPU (with­out going through the API). Additionally, this data cannot be used to create resources via the exposed API wrapper constructs.

MPxShaderOverride is an interface which commonly accesses GPU data. Both the CgFx and the dx11Shader SDK plug-ins show examples of how to access raw data in order to either bind parameters to shaders or to bind geometry for drawing. Additionally, the plug-ins include code to access the GPU context to create resources which are not tracked by the internal framework.

DirectCompute Example

One possible way to use GPU compute is for supporting procedurally generated geometry. To set this up, a plug-in can be written which adds a custom DAG node (for example, MPxLocatorNode) along with an asso­ciated draw override (MPxDrawOverride). Within the draw routine for the override, the DirectX11 device is queried:

// Get the device and context
ID3D11Device* dxDevice = NULL;
ID3D11DeviceContext* dxContext = NULL;
MHWRender::MRenderer* theRenderer = MHWRender::MRenderer::theRenderer();
if (theRenderer->drawAPI() == MHWRender::DrawAPI::kDirectX11)
{
    // init device
    ID3D11Device* dxDevice = (ID3D11Device*)theRenderer->GPUDeviceHandle();
    if (!dxDevice) return;

    // get context
    dxDevice->GetImmediateContext(&dxContext);
    if (!dxContext) return;
}

After this information has been extracted, a compute shader is used to fill in a texture. This texture is then used by a pixel shader to render some geometry.

// Load in a compute shader
MString shaderPath("myComputeShader.hlsl");
ID3D11ComputeShader* aComputeShader = NULL;

ID3D10Blob* pPSBuf = NULL;	
HRESULT hr = D3DX11CompileFromFile( shaderPath.asChar(), NULL, NULL, "AComputeShader", "cs_5_0", 	dwShaderFlags, NULL, NULL, &pPSBuf, NULL, NULL );
hr = dxDevice->CreateComputeShader( ( DWORD* )pPSBuf->GetBufferPointer(), pPSBuf->GetBufferSize(), NULL, &aComputeShader );
pPSBuf->Release();

// The “resource” to update. Filled by the compute shader(s), and
// rendered by the pixel shader.
ID3D11ShaderResourceView*  shaderResourceView; // Pixel shader view
ID3D11UnorderedAccessView* computeResourceView; // Compute shader view
Resource pRenderOutput;
ID3D11Texture2D* outputTexture = NULL;

// Create a 2D texture to write into
D3D11_TEXTURE2D_DESC tdesc;
tdesc.Width = g_width;
tdesc.Height = g_height;
tdesc.MipLevels = 1;
tdesc.ArraySize = 1;
tdesc.SampleDesc.Count = 1;
tdesc.SampleDesc.Quality = 0;
tdesc.Usage = D3D11_USAGE_DEFAULT;
tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
tdesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
tdesc.CPUAccessFlags = 0;
tdesc.MiscFlags = 0;
		
hr = dxDevice->CreateTexture2D(&tdesc, NULL, &outputTexture);

// Create an unordered access view to the texture which a compute shader 
// can write into.
dxDevice->CreateUnorderedAccessView( outputTexture, NULL, &computeResourceView);
// Create a resource view to the texture that will be used by a pixel shader 
dxDevice->CreateShaderResourceView(  outputTexture, NULL, &shaderResourceView);

// Set inputs
dxContext->CSSetShaderResources( 0, 1, shaderResourceView);
// Set outputs
dxContext->CSSetUnorderedAccessViews(0, 1, computeResourceView, (UINT*)(&computeResourceView));

// Set the compute shader
dxContext->CSSetShader(aComputeShader, NULL, 0 );
// Run the compute shader with 32 x 32 groups (e.g.)
dxContext->Dispatch( 32, 32, 1 );

//...