Diligent Graphics > Diligent Engine > Architecture > D3D12 > Binding Shader Resources to the GPU Pipeline
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:
1 2 3 4 5 |
ShaderResourceCacheD3D12* PipelineStateD3D12Impl::CommitAndTransitionShaderResources(IShaderResourceBinding *pShaderResourceBinding, CommandContext &Ctx, bool CommitResources, bool TransitionResources)const; |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
ShaderResourceCacheD3D12* PipelineStateD3D12Impl::CommitAndTransitionShaderResources(IShaderResourceBinding *pShaderResourceBinding, CommandContext &Ctx, bool CommitResources, bool TransitionResources)const { // If the shaders contain no resources or static resources only, shader resource binding may be null. // In this case use special internal SRB object auto *pResBindingD3D12Impl = pShaderResourceBinding ? ValidatedCast<ShaderResourceBindingD3D12Impl>(pShaderResourceBinding) : m_pDefaultShaderResBinding.get(); // First time only, copy static shader resources to the cache if(!pResBindingD3D12Impl->StaticResourcesInitialized()) pResBindingD3D12Impl->InitializeStaticResources(this); auto *pDeviceD3D12Impl = ValidatedCast<RenderDeviceD3D12Impl>( GetDevice() ); auto &ResourceCache = pResBindingD3D12Impl->GetResourceCache(); if(CommitResources) { if(m_Desc.IsComputePipeline) Ctx.AsComputeContext().SetRootSignature( GetD3D12RootSignature() ); else Ctx.AsGraphicsContext().SetRootSignature( GetD3D12RootSignature() ); if(TransitionResources) (m_RootSig.*m_RootSig.TransitionAndCommitDescriptorHandles)(pDeviceD3D12Impl, ResourceCache, Ctx, m_Desc.IsComputePipeline); else (m_RootSig.*m_RootSig.CommitDescriptorHandles)(pDeviceD3D12Impl, ResourceCache, Ctx, m_Desc.IsComputePipeline); } else { m_RootSig.TransitionResources(ResourceCache, Ctx); } return &ResourceCache; } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
void ShaderResourceLayoutD3D12::CopyStaticResourceDesriptorHandles(const ShaderResourceLayoutD3D12 &SrcLayout) { // Iterate over all static SRV/CBV/UAV resources for(Uint32 r=0; r < m_NumCbvSrvUav[SHADER_VARIABLE_TYPE_STATIC]; ++r) { // Get resource attributes const auto &res = GetSrvCbvUav(SHADER_VARIABLE_TYPE_STATIC, r); D3D12_DESCRIPTOR_RANGE_TYPE RangeType = GetDescriptorRangeType(res.GetResType()); // Iterate over all array elements (for non-array resources, BindCount==1) for(Uint32 ArrInd = 0; ArrInd < res.Attribs.BindCount; ++ArrInd) { auto BindPoint = res.Attribs.BindPoint + ArrInd; // Source resource in the static resource cache is in the root table at index RangeType, at offset BindPoint // D3D12_DESCRIPTOR_RANGE_TYPE_SRV = 0, // D3D12_DESCRIPTOR_RANGE_TYPE_UAV = 1 // D3D12_DESCRIPTOR_RANGE_TYPE_CBV = 2 const auto &SrcRes = SrcLayout.m_pResourceCache->GetRootTable(RangeType).GetResource(BindPoint); // Destination resource is at the root index and offset defined by the resource layout auto &DstRes = m_pResourceCache->GetRootTable(res.GetRootIndex()).GetResource(res.OffsetFromTableStart + ArrInd); if(DstRes.pObject != SrcRes.pObject) { // Copy object reference DstRes.pObject = SrcRes.pObject; DstRes.Type = SrcRes.Type; DstRes.CPUDescriptorHandle = SrcRes.CPUDescriptorHandle; // Copy descriptor into a shader-visible heap auto ShdrVisibleHeapCPUDescriptorHandle = m_pResourceCache->GetShaderVisibleTableCPUDescriptorHandle<D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV>( res.GetRootIndex(), res.OffsetFromTableStart + ArrInd); // Root views are not assigned space in the GPU-visible descriptor heap allocation if (ShdrVisibleHeapCPUDescriptorHandle.ptr != 0) { m_pd3d12Device->CopyDescriptorsSimple(1, ShdrVisibleHeapCPUDescriptorHandle, SrcRes.CPUDescriptorHandle, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); } } } if(res.IsValidSampler()) { // If resource is assigned a valid sampler, copy it auto &SamInfo = GetAssignedSampler(res); for(Uint32 ArrInd = 0; ArrInd < SamInfo.Attribs.BindCount; ++ArrInd) { auto BindPoint = SamInfo.Attribs.BindPoint + ArrInd; // Source sampler in the static resource cache is in the root table at index 3 // (D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER = 3), at offset BindPoint auto& SrcSampler = SrcLayout.m_pResourceCache->GetRootTable(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER).GetResource(BindPoint); auto &DstSampler = m_pResourceCache->GetRootTable(SamInfo.RootIndex).GetResource(SamInfo.OffsetFromTableStart + ArrInd); if(DstSampler.pObject != SrcSampler.pObject) { DstSampler.pObject = SrcSampler.pObject; DstSampler.Type = SrcSampler.Type; DstSampler.CPUDescriptorHandle = SrcSampler.CPUDescriptorHandle; auto ShdrVisibleSamplerHeapCPUDescriptorHandle = m_pResourceCache->GetShaderVisibleTableCPUDescriptorHandle<D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER>( SamInfo.RootIndex, SamInfo.OffsetFromTableStart + ArrInd); if (ShdrVisibleSamplerHeapCPUDescriptorHandle.ptr != 0) { m_pd3d12Device->CopyDescriptorsSimple(1, ShdrVisibleSamplerHeapCPUDescriptorHandle, SrcSampler.CPUDescriptorHandle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); } } } } } } |
When couples undergo male infertility treatment in India is attracting many foreign patients for the services offered are monitoring your medications, proper online viagra dosing, providing therapies and ensuring sufficient and on-time medical supply in Columbus, Ohio. The majority of these products are mostly herbal with sildenafil tablets australia no side effects. You’ll be able to be part of the forum and exchange data with the opposite learners generic cialis in australia deeprootsmag.org furthermore. As long as it doesn’t come up with a weird sound and is smooth on the road, things are levitra wholesale deeprootsmag.org normal for us where on a regular basis it helps us reach our destination and come back for more be informative while being concise! Online Article Marketing and Article Writing Services – How Do You Optimize.
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
template<bool PerformResourceTransitions> void RootSignature::CommitDescriptorHandlesInternal_SM(RenderDeviceD3D12Impl *pRenderDeviceD3D12, ShaderResourceCacheD3D12& ResourceCache, CommandContext &Ctx, bool IsCompute)const; template<bool PerformResourceTransitions> void RootSignature::CommitDescriptorHandlesInternal_SMD(RenderDeviceD3D12Impl *pRenderDeviceD3D12, ShaderResourceCacheD3D12& ResourceCache, CommandContext &Ctx, bool IsCompute)const; |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
template<bool PerformResourceTransitions> void RootSignature::CommitDescriptorHandlesInternal_SM(RenderDeviceD3D12Impl *pRenderDeviceD3D12, ShaderResourceCacheD3D12& ResourceCache, CommandContext &Ctx, bool IsCompute)const { // Set descriptor heaps in the command list CommandContext::ShaderDescriptorHeaps Heaps(ResourceCache.GetSrvCbvUavDescriptorHeap(), ResourceCache.GetSamplerDescriptorHeap()); if(Heaps) Ctx.SetDescriptorHeaps(Heaps); // Process all root tables in the root signature m_RootParams.ProcessRootTables( [&](Uint32 RootInd, const RootParameter &RootTable, const D3D12_ROOT_PARAMETER& D3D12Param, bool IsResourceTable, D3D12_DESCRIPTOR_HEAP_TYPE dbgHeapType ) { // Get GPU descriptor handle of current root table D3D12_GPU_DESCRIPTOR_HANDLE RootTableGPUDescriptorHandle = IsResourceTable ? ResourceCache.GetShaderVisibleTableGPUDescriptorHandle<D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV>(RootInd) : ResourceCache.GetShaderVisibleTableGPUDescriptorHandle<D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER>(RootInd); // Bind the table as graphics or compute root descriptor table if(IsCompute) Ctx.GetCommandList()->SetComputeRootDescriptorTable(RootInd, RootTableGPUDescriptorHandle); else Ctx.GetCommandList()->SetGraphicsRootDescriptorTable(RootInd, RootTableGPUDescriptorHandle); // Transition resources if(PerformResourceTransitions) { ProcessCachedTableResources(RootInd, D3D12Param, ResourceCache, dbgHeapType, [&](UINT OffsetFromTableStart, const D3D12_DESCRIPTOR_RANGE &range, ShaderResourceCacheD3D12::Resource &Res) { TransitionResource(Ctx, Res, range.RangeType); } ); } } ); } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
template<class TOperation> __forceinline void RootSignature::RootParamsManager::ProcessRootTables(TOperation Operation)const { for(Uint32 rt = 0; rt < m_NumRootTables; ++rt) { auto &RootTable = GetRootTable(rt); auto RootInd = RootTable.GetRootIndex(); const D3D12_ROOT_PARAMETER& D3D12Param = RootTable; auto &d3d12Table = D3D12Param.DescriptorTable; bool IsResourceTable = d3d12Table.pDescriptorRanges[0].RangeType != D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; Operation(RootInd, RootTable, D3D12Param, IsResourceTable); } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
template<class TOperation> __forceinline void ProcessCachedTableResources(Uint32 RootInd, const D3D12_ROOT_PARAMETER& D3D12Param, ShaderResourceCacheD3D12& ResourceCache, TOperation Operation) { for (UINT r = 0; r < D3D12Param.DescriptorTable.NumDescriptorRanges; ++r) { const auto &range = D3D12Param.DescriptorTable.pDescriptorRanges[r]; for (UINT d = 0; d < range.NumDescriptors; ++d) { auto OffsetFromTableStart = range.OffsetInDescriptorsFromTableStart + d; auto& Res = ResourceCache.GetRootTable(RootInd).GetResource(OffsetFromTableStart); Operation(OffsetFromTableStart, range, Res); } } } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
void RootSignature::CommitRootViews(ShaderResourceCacheD3D12& ResourceCache, CommandContext &Ctx, bool IsCompute, Uint32 ContextId)const { for(Uint32 rv = 0; rv < m_RootParams.GetNumRootViews(); ++rv) { auto &RootView = m_RootParams.GetRootView(rv); auto RootInd = RootView.GetRootIndex(); // Root views are stored at offset 0 auto& Res = ResourceCache.GetRootTable(RootInd).GetResource(0); auto *pBuffToTransition = ValidatedCast<BufferD3D12Impl>(Res.pObject.RawPtr()); if( !pBuffToTransition->CheckAllStates(D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER) ) Ctx.TransitionResource(pBuffToTransition, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); D3D12_GPU_VIRTUAL_ADDRESS CBVAddress = pBuffToTransition->GetGPUAddress(ContextId); if(IsCompute) Ctx.GetCommandList()->SetComputeRootConstantBufferView(RootInd, CBVAddress); else Ctx.GetCommandList()->SetGraphicsRootConstantBufferView(RootInd, CBVAddress); } } |
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.