API Guide > Viewport 2.0 API > Maya Viewport 2.0 API Guide > 5. Devices and Draw APIs >

# 5.2 Accessing Resource Handles and Device Handle

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:

- Device: For DirectX11, a reference to the D3D device used by the framework can be returned. For OpenGL this is an OpenGL context which can be used to share with a context created by the plug-in. The sharing API on Windows is uni-directional, and as such, the destination of the share should never be the context for the existing framework; otherwise, all resources in the framework are destroyed.
- Textures (MTexture): A pointer reference to a shader resource view for DirectX11 or a texture identifier for OpenGL can be returned.
- Target (MRenderTarget): This is exposed as a pointer reference to a render target view or a depth stencil view (depending on target type) for DirectX. For OpenGL this is returned as a pointer to a texture id.
- Vertex and index buffers: For DirectX11 a pointer reference to a buffer can be returned. For OpenGL a pointer reference to the buffer id can be returned.
- State objects:
  - Blend States: For DirectX, a pointer to a Direct3D state object is returned.
  - Rasterizer States: For DirectX, a pointer to a Direct3D state object is returned.
  - Depth Stencil States: For DirectX, a pointer to a Direct3D state object is returned.
  - Sampler States: For DirectX, a pointer to a Direct3D state object is returned.
  - There is no equivalent for a state object in OpenGL. Thus, to access state, descriptors for each state type should be used. Setting state without using API state objects is discouraged as the internal framework cannot track these changes.

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.

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 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 );

    //...