Binding Shader Resources to the GPU Pipeline

Overview

Before a draw or a dispatch compute command can be invoked, all required resources bound to the Shader Resource Binding object and kept in its internal shader resource cache must be committed to the graphics pipeline. This is done explicitly by calling IDeviceContext::CommitShaderResources(IShaderResourceBinding *pShaderResourceBinding, Uint32 Flags)  API function, which calls the following internal method:

CommitAndTransitionShaderResources() function takes pointer to the shader resource binding whose resources should be committed to the pipeline, and two flags indicating if resources are to be committed, transitioned to correct states or both. If all shader stages contain no resources or only static resources, shader resource binding may be null. In this case, the method uses internal shader resource binding object. When SRB object is committed for the first time, the method copies static resources from the shaders’ static resource caches into the cache of the SRB. ShaderResourceBindingD3D12Impl::InitializeStaticResources() performs this task. The method then sets D3D12 root signature in the command list and calls method to transition only, commit only, or transition and commit resources. The full source code of the function is given in the listing below:

For performance reasons, RootSignature class contains two versions of the functions that transition and commit resources or transition only. If there are no dynamic resources, the operation can be performed faster, which is implemented by the first version. Second version is used in the case if at least one shader contains dynamic resources. Details of both versions are covered in below. m_RootSig.TransitionAndCommitDescriptorHandles and m_RootSig.CommitDescriptorHandles are function pointers that are initialized to point at the appropriate version.

Initializing Static Resources

Recall that static shader resources are bound directly through shader objects and references to objects are kept by the static resource cache. This cache uses special layout with four root tables, where root index is defined by the resource type (SRV, UAV, CBV, or Sampler), and offset in the table is equal to the resource bind point. Before resources of a shader resource binding object can be committed, all static resources need to be copied from the static resource caches of shader object. This is performed by ShaderResourceBindingD3D12Impl::InitializeStaticResources() method. The method goes through all layouts stored by the SRB object, and for every layout, calls     ShaderResourceLayoutD3D12::CopyStaticResourceDesriptorHandles(), which copies static resources of one shader stage. The source code of this method is given in the listing below:

Setting Descriptor Tables and Root Views, and Transitioning Resources to Correct States

Recall that every shader resource binding object contains shader resource cache that holds references to bound resources as well as allocation in the GPU-visible descriptor heap. Root signature contains two template methods that commit shader resource to the pipeline:

The first method is used when active shader stages do not contain dynamic resources. Dynamic resources require special handling implemented by the second version of the function. Both functions also take template argument indicating if resources need to be transitioned to correct states.

Handling Root Signatures with Static and Mutable Resources only

CommitDescriptorHandlesInternal_SM method iterates over all descriptor tables in the root signature, and for every table, binds its GPU descriptor handle as the graphics or compute root descriptor table. If PerformResourceTransitions flag is set to true, the method also iterates over all resources in the table and transitions every resource into the correct state. Full source code of this function is given in the listing below:

In the listing above, ProcessRootTables()  is a template function that iterates over all root tables in the root signature, and for every tables calls a function specified as its template argument:

ProcessCachedTableResources() is another helper template function that iterates over all resources in a descriptor table and for every resource, calls function specified as its template argument:

Handling Root Signatures Containing Dynamic Resources

If root signature contains at least one dynamic resources, the resources are processed by the CommitDescriptorHandlesInternal_SMD() function. For static and mutable resources, the method works exactly as CommitDescriptorHandlesInternal_SM(). However, to handle dynamic resources, the method first requests dynamic allocation in CBV/SRV/UAV and Sampler GPU-visible descriptor heaps hold all dynamic descriptors. The method maintains current offsets in the allocations and all dynamic descriptor tables are then suballocated in these allocations. When method encounters a table with dynamic resources, it binds GPU descriptor at current offset as the graphics or compute root descriptor table, copies all descriptors into the GPU-visible heap, and advances the current pointer.

Committing Root Views

While there is explicit call for committing resource, root views are committed automatically at every draw call. The method iterates over all root views in the root signature, transitions each resource into a correct state and binds the resource as graphics or compute root CBV. The source code of the method is give in the listing below:

Transitioning Resources to Correct States

Every resource object in Diligent Engine keeps tracks its D3D12 state. When resources are committed to the GPU, the state of every resource is checked if it is compatible with shader resource. If the resource needs to be transitioned to another state, the corresponding resource barrier is added to the command list before the draw command is executed.