Although writing device or draw API specific code is discouraged, access is sometimes necessary when device specific information is required. An example of this is if a plug-in wishes to perform some operations using GPU compute.
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 (without 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.
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 associated 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 ); //...