git.s-ol.nu ~forks/DiligentCore / 515588c
D3D12 backend: moved resource binding logic to shader variable manger assiduous 6 months ago
5 changed file(s) with 539 addition(s) and 472 deletion(s). Raw diff Collapse all Expand all
133133
134134 void CopyStaticResources(ShaderResourceCacheD3D12& ResourceCache) const;
135135
136 // Binds object pObj to resource with index ResIndex in m_Desc.Resources and
137 // array index ArrayIndex.
138 void BindResource(IDeviceObject* pObj,
139 Uint32 ArrayIndex,
140 Uint32 ResIndex,
141 ShaderResourceCacheD3D12& ResourceCache) const;
142
143 bool IsBound(Uint32 ArrayIndex,
144 Uint32 ResIndex,
145 const ShaderResourceCacheD3D12& ResourceCache) const;
146
147136 struct CommitCacheResourcesAttribs
148137 {
149138 const ShaderResourceCacheD3D12& ResourceCache;
9797 ShaderVariableD3D12Impl* GetVariable(const Char* Name) const;
9898 ShaderVariableD3D12Impl* GetVariable(Uint32 Index) const;
9999
100 // Binds object pObj to resource with index ResIndex and array index ArrayIndex.
101 void BindResource(IDeviceObject* pObj,
102 Uint32 ArrayIndex,
103 Uint32 ResIndex);
104
105 bool IsBound(Uint32 ArrayIndex,
106 Uint32 ResIndex) const;
107
100108 void BindResources(IResourceMapping* pResourceMapping, Uint32 Flags);
101109
102110 static size_t GetRequiredMemorySize(const PipelineResourceSignatureD3D12Impl& Signature,
197205 return m_ParentManager.GetVariableIndex(*this);
198206 }
199207
200 // This method can't be defined in the header due to dependency on PipelineResourceSignatureD3D12Impl
201 virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final;
208 virtual bool DILIGENT_CALL_TYPE IsBound(Uint32 ArrayIndex) const override final
209 {
210 return m_ParentManager.IsBound(ArrayIndex, m_ResIndex);
211 }
202212
203213 virtual void DILIGENT_CALL_TYPE GetHLSLResourceDesc(HLSLShaderResourceDesc& HLSLResDesc) const override final
204214 {
206216 HLSLResDesc.ShaderRegister = GetAttribs().Register;
207217 }
208218
209 const PipelineResourceDesc& GetDesc() const { return m_ParentManager.GetResourceDesc(m_ResIndex); }
210
211 // This method can't be defined in the header due to dependency on PipelineResourceSignatureD3D12Impl
212 void BindResource(IDeviceObject* pObj, Uint32 ArrayIndex) const;
219 const PipelineResourceDesc& GetDesc() const
220 {
221 return m_ParentManager.GetResourceDesc(m_ResIndex);
222 }
223
224 void BindResource(IDeviceObject* pObj, Uint32 ArrayIndex) const
225 {
226 m_ParentManager.BindResource(pObj, ArrayIndex, m_ResIndex);
227 }
213228
214229 private:
215230 using ResourceAttribs = PipelineResourceAttribsD3D12;
216 const ResourceAttribs& GetAttribs() const { return m_ParentManager.GetResourceAttribs(m_ResIndex); }
231 const ResourceAttribs& GetAttribs() const
232 {
233 return m_ParentManager.GetResourceAttribs(m_ResIndex);
234 }
217235
218236 private:
219237 const Uint32 m_ResIndex;
3333 #include "RenderDeviceD3D12Impl.hpp"
3434 #include "BufferD3D12Impl.hpp"
3535 #include "BufferViewD3D12Impl.hpp"
36 #include "SamplerD3D12Impl.hpp"
3736 #include "TextureD3D12Impl.hpp"
3837 #include "TextureViewD3D12Impl.hpp"
39 #include "TopLevelASD3D12Impl.hpp"
38 #include "ShaderVariableD3D.hpp"
39
4040 #include "D3D12TypeConversions.hpp"
41 #include "ShaderVariableD3D.hpp"
4241
4342 namespace Diligent
4443 {
705704 return false;
706705 }
707706
708
709 namespace
710 {
711
712 class BindResourceHelper
713 {
714 public:
715 BindResourceHelper(const PipelineResourceSignatureD3D12Impl& Signature,
716 ShaderResourceCacheD3D12& ResourceCache,
717 Uint32 ResIndex,
718 Uint32 ArrayIndex);
719
720 void operator()(IDeviceObject* pObj) const;
721
722 private:
723 void CacheCB(IDeviceObject* pBuffer) const;
724 void CacheSampler(IDeviceObject* pBuffer) const;
725 void CacheAccelStruct(IDeviceObject* pBuffer) const;
726 void BindCombinedSampler(TextureViewD3D12Impl* pTexView) const;
727 void BindCombinedSampler(BufferViewD3D12Impl* pTexView) const {}
728
729 template <typename TResourceViewType, ///< The type of the view (TextureViewD3D12Impl or BufferViewD3D12Impl)
730 typename TViewTypeEnum ///< The type of the expected view type enum (TEXTURE_VIEW_TYPE or BUFFER_VIEW_TYPE)
731 >
732 void CacheResourceView(IDeviceObject* pBufferView,
733 TViewTypeEnum dbgExpectedViewType) const;
734
735 ID3D12Device* GetD3D12Device() const { return m_Signature.GetDevice()->GetD3D12Device(); }
736
737 void SetResource(D3D12_CPU_DESCRIPTOR_HANDLE CPUDescriptorHandle, RefCntAutoPtr<IDeviceObject>&& pObject) const
738 {
739 if (m_DstTableCPUDescriptorHandle.ptr != 0)
740 {
741 VERIFY(CPUDescriptorHandle.ptr != 0, "CPU descriptor handle must not be null for resources allocated in descriptor tables");
742 DEV_CHECK_ERR(m_ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC || m_DstRes.pObject == nullptr, "Static and mutable resource descriptors should only be copied once");
743 const auto d3d12HeapType = m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER ?
744 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER :
745 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
746 GetD3D12Device()->CopyDescriptorsSimple(1, m_DstTableCPUDescriptorHandle, CPUDescriptorHandle, d3d12HeapType);
747 }
748
749 m_ResourceCache.SetResource(m_RootIndex, m_OffsetFromTableStart, m_ResDesc.ResourceType, CPUDescriptorHandle, std::move(pObject));
750 }
751
752 private:
753 using ResourceAttribs = PipelineResourceSignatureD3D12Impl::ResourceAttribs;
754
755 const PipelineResourceSignatureD3D12Impl& m_Signature;
756 ShaderResourceCacheD3D12& m_ResourceCache;
757
758 const PipelineResourceDesc& m_ResDesc;
759 const ResourceAttribs& m_Attribs; // Must go before m_RootIndex, m_OffsetFromTableStart
760
761 const ResourceCacheContentType m_CacheType; // Must go before m_RootIndex, m_OffsetFromTableStart
762 const Uint32 m_RootIndex; // Must go before m_DstRes
763 const Uint32 m_ArrayIndex;
764 const Uint32 m_OffsetFromTableStart; // Must go before m_DstRes
765
766 const ShaderResourceCacheD3D12::Resource& m_DstRes;
767
768 D3D12_CPU_DESCRIPTOR_HANDLE m_DstTableCPUDescriptorHandle{};
769 };
770
771 BindResourceHelper::BindResourceHelper(const PipelineResourceSignatureD3D12Impl& Signature,
772 ShaderResourceCacheD3D12& ResourceCache,
773 Uint32 ResIndex,
774 Uint32 ArrayIndex) :
775 // clang-format off
776 m_Signature {Signature},
777 m_ResourceCache {ResourceCache},
778 m_ResDesc {Signature.GetResourceDesc(ResIndex)},
779 m_Attribs {Signature.GetResourceAttribs(ResIndex)},
780 m_CacheType {ResourceCache.GetContentType()},
781 m_RootIndex {m_Attribs.RootIndex(m_CacheType)},
782 m_ArrayIndex {ArrayIndex},
783 m_OffsetFromTableStart{m_Attribs.OffsetFromTableStart(m_CacheType) + ArrayIndex},
784 m_DstRes {const_cast<const ShaderResourceCacheD3D12&>(ResourceCache).GetRootTable(m_RootIndex).GetResource(m_OffsetFromTableStart)}
785 // clang-format on
786 {
787 VERIFY(ArrayIndex < m_ResDesc.ArraySize, "Array index is out of range");
788
789 if (m_CacheType != ResourceCacheContentType::Signature && !m_Attribs.IsRootView())
790 {
791 const auto IsSampler = (m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
792 const auto RootParamGroup = VariableTypeToRootParameterGroup(m_ResDesc.VarType);
793 // Static/mutable resources are allocated in GPU-visible descriptor heap, while dynamic resources - in CPU-only heap.
794 m_DstTableCPUDescriptorHandle =
795 ResourceCache.GetDescriptorTableHandle<D3D12_CPU_DESCRIPTOR_HANDLE>(
796 IsSampler ? D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER : D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
797 RootParamGroup, m_RootIndex, m_OffsetFromTableStart);
798 }
799
800 #ifdef DILIGENT_DEBUG
801 if (m_CacheType == ResourceCacheContentType::Signature)
802 {
803 VERIFY(m_DstTableCPUDescriptorHandle.ptr == 0, "Static shader resource cache should never be assigned descriptor space.");
804 }
805 else if (m_CacheType == ResourceCacheContentType::SRB)
806 {
807 if (m_Attribs.GetD3D12RootParamType() == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
808 {
809 VERIFY(m_DstTableCPUDescriptorHandle.ptr != 0, "Shader resources allocated in descriptor tables must be assigned descriptor space.");
810 }
811 else
812 {
813 VERIFY_EXPR(m_Attribs.IsRootView());
814 VERIFY((m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER ||
815 m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV ||
816 m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV),
817 "Only constant buffers and dynamic buffer views can be allocated as root views");
818 VERIFY(m_DstTableCPUDescriptorHandle.ptr == 0, "Resources allocated as root views should never be assigned descriptor space.");
819 }
820 }
821 else
822 {
823 UNEXPECTED("Unknown content type");
824 }
825 #endif
826 }
827
828 void BindResourceHelper::CacheCB(IDeviceObject* pBuffer) const
829 {
830 // We cannot use ValidatedCast<> here as the resource retrieved from the
831 // resource mapping can be of wrong type
832 RefCntAutoPtr<BufferD3D12Impl> pBuffD3D12{pBuffer, IID_BufferD3D12};
833 #ifdef DILIGENT_DEVELOPMENT
834 VerifyConstantBufferBinding(m_ResDesc.Name, m_ResDesc.ArraySize, m_ResDesc.VarType, m_ResDesc.Flags, m_ArrayIndex,
835 pBuffer, pBuffD3D12.RawPtr(), m_DstRes.pObject.RawPtr());
836 if (m_ResDesc.ArraySize != 1 && pBuffD3D12 && pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC && pBuffD3D12->GetD3D12Resource() == nullptr)
837 {
838 LOG_ERROR_MESSAGE("Attempting to bind dynamic buffer '", pBuffD3D12->GetDesc().Name, "' that doesn't have backing d3d12 resource to array variable '", m_ResDesc.Name,
839 "[", m_ResDesc.ArraySize, "]', which is currently not supported in Direct3D12 backend. Either use non-array variable, or bind non-dynamic buffer.");
840 }
841 #endif
842 if (pBuffD3D12)
843 {
844 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
845 {
846 // Do not update resource if one is already bound unless it is dynamic. This may be
847 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
848 return;
849 }
850
851 const auto CPUDescriptorHandle = pBuffD3D12->GetCBVHandle();
852 VERIFY(CPUDescriptorHandle.ptr != 0 || pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC,
853 "Only dynamic constant buffers may have null CPU descriptor");
854
855 SetResource(CPUDescriptorHandle, std::move(pBuffD3D12));
856 }
857 }
858
859
860 void BindResourceHelper::CacheSampler(IDeviceObject* pSampler) const
861 {
862 RefCntAutoPtr<ISamplerD3D12> pSamplerD3D12{pSampler, IID_SamplerD3D12};
863 if (pSamplerD3D12)
864 {
865 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
866 {
867 if (m_DstRes.pObject != pSampler)
868 {
869 auto VarTypeStr = GetShaderVariableTypeLiteralName(m_ResDesc.VarType);
870 LOG_ERROR_MESSAGE("Non-null sampler is already bound to ", VarTypeStr, " shader variable '", GetShaderResourcePrintName(m_ResDesc, m_ArrayIndex),
871 "'. Attempting to bind another sampler is an error and will be ignored. ",
872 "Use another shader resource binding instance or label the variable as dynamic.");
873 }
874
875 // Do not update resource if one is already bound unless it is dynamic. This may be
876 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
877 return;
878 }
879
880 const auto CPUDescriptorHandle = pSamplerD3D12->GetCPUDescriptorHandle();
881 VERIFY(CPUDescriptorHandle.ptr != 0, "Samplers must always have valid CPU descriptors");
882 VERIFY(m_CacheType == ResourceCacheContentType::Signature || m_DstTableCPUDescriptorHandle.ptr != 0,
883 "Samplers in SRB cache must always be allocated in root tables and thus assigned descriptor in the table");
884
885 SetResource(CPUDescriptorHandle, std::move(pSamplerD3D12));
886 }
887 else
888 {
889 LOG_ERROR_MESSAGE("Failed to bind object '", pSampler->GetDesc().Name, "' to variable '",
890 GetShaderResourcePrintName(m_ResDesc, m_ArrayIndex), "'. Incorect object type: sampler is expected.");
891 }
892 }
893
894
895 void BindResourceHelper::CacheAccelStruct(IDeviceObject* pTLAS) const
896 {
897 RefCntAutoPtr<ITopLevelASD3D12> pTLASD3D12{pTLAS, IID_TopLevelASD3D12};
898 if (pTLASD3D12)
899 {
900 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
901 {
902 // Do not update resource if one is already bound unless it is dynamic. This may be
903 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
904 return;
905 }
906
907 const auto CPUDescriptorHandle = pTLASD3D12->GetCPUDescriptorHandle();
908 VERIFY(CPUDescriptorHandle.ptr != 0, "Acceleration structures must always have valid CPU descriptor handles");
909 VERIFY(m_CacheType == ResourceCacheContentType::Signature || m_DstTableCPUDescriptorHandle.ptr != 0,
910 "Acceleration structures in SRB cache are always allocated in root tables and thus must have a descriptor");
911
912 SetResource(CPUDescriptorHandle, std::move(pTLASD3D12));
913 }
914 }
915
916
917 template <typename TResourceViewType>
918 struct ResourceViewTraits
919 {};
920
921 template <>
922 struct ResourceViewTraits<TextureViewD3D12Impl>
923 {
924 static const INTERFACE_ID& IID;
925
926 static bool VerifyView(const TextureViewD3D12Impl* pViewD3D12, const PipelineResourceDesc& ResDesc)
927 {
928 return true;
929 }
930 };
931 const INTERFACE_ID& ResourceViewTraits<TextureViewD3D12Impl>::IID = IID_TextureViewD3D12;
932
933 template <>
934 struct ResourceViewTraits<BufferViewD3D12Impl>
935 {
936 static const INTERFACE_ID& IID;
937
938 static bool VerifyView(const BufferViewD3D12Impl* pViewD3D12, const PipelineResourceDesc& ResDesc)
939 {
940 if (pViewD3D12 != nullptr)
941 {
942 const auto* const pBuffer = pViewD3D12->GetBuffer<BufferD3D12Impl>();
943 if (ResDesc.ArraySize != 1 && pBuffer->GetDesc().Usage == USAGE_DYNAMIC && pBuffer->GetD3D12Resource() == nullptr)
944 {
945 LOG_ERROR_MESSAGE("Attempting to bind dynamic buffer '", pBuffer->GetDesc().Name, "' that doesn't have backing d3d12 resource to array variable '", ResDesc.Name,
946 "[", ResDesc.ArraySize, "]', which is currently not supported in Direct3D12 backend. Either use non-array variable, or bind non-dynamic buffer.");
947 return false;
948 }
949 }
950
951 return true;
952 }
953 };
954 const INTERFACE_ID& ResourceViewTraits<BufferViewD3D12Impl>::IID = IID_BufferViewD3D12;
955
956
957 template <typename TResourceViewType,
958 typename TViewTypeEnum>
959 void BindResourceHelper::CacheResourceView(IDeviceObject* pView,
960 TViewTypeEnum dbgExpectedViewType) const
961 {
962 // We cannot use ValidatedCast<> here as the resource retrieved from the
963 // resource mapping can be of wrong type
964 RefCntAutoPtr<TResourceViewType> pViewD3D12{pView, ResourceViewTraits<TResourceViewType>::IID};
965 #ifdef DILIGENT_DEVELOPMENT
966 VerifyResourceViewBinding(m_ResDesc.Name, m_ResDesc.ArraySize, m_ResDesc.VarType, m_ArrayIndex,
967 pView, pViewD3D12.RawPtr(),
968 {dbgExpectedViewType}, RESOURCE_DIM_UNDEFINED,
969 false, // IsMultisample
970 m_DstRes.pObject.RawPtr());
971 ResourceViewTraits<TResourceViewType>::VerifyView(pViewD3D12, m_ResDesc);
972 #endif
973 if (pViewD3D12)
974 {
975 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
976 {
977 // Do not update resource if one is already bound unless it is dynamic. This may be
978 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
979 return;
980 }
981
982 const auto CPUDescriptorHandle = pViewD3D12->GetCPUDescriptorHandle();
983 // Note that for dynamic structured buffers we still create SRV even though we don't really use it.
984 VERIFY(CPUDescriptorHandle.ptr != 0, "Texture/buffer views should always have valid CPU descriptor handles");
985
986 BindCombinedSampler(pViewD3D12);
987
988 SetResource(CPUDescriptorHandle, std::move(pViewD3D12));
989 }
990 }
991
992
993 void BindResourceHelper::BindCombinedSampler(TextureViewD3D12Impl* pTexView) const
994 {
995 if (m_ResDesc.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_SRV)
996 {
997 VERIFY(!m_Attribs.IsCombinedWithSampler(), "Only texture SRVs can be combined with sampler");
998 return;
999 }
1000
1001 if (!m_Attribs.IsCombinedWithSampler())
1002 return;
1003
1004 const auto& SamplerResDesc = m_Signature.GetResourceDesc(m_Attribs.SamplerInd);
1005 const auto& SamplerAttribs = m_Signature.GetResourceAttribs(m_Attribs.SamplerInd);
1006 VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
1007
1008 if (SamplerAttribs.IsImmutableSamplerAssigned())
1009 {
1010 // Immutable samplers should not be assigned cache space
1011 VERIFY_EXPR(SamplerAttribs.RootIndex(ResourceCacheContentType::Signature) == ResourceAttribs::InvalidSigRootIndex);
1012 VERIFY_EXPR(SamplerAttribs.RootIndex(ResourceCacheContentType::SRB) == ResourceAttribs::InvalidSRBRootIndex);
1013 VERIFY_EXPR(SamplerAttribs.SigOffsetFromTableStart == ResourceAttribs::InvalidOffset);
1014 VERIFY_EXPR(SamplerAttribs.SRBOffsetFromTableStart == ResourceAttribs::InvalidOffset);
1015 return;
1016 }
1017
1018 auto* const pSampler = pTexView->GetSampler();
1019 if (pSampler == nullptr)
1020 {
1021 LOG_ERROR_MESSAGE("Failed to bind sampler to variable '", SamplerResDesc.Name, ". Sampler is not set in the texture view '", pTexView->GetDesc().Name, '\'');
1022 return;
1023 }
1024
1025 VERIFY_EXPR(m_ResDesc.ArraySize == SamplerResDesc.ArraySize || SamplerResDesc.ArraySize == 1);
1026 const auto SamplerArrInd = SamplerResDesc.ArraySize > 1 ? m_ArrayIndex : 0;
1027
1028 BindResourceHelper BindSampler{m_Signature, m_ResourceCache, m_Attribs.SamplerInd, SamplerArrInd};
1029 BindSampler(pSampler);
1030 }
1031
1032 void BindResourceHelper::operator()(IDeviceObject* pObj) const
1033 {
1034 if (pObj)
1035 {
1036 static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type");
1037 switch (m_ResDesc.ResourceType)
1038 {
1039 case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER:
1040 CacheCB(pObj);
1041 break;
1042
1043 case SHADER_RESOURCE_TYPE_TEXTURE_SRV:
1044 case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT:
1045 CacheResourceView<TextureViewD3D12Impl>(pObj, TEXTURE_VIEW_SHADER_RESOURCE);
1046 break;
1047
1048 case SHADER_RESOURCE_TYPE_TEXTURE_UAV:
1049 CacheResourceView<TextureViewD3D12Impl>(pObj, TEXTURE_VIEW_UNORDERED_ACCESS);
1050 break;
1051
1052 case SHADER_RESOURCE_TYPE_BUFFER_SRV:
1053 CacheResourceView<BufferViewD3D12Impl>(pObj, BUFFER_VIEW_SHADER_RESOURCE);
1054 break;
1055
1056 case SHADER_RESOURCE_TYPE_BUFFER_UAV:
1057 CacheResourceView<BufferViewD3D12Impl>(pObj, BUFFER_VIEW_UNORDERED_ACCESS);
1058 break;
1059
1060 case SHADER_RESOURCE_TYPE_SAMPLER:
1061 CacheSampler(pObj);
1062 break;
1063
1064 case SHADER_RESOURCE_TYPE_ACCEL_STRUCT:
1065 CacheAccelStruct(pObj);
1066 break;
1067
1068 default: UNEXPECTED("Unknown resource type ", static_cast<Int32>(m_ResDesc.ResourceType));
1069 }
1070 }
1071 else
1072 {
1073 if (m_DstRes.pObject != nullptr && m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
1074 {
1075 LOG_ERROR_MESSAGE("Shader variable '", m_ResDesc.Name, "' is not dynamic but is being reset to null. This is an error and may cause unpredicted behavior. ",
1076 "Use another shader resource binding instance or label the variable as dynamic if you need to bind another resource.");
1077 }
1078
1079 m_ResourceCache.ResetResource(m_RootIndex, m_OffsetFromTableStart);
1080 if (m_Attribs.IsCombinedWithSampler())
1081 {
1082 auto& SamplerResDesc = m_Signature.GetResourceDesc(m_Attribs.SamplerInd);
1083 auto& SamplerAttribs = m_Signature.GetResourceAttribs(m_Attribs.SamplerInd);
1084 VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
1085
1086 if (!SamplerAttribs.IsImmutableSamplerAssigned())
1087 {
1088 const auto SamplerArrInd = SamplerResDesc.ArraySize > 1 ? m_ArrayIndex : 0;
1089 const auto SamRootIndex = SamplerAttribs.RootIndex(m_CacheType);
1090 const auto SamOffsetFromTableStart = SamplerAttribs.OffsetFromTableStart(m_CacheType) + SamplerArrInd;
1091 const auto& DstSam = const_cast<const ShaderResourceCacheD3D12&>(m_ResourceCache).GetRootTable(SamRootIndex).GetResource(SamOffsetFromTableStart);
1092
1093 if (DstSam.pObject != nullptr && SamplerResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
1094 {
1095 LOG_ERROR_MESSAGE("Sampler variable '", SamplerResDesc.Name, "' is not dynamic but is being reset to null. This is an error and may cause unpredicted behavior. ",
1096 "Use another shader resource binding instance or label the variable as dynamic if you need to bind another sampler.");
1097 }
1098
1099 m_ResourceCache.ResetResource(SamRootIndex, SamOffsetFromTableStart);
1100 }
1101 }
1102 }
1103 }
1104
1105 } // namespace
1106
1107
1108 void PipelineResourceSignatureD3D12Impl::BindResource(IDeviceObject* pObj,
1109 Uint32 ArrayIndex,
1110 Uint32 ResIndex,
1111 ShaderResourceCacheD3D12& ResourceCache) const
1112 {
1113 VERIFY(IsUsingSeparateSamplers() || GetResourceDesc(ResIndex).ResourceType != SHADER_RESOURCE_TYPE_SAMPLER,
1114 "Samplers should not be set directly when using combined texture samplers");
1115 BindResourceHelper BindResHelper{*this, ResourceCache, ResIndex, ArrayIndex};
1116 BindResHelper(pObj);
1117 }
1118
1119 bool PipelineResourceSignatureD3D12Impl::IsBound(Uint32 ArrayIndex,
1120 Uint32 ResIndex,
1121 const ShaderResourceCacheD3D12& ResourceCache) const
1122 {
1123 const auto& ResDesc = GetResourceDesc(ResIndex);
1124 const auto& Attribs = GetResourceAttribs(ResIndex);
1125 const auto CacheType = ResourceCache.GetContentType();
1126 const auto RootIndex = Attribs.RootIndex(CacheType);
1127 const auto OffsetFromTableStart = Attribs.OffsetFromTableStart(CacheType) + ArrayIndex;
1128
1129 VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize);
1130
1131 if (RootIndex < ResourceCache.GetNumRootTables())
1132 {
1133 const auto& RootTable = ResourceCache.GetRootTable(RootIndex);
1134 if (OffsetFromTableStart < RootTable.GetSize())
1135 {
1136 const auto& CachedRes = RootTable.GetResource(OffsetFromTableStart);
1137 return !CachedRes.IsNull();
1138 }
1139 }
1140
1141 return false;
1142 }
1143
1144
1145707 #ifdef DILIGENT_DEVELOPMENT
1146708 bool PipelineResourceSignatureD3D12Impl::DvpValidateCommittedResource(const D3DShaderResourceAttribs& D3DAttribs,
1147709 Uint32 ResIndex,
1166728 bool BindingsOK = true;
1167729 for (Uint32 ArrIndex = 0; ArrIndex < D3DAttribs.BindCount; ++ArrIndex)
1168730 {
1169 if (!IsBound(ArrIndex, ResIndex, ResourceCache))
731 const auto& CachedRes = RootTable.GetResource(OffsetFromTableStart + ArrIndex);
732 if (CachedRes.IsNull())
1170733 {
1171734 LOG_ERROR_MESSAGE("No resource is bound to variable '", GetShaderResourcePrintName(D3DAttribs.Name, D3DAttribs.BindCount, ArrIndex),
1172735 "' in shader '", ShaderName, "' of PSO '", PSOName, "'.");
1176739
1177740 if (ResAttribs.IsCombinedWithSampler())
1178741 {
1179 auto& SamplerResDesc = GetResourceDesc(ResAttribs.SamplerInd);
1180 auto& SamplerAttribs = GetResourceAttribs(ResAttribs.SamplerInd);
742 const auto& SamplerResDesc = GetResourceDesc(ResAttribs.SamplerInd);
743 const auto& SamplerAttribs = GetResourceAttribs(ResAttribs.SamplerInd);
1181744 VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
1182745 VERIFY_EXPR(SamplerResDesc.ArraySize == 1 || SamplerResDesc.ArraySize == ResDesc.ArraySize);
1183746 if (!SamplerAttribs.IsImmutableSamplerAssigned())
1184747 {
1185748 if (ArrIndex < SamplerResDesc.ArraySize)
1186749 {
1187 if (!IsBound(ArrIndex, ResAttribs.SamplerInd, ResourceCache))
750 const auto SamRootIndex = SamplerAttribs.RootIndex(CacheType);
751 const auto SamOffsetFromTableStart = SamplerAttribs.OffsetFromTableStart(CacheType);
752 const auto& SamRootTable = ResourceCache.GetRootTable(RootIndex);
753 const auto& CachedSam = SamRootTable.GetResource(SamOffsetFromTableStart + ArrIndex);
754 if (CachedSam.IsNull())
1188755 {
1189756 LOG_ERROR_MESSAGE("No sampler is bound to sampler variable '", GetShaderResourcePrintName(SamplerResDesc, ArrIndex),
1190757 "' combined with texture '", D3DAttribs.Name, "' in shader '", ShaderName, "' of PSO '", PSOName, "'.");
1193760 }
1194761 }
1195762 }
1196
1197 const auto& CachedRes = RootTable.GetResource(OffsetFromTableStart);
1198763
1199764 switch (ResDesc.ResourceType)
1200765 {
3030
3131 #include "RenderDeviceD3D12Impl.hpp"
3232 #include "PipelineResourceSignatureD3D12Impl.hpp"
33 #include "BufferD3D12Impl.hpp"
34 #include "BufferViewD3D12Impl.hpp"
35 #include "SamplerD3D12Impl.hpp"
36 #include "TextureD3D12Impl.hpp"
37 #include "TextureViewD3D12Impl.hpp"
38 #include "TopLevelASD3D12Impl.hpp"
39
40 #include "ShaderVariableD3D.hpp"
3341 #include "ShaderResourceCacheD3D12.hpp"
3442
3543 namespace Diligent
202210 BindResource(ppObjects[Elem], FirstElement + Elem);
203211 }
204212
205 bool ShaderVariableD3D12Impl::IsBound(Uint32 ArrayIndex) const
206 {
207 return m_ParentManager.m_pSignature->IsBound(ArrayIndex, m_ResIndex, m_ParentManager.m_ResourceCache);
208 }
209
210 void ShaderVariableD3D12Impl::BindResource(IDeviceObject* pObj, Uint32 ArrayIndex) const
211 {
212 m_ParentManager.m_pSignature->BindResource(pObj, ArrayIndex, m_ResIndex, m_ParentManager.m_ResourceCache);
213
214 namespace
215 {
216
217 class BindResourceHelper
218 {
219 public:
220 BindResourceHelper(const PipelineResourceSignatureD3D12Impl& Signature,
221 ShaderResourceCacheD3D12& ResourceCache,
222 Uint32 ResIndex,
223 Uint32 ArrayIndex);
224
225 void operator()(IDeviceObject* pObj) const;
226
227 private:
228 void CacheCB(IDeviceObject* pBuffer) const;
229 void CacheSampler(IDeviceObject* pBuffer) const;
230 void CacheAccelStruct(IDeviceObject* pBuffer) const;
231 void BindCombinedSampler(TextureViewD3D12Impl* pTexView) const;
232 void BindCombinedSampler(BufferViewD3D12Impl* pTexView) const {}
233
234 template <typename TResourceViewType, ///< The type of the view (TextureViewD3D12Impl or BufferViewD3D12Impl)
235 typename TViewTypeEnum ///< The type of the expected view type enum (TEXTURE_VIEW_TYPE or BUFFER_VIEW_TYPE)
236 >
237 void CacheResourceView(IDeviceObject* pBufferView,
238 TViewTypeEnum dbgExpectedViewType) const;
239
240 ID3D12Device* GetD3D12Device() const { return m_Signature.GetDevice()->GetD3D12Device(); }
241
242 void SetResource(D3D12_CPU_DESCRIPTOR_HANDLE CPUDescriptorHandle, RefCntAutoPtr<IDeviceObject>&& pObject) const
243 {
244 if (m_DstTableCPUDescriptorHandle.ptr != 0)
245 {
246 VERIFY(CPUDescriptorHandle.ptr != 0, "CPU descriptor handle must not be null for resources allocated in descriptor tables");
247 DEV_CHECK_ERR(m_ResDesc.VarType == SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC || m_DstRes.pObject == nullptr, "Static and mutable resource descriptors should only be copied once");
248 const auto d3d12HeapType = m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER ?
249 D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER :
250 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
251 GetD3D12Device()->CopyDescriptorsSimple(1, m_DstTableCPUDescriptorHandle, CPUDescriptorHandle, d3d12HeapType);
252 }
253
254 m_ResourceCache.SetResource(m_RootIndex, m_OffsetFromTableStart, m_ResDesc.ResourceType, CPUDescriptorHandle, std::move(pObject));
255 }
256
257 private:
258 using ResourceAttribs = PipelineResourceSignatureD3D12Impl::ResourceAttribs;
259
260 const PipelineResourceSignatureD3D12Impl& m_Signature;
261 ShaderResourceCacheD3D12& m_ResourceCache;
262
263 const PipelineResourceDesc& m_ResDesc;
264 const ResourceAttribs& m_Attribs; // Must go before m_RootIndex, m_OffsetFromTableStart
265
266 const ResourceCacheContentType m_CacheType; // Must go before m_RootIndex, m_OffsetFromTableStart
267 const Uint32 m_RootIndex; // Must go before m_DstRes
268 const Uint32 m_ArrayIndex;
269 const Uint32 m_OffsetFromTableStart; // Must go before m_DstRes
270
271 const ShaderResourceCacheD3D12::Resource& m_DstRes;
272
273 D3D12_CPU_DESCRIPTOR_HANDLE m_DstTableCPUDescriptorHandle{};
274 };
275
276 BindResourceHelper::BindResourceHelper(const PipelineResourceSignatureD3D12Impl& Signature,
277 ShaderResourceCacheD3D12& ResourceCache,
278 Uint32 ResIndex,
279 Uint32 ArrayIndex) :
280 // clang-format off
281 m_Signature {Signature},
282 m_ResourceCache {ResourceCache},
283 m_ResDesc {Signature.GetResourceDesc(ResIndex)},
284 m_Attribs {Signature.GetResourceAttribs(ResIndex)},
285 m_CacheType {ResourceCache.GetContentType()},
286 m_RootIndex {m_Attribs.RootIndex(m_CacheType)},
287 m_ArrayIndex {ArrayIndex},
288 m_OffsetFromTableStart{m_Attribs.OffsetFromTableStart(m_CacheType) + ArrayIndex},
289 m_DstRes {const_cast<const ShaderResourceCacheD3D12&>(ResourceCache).GetRootTable(m_RootIndex).GetResource(m_OffsetFromTableStart)}
290 // clang-format on
291 {
292 VERIFY(ArrayIndex < m_ResDesc.ArraySize, "Array index is out of range");
293
294 if (m_CacheType != ResourceCacheContentType::Signature && !m_Attribs.IsRootView())
295 {
296 const auto IsSampler = (m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
297 const auto RootParamGroup = VariableTypeToRootParameterGroup(m_ResDesc.VarType);
298 // Static/mutable resources are allocated in GPU-visible descriptor heap, while dynamic resources - in CPU-only heap.
299 m_DstTableCPUDescriptorHandle =
300 ResourceCache.GetDescriptorTableHandle<D3D12_CPU_DESCRIPTOR_HANDLE>(
301 IsSampler ? D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER : D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
302 RootParamGroup, m_RootIndex, m_OffsetFromTableStart);
303 }
304
305 #ifdef DILIGENT_DEBUG
306 if (m_CacheType == ResourceCacheContentType::Signature)
307 {
308 VERIFY(m_DstTableCPUDescriptorHandle.ptr == 0, "Static shader resource cache should never be assigned descriptor space.");
309 }
310 else if (m_CacheType == ResourceCacheContentType::SRB)
311 {
312 if (m_Attribs.GetD3D12RootParamType() == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
313 {
314 VERIFY(m_DstTableCPUDescriptorHandle.ptr != 0, "Shader resources allocated in descriptor tables must be assigned descriptor space.");
315 }
316 else
317 {
318 VERIFY_EXPR(m_Attribs.IsRootView());
319 VERIFY((m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER ||
320 m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV ||
321 m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV),
322 "Only constant buffers and dynamic buffer views can be allocated as root views");
323 VERIFY(m_DstTableCPUDescriptorHandle.ptr == 0, "Resources allocated as root views should never be assigned descriptor space.");
324 }
325 }
326 else
327 {
328 UNEXPECTED("Unknown content type");
329 }
330 #endif
331 }
332
333 void BindResourceHelper::CacheCB(IDeviceObject* pBuffer) const
334 {
335 // We cannot use ValidatedCast<> here as the resource retrieved from the
336 // resource mapping can be of wrong type
337 RefCntAutoPtr<BufferD3D12Impl> pBuffD3D12{pBuffer, IID_BufferD3D12};
338 #ifdef DILIGENT_DEVELOPMENT
339 VerifyConstantBufferBinding(m_ResDesc.Name, m_ResDesc.ArraySize, m_ResDesc.VarType, m_ResDesc.Flags, m_ArrayIndex,
340 pBuffer, pBuffD3D12.RawPtr(), m_DstRes.pObject.RawPtr());
341 if (m_ResDesc.ArraySize != 1 && pBuffD3D12 && pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC && pBuffD3D12->GetD3D12Resource() == nullptr)
342 {
343 LOG_ERROR_MESSAGE("Attempting to bind dynamic buffer '", pBuffD3D12->GetDesc().Name, "' that doesn't have backing d3d12 resource to array variable '", m_ResDesc.Name,
344 "[", m_ResDesc.ArraySize, "]', which is currently not supported in Direct3D12 backend. Either use non-array variable, or bind non-dynamic buffer.");
345 }
346 #endif
347 if (pBuffD3D12)
348 {
349 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
350 {
351 // Do not update resource if one is already bound unless it is dynamic. This may be
352 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
353 return;
354 }
355
356 const auto CPUDescriptorHandle = pBuffD3D12->GetCBVHandle();
357 VERIFY(CPUDescriptorHandle.ptr != 0 || pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC,
358 "Only dynamic constant buffers may have null CPU descriptor");
359
360 SetResource(CPUDescriptorHandle, std::move(pBuffD3D12));
361 }
362 }
363
364
365 void BindResourceHelper::CacheSampler(IDeviceObject* pSampler) const
366 {
367 RefCntAutoPtr<ISamplerD3D12> pSamplerD3D12{pSampler, IID_SamplerD3D12};
368 if (pSamplerD3D12)
369 {
370 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
371 {
372 if (m_DstRes.pObject != pSampler)
373 {
374 auto VarTypeStr = GetShaderVariableTypeLiteralName(m_ResDesc.VarType);
375 LOG_ERROR_MESSAGE("Non-null sampler is already bound to ", VarTypeStr, " shader variable '", GetShaderResourcePrintName(m_ResDesc, m_ArrayIndex),
376 "'. Attempting to bind another sampler is an error and will be ignored. ",
377 "Use another shader resource binding instance or label the variable as dynamic.");
378 }
379
380 // Do not update resource if one is already bound unless it is dynamic. This may be
381 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
382 return;
383 }
384
385 const auto CPUDescriptorHandle = pSamplerD3D12->GetCPUDescriptorHandle();
386 VERIFY(CPUDescriptorHandle.ptr != 0, "Samplers must always have valid CPU descriptors");
387 VERIFY(m_CacheType == ResourceCacheContentType::Signature || m_DstTableCPUDescriptorHandle.ptr != 0,
388 "Samplers in SRB cache must always be allocated in root tables and thus assigned descriptor in the table");
389
390 SetResource(CPUDescriptorHandle, std::move(pSamplerD3D12));
391 }
392 else
393 {
394 LOG_ERROR_MESSAGE("Failed to bind object '", pSampler->GetDesc().Name, "' to variable '",
395 GetShaderResourcePrintName(m_ResDesc, m_ArrayIndex), "'. Incorect object type: sampler is expected.");
396 }
397 }
398
399
400 void BindResourceHelper::CacheAccelStruct(IDeviceObject* pTLAS) const
401 {
402 RefCntAutoPtr<ITopLevelASD3D12> pTLASD3D12{pTLAS, IID_TopLevelASD3D12};
403 if (pTLASD3D12)
404 {
405 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
406 {
407 // Do not update resource if one is already bound unless it is dynamic. This may be
408 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
409 return;
410 }
411
412 const auto CPUDescriptorHandle = pTLASD3D12->GetCPUDescriptorHandle();
413 VERIFY(CPUDescriptorHandle.ptr != 0, "Acceleration structures must always have valid CPU descriptor handles");
414 VERIFY(m_CacheType == ResourceCacheContentType::Signature || m_DstTableCPUDescriptorHandle.ptr != 0,
415 "Acceleration structures in SRB cache are always allocated in root tables and thus must have a descriptor");
416
417 SetResource(CPUDescriptorHandle, std::move(pTLASD3D12));
418 }
419 }
420
421
422 template <typename TResourceViewType>
423 struct ResourceViewTraits
424 {};
425
426 template <>
427 struct ResourceViewTraits<TextureViewD3D12Impl>
428 {
429 static const INTERFACE_ID& IID;
430
431 static bool VerifyView(const TextureViewD3D12Impl* pViewD3D12, const PipelineResourceDesc& ResDesc)
432 {
433 return true;
434 }
435 };
436 const INTERFACE_ID& ResourceViewTraits<TextureViewD3D12Impl>::IID = IID_TextureViewD3D12;
437
438 template <>
439 struct ResourceViewTraits<BufferViewD3D12Impl>
440 {
441 static const INTERFACE_ID& IID;
442
443 static bool VerifyView(const BufferViewD3D12Impl* pViewD3D12, const PipelineResourceDesc& ResDesc)
444 {
445 if (pViewD3D12 != nullptr)
446 {
447 const auto* const pBuffer = pViewD3D12->GetBuffer<BufferD3D12Impl>();
448 if (ResDesc.ArraySize != 1 && pBuffer->GetDesc().Usage == USAGE_DYNAMIC && pBuffer->GetD3D12Resource() == nullptr)
449 {
450 LOG_ERROR_MESSAGE("Attempting to bind dynamic buffer '", pBuffer->GetDesc().Name, "' that doesn't have backing d3d12 resource to array variable '", ResDesc.Name,
451 "[", ResDesc.ArraySize, "]', which is currently not supported in Direct3D12 backend. Either use non-array variable, or bind non-dynamic buffer.");
452 return false;
453 }
454 }
455
456 return true;
457 }
458 };
459 const INTERFACE_ID& ResourceViewTraits<BufferViewD3D12Impl>::IID = IID_BufferViewD3D12;
460
461
462 template <typename TResourceViewType,
463 typename TViewTypeEnum>
464 void BindResourceHelper::CacheResourceView(IDeviceObject* pView,
465 TViewTypeEnum dbgExpectedViewType) const
466 {
467 // We cannot use ValidatedCast<> here as the resource retrieved from the
468 // resource mapping can be of wrong type
469 RefCntAutoPtr<TResourceViewType> pViewD3D12{pView, ResourceViewTraits<TResourceViewType>::IID};
470 #ifdef DILIGENT_DEVELOPMENT
471 VerifyResourceViewBinding(m_ResDesc.Name, m_ResDesc.ArraySize, m_ResDesc.VarType, m_ArrayIndex,
472 pView, pViewD3D12.RawPtr(),
473 {dbgExpectedViewType}, RESOURCE_DIM_UNDEFINED,
474 false, // IsMultisample
475 m_DstRes.pObject.RawPtr());
476 ResourceViewTraits<TResourceViewType>::VerifyView(pViewD3D12, m_ResDesc);
477 #endif
478 if (pViewD3D12)
479 {
480 if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr)
481 {
482 // Do not update resource if one is already bound unless it is dynamic. This may be
483 // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor.
484 return;
485 }
486
487 const auto CPUDescriptorHandle = pViewD3D12->GetCPUDescriptorHandle();
488 // Note that for dynamic structured buffers we still create SRV even though we don't really use it.
489 VERIFY(CPUDescriptorHandle.ptr != 0, "Texture/buffer views should always have valid CPU descriptor handles");
490
491 BindCombinedSampler(pViewD3D12);
492
493 SetResource(CPUDescriptorHandle, std::move(pViewD3D12));
494 }
495 }
496
497
498 void BindResourceHelper::BindCombinedSampler(TextureViewD3D12Impl* pTexView) const
499 {
500 if (m_ResDesc.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_SRV)
501 {
502 VERIFY(!m_Attribs.IsCombinedWithSampler(), "Only texture SRVs can be combined with sampler");
503 return;
504 }
505
506 if (!m_Attribs.IsCombinedWithSampler())
507 return;
508
509 const auto& SamplerResDesc = m_Signature.GetResourceDesc(m_Attribs.SamplerInd);
510 const auto& SamplerAttribs = m_Signature.GetResourceAttribs(m_Attribs.SamplerInd);
511 VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
512
513 if (SamplerAttribs.IsImmutableSamplerAssigned())
514 {
515 // Immutable samplers should not be assigned cache space
516 VERIFY_EXPR(SamplerAttribs.RootIndex(ResourceCacheContentType::Signature) == ResourceAttribs::InvalidSigRootIndex);
517 VERIFY_EXPR(SamplerAttribs.RootIndex(ResourceCacheContentType::SRB) == ResourceAttribs::InvalidSRBRootIndex);
518 VERIFY_EXPR(SamplerAttribs.SigOffsetFromTableStart == ResourceAttribs::InvalidOffset);
519 VERIFY_EXPR(SamplerAttribs.SRBOffsetFromTableStart == ResourceAttribs::InvalidOffset);
520 return;
521 }
522
523 auto* const pSampler = pTexView->GetSampler();
524 if (pSampler == nullptr)
525 {
526 LOG_ERROR_MESSAGE("Failed to bind sampler to variable '", SamplerResDesc.Name, ". Sampler is not set in the texture view '", pTexView->GetDesc().Name, '\'');
527 return;
528 }
529
530 VERIFY_EXPR(m_ResDesc.ArraySize == SamplerResDesc.ArraySize || SamplerResDesc.ArraySize == 1);
531 const auto SamplerArrInd = SamplerResDesc.ArraySize > 1 ? m_ArrayIndex : 0;
532
533 BindResourceHelper BindSampler{m_Signature, m_ResourceCache, m_Attribs.SamplerInd, SamplerArrInd};
534 BindSampler(pSampler);
535 }
536
537 void BindResourceHelper::operator()(IDeviceObject* pObj) const
538 {
539 if (pObj)
540 {
541 static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type");
542 switch (m_ResDesc.ResourceType)
543 {
544 case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER:
545 CacheCB(pObj);
546 break;
547
548 case SHADER_RESOURCE_TYPE_TEXTURE_SRV:
549 case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT:
550 CacheResourceView<TextureViewD3D12Impl>(pObj, TEXTURE_VIEW_SHADER_RESOURCE);
551 break;
552
553 case SHADER_RESOURCE_TYPE_TEXTURE_UAV:
554 CacheResourceView<TextureViewD3D12Impl>(pObj, TEXTURE_VIEW_UNORDERED_ACCESS);
555 break;
556
557 case SHADER_RESOURCE_TYPE_BUFFER_SRV:
558 CacheResourceView<BufferViewD3D12Impl>(pObj, BUFFER_VIEW_SHADER_RESOURCE);
559 break;
560
561 case SHADER_RESOURCE_TYPE_BUFFER_UAV:
562 CacheResourceView<BufferViewD3D12Impl>(pObj, BUFFER_VIEW_UNORDERED_ACCESS);
563 break;
564
565 case SHADER_RESOURCE_TYPE_SAMPLER:
566 CacheSampler(pObj);
567 break;
568
569 case SHADER_RESOURCE_TYPE_ACCEL_STRUCT:
570 CacheAccelStruct(pObj);
571 break;
572
573 default: UNEXPECTED("Unknown resource type ", static_cast<Int32>(m_ResDesc.ResourceType));
574 }
575 }
576 else
577 {
578 if (m_DstRes.pObject != nullptr && m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
579 {
580 LOG_ERROR_MESSAGE("Shader variable '", m_ResDesc.Name, "' is not dynamic but is being reset to null. This is an error and may cause unpredicted behavior. ",
581 "Use another shader resource binding instance or label the variable as dynamic if you need to bind another resource.");
582 }
583
584 m_ResourceCache.ResetResource(m_RootIndex, m_OffsetFromTableStart);
585 if (m_Attribs.IsCombinedWithSampler())
586 {
587 auto& SamplerResDesc = m_Signature.GetResourceDesc(m_Attribs.SamplerInd);
588 auto& SamplerAttribs = m_Signature.GetResourceAttribs(m_Attribs.SamplerInd);
589 VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER);
590
591 if (!SamplerAttribs.IsImmutableSamplerAssigned())
592 {
593 const auto SamplerArrInd = SamplerResDesc.ArraySize > 1 ? m_ArrayIndex : 0;
594 const auto SamRootIndex = SamplerAttribs.RootIndex(m_CacheType);
595 const auto SamOffsetFromTableStart = SamplerAttribs.OffsetFromTableStart(m_CacheType) + SamplerArrInd;
596 const auto& DstSam = const_cast<const ShaderResourceCacheD3D12&>(m_ResourceCache).GetRootTable(SamRootIndex).GetResource(SamOffsetFromTableStart);
597
598 if (DstSam.pObject != nullptr && SamplerResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC)
599 {
600 LOG_ERROR_MESSAGE("Sampler variable '", SamplerResDesc.Name, "' is not dynamic but is being reset to null. This is an error and may cause unpredicted behavior. ",
601 "Use another shader resource binding instance or label the variable as dynamic if you need to bind another sampler.");
602 }
603
604 m_ResourceCache.ResetResource(SamRootIndex, SamOffsetFromTableStart);
605 }
606 }
607 }
608 }
609
610 } // namespace
611
612
613 void ShaderVariableManagerD3D12::BindResource(IDeviceObject* pObj,
614 Uint32 ArrayIndex,
615 Uint32 ResIndex)
616 {
617 VERIFY(m_pSignature->IsUsingSeparateSamplers() || GetResourceDesc(ResIndex).ResourceType != SHADER_RESOURCE_TYPE_SAMPLER,
618 "Samplers should not be set directly when using combined texture samplers");
619 BindResourceHelper BindResHelper{*m_pSignature, m_ResourceCache, ResIndex, ArrayIndex};
620 BindResHelper(pObj);
621 }
622
623 bool ShaderVariableManagerD3D12::IsBound(Uint32 ArrayIndex,
624 Uint32 ResIndex) const
625 {
626 const auto& ResDesc = GetResourceDesc(ResIndex);
627 const auto& Attribs = GetResourceAttribs(ResIndex);
628 const auto CacheType = m_ResourceCache.GetContentType();
629 const auto RootIndex = Attribs.RootIndex(CacheType);
630 const auto OffsetFromTableStart = Attribs.OffsetFromTableStart(CacheType) + ArrayIndex;
631
632 VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize);
633
634 if (RootIndex < m_ResourceCache.GetNumRootTables())
635 {
636 const auto& RootTable = const_cast<const ShaderResourceCacheD3D12&>(m_ResourceCache).GetRootTable(RootIndex);
637 if (OffsetFromTableStart < RootTable.GetSize())
638 {
639 const auto& CachedRes = RootTable.GetResource(OffsetFromTableStart);
640 return !CachedRes.IsNull();
641 }
642 }
643
644 return false;
213645 }
214646
215647 } // namespace Diligent
402402 EXPECT_EQ(ResDesc.ArraySize, 1u);
403403 EXPECT_EQ(tex2D_Static, pTestPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
404404 tex2D_Static->Set(pSRVs[0]);
405 EXPECT_TRUE(tex2D_Static->IsBound(0));
405406 }
406407
407408 {
417418 EXPECT_EQ(ResDesc.ArraySize, 2u);
418419 EXPECT_EQ(tex2D_StaticArr, pTestPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
419420 tex2D_StaticArr->SetArray(pSRVs, 0, 2);
421 EXPECT_TRUE(tex2D_StaticArr->IsBound(0));
422 EXPECT_TRUE(tex2D_StaticArr->IsBound(1));
420423 }
421424
422425 {
432435 EXPECT_EQ(ResDesc.ArraySize, 1u);
433436 EXPECT_EQ(UniformBuff_Stat, pTestPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
434437 UniformBuff_Stat->Set(pUBs[0]);
438 EXPECT_TRUE(UniformBuff_Stat->IsBound(0));
435439 }
436440
437441 {
442446 EXPECT_EQ(ResDesc.ArraySize, 1u);
443447 EXPECT_EQ(UniformBuff_Stat2, pTestPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
444448 UniformBuff_Stat2->Set(pUBs[0]);
449 EXPECT_TRUE(UniformBuff_Stat2->IsBound(0));
445450 }
446451
447452 {
452457 EXPECT_EQ(ResDesc.ArraySize, 1u);
453458 EXPECT_EQ(Buffer_Static, pTestPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
454459 Buffer_Static->Set(pFormattedBuffSRV);
460 EXPECT_TRUE(Buffer_Static->IsBound(0));
455461 }
456462
457463 {
463469 EXPECT_EQ(Buffer_StaticArr, pTestPSO->GetStaticVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
464470 Buffer_StaticArr->SetArray(&pFormattedBuffSRV, 0, 1);
465471 Buffer_StaticArr->SetArray(&pFormattedBuffSRV, 1, 1);
472 EXPECT_TRUE(Buffer_StaticArr->IsBound(0));
473 EXPECT_TRUE(Buffer_StaticArr->IsBound(1));
466474 }
467475
468476
510518 EXPECT_EQ(ResDesc.ArraySize, 1u);
511519 EXPECT_EQ(tex2D_Static, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
512520 tex2D_Static->Set(pSRVs[0]);
521 EXPECT_TRUE(tex2D_Static->IsBound(0));
513522 }
514523
515524 {
525534 EXPECT_EQ(ResDesc.ArraySize, 2u);
526535 EXPECT_EQ(tex2D_StaticArr, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
527536 tex2D_StaticArr->SetArray(pSRVs, 0, 2);
537 EXPECT_TRUE(tex2D_StaticArr->IsBound(0));
538 EXPECT_TRUE(tex2D_StaticArr->IsBound(1));
528539 }
529540 {
530541 auto tex2D_StaticArr_sampler = pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, "g_tex2D_StaticArr_sampler");
539550 EXPECT_EQ(ResDesc.ArraySize, 1u);
540551 EXPECT_EQ(UniformBuff_Stat, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
541552 UniformBuff_Stat->Set(pUBs[0]);
553 EXPECT_TRUE(UniformBuff_Stat->IsBound(0));
542554 }
543555
544556 {
549561 EXPECT_EQ(ResDesc.ArraySize, 1u);
550562 EXPECT_EQ(UniformBuff_Stat2, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
551563 UniformBuff_Stat2->Set(pUBs[0]);
564 EXPECT_TRUE(UniformBuff_Stat2->IsBound(0));
552565 }
553566
554567 {
559572 EXPECT_EQ(ResDesc.ArraySize, 1u);
560573 EXPECT_EQ(Buffer_Static, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
561574 Buffer_Static->Set(pFormattedBuffSRV);
575 EXPECT_TRUE(Buffer_Static->IsBound(0));
562576 }
563577
564578 {
570584 EXPECT_EQ(Buffer_StaticArr, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
571585 Buffer_StaticArr->SetArray(&pFormattedBuffSRV, 0, 1);
572586 Buffer_StaticArr->SetArray(&pFormattedBuffSRV, 1, 1);
587 EXPECT_TRUE(Buffer_StaticArr->IsBound(0));
588 EXPECT_TRUE(Buffer_StaticArr->IsBound(1));
573589 }
574590
575591
581597 EXPECT_EQ(ResDesc.ArraySize, 1u);
582598 EXPECT_EQ(rwtex2D_Static, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
583599 rwtex2D_Static->Set(pTexUAVs[0]);
600 EXPECT_TRUE(rwtex2D_Static->IsBound(0));
584601 }
585602
586603
592609 EXPECT_EQ(ResDesc.ArraySize, 1u);
593610 EXPECT_EQ(rwtex2D_Static2, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
594611 rwtex2D_Static2->Set(pTexUAVs[1]);
612 EXPECT_TRUE(rwtex2D_Static2->IsBound(0));
595613 }
596614
597615 {
602620 EXPECT_EQ(ResDesc.ArraySize, 1u);
603621 EXPECT_EQ(rwBuff_Static, pTestPSO->GetStaticVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
604622 rwBuff_Static->Set(spRawBuffUAV[0]);
623 EXPECT_TRUE(rwBuff_Static->IsBound(0));
605624 }
606625
607626
671690 EXPECT_EQ(ResDesc.ArraySize, 1u);
672691 EXPECT_EQ(tex2D_Mut, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
673692 tex2D_Mut->Set(pSRVs[0]);
693 EXPECT_TRUE(tex2D_Mut->IsBound(0));
674694 }
675695
676696 {
686706 EXPECT_EQ(ResDesc.ArraySize, 2u);
687707 EXPECT_EQ(tex2D_MutArr, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
688708 tex2D_MutArr->SetArray(pSRVs, 0, 2);
709 EXPECT_TRUE(tex2D_MutArr->IsBound(0));
710 EXPECT_TRUE(tex2D_MutArr->IsBound(1));
689711 }
690712
691713 {
717739 EXPECT_EQ(ResDesc.ArraySize, 2u);
718740 EXPECT_EQ(tex2D_DynArr, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
719741 tex2D_DynArr->SetArray(pSRVs, 0, 2);
742 EXPECT_TRUE(tex2D_DynArr->IsBound(0));
743 EXPECT_TRUE(tex2D_DynArr->IsBound(1));
720744 }
721745
722746 {
732756 EXPECT_EQ(ResDesc.ArraySize, 1u);
733757 EXPECT_EQ(UniformBuff_Mut, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
734758 UniformBuff_Mut->Set(pUBs[0]);
759 EXPECT_TRUE(UniformBuff_Mut->IsBound(0));
735760 }
736761
737762 {
742767 EXPECT_EQ(ResDesc.ArraySize, 1u);
743768 EXPECT_EQ(UniformBuff_Dyn, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
744769 UniformBuff_Dyn->Set(pUBs[1]);
770 EXPECT_TRUE(UniformBuff_Dyn->IsBound(0));
745771 UniformBuff_Dyn->Set(nullptr);
772 EXPECT_FALSE(UniformBuff_Dyn->IsBound(0));
746773 UniformBuff_Dyn->Set(pUBs[0]);
774 EXPECT_TRUE(UniformBuff_Dyn->IsBound(0));
747775 }
748776
749777 {
754782 EXPECT_EQ(ResDesc.ArraySize, 1u);
755783 EXPECT_EQ(Buffer_Mut, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
756784 Buffer_Mut->Set(pFormattedBuffSRV);
785 EXPECT_TRUE(Buffer_Mut->IsBound(0));
757786 }
758787
759788 {
765794 EXPECT_EQ(Buffer_MutArr, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
766795 Buffer_MutArr->SetArray(&pFormattedBuffSRV, 0, 1);
767796 Buffer_MutArr->SetArray(&pFormattedBuffSRV, 1, 1);
797 EXPECT_TRUE(Buffer_MutArr->IsBound(0));
798 EXPECT_TRUE(Buffer_MutArr->IsBound(1));
768799 }
769800
770801 {
775806 EXPECT_EQ(ResDesc.ArraySize, 1u);
776807 EXPECT_EQ(Buffer_Dyn, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
777808 Buffer_Dyn->Set(pFormattedBuffSRV);
809 EXPECT_TRUE(Buffer_Dyn->IsBound(0));
778810 Buffer_Dyn->Set(nullptr);
811 EXPECT_FALSE(Buffer_Dyn->IsBound(0));
779812 Buffer_Dyn->Set(pFormattedBuffSRV);
813 EXPECT_TRUE(Buffer_Dyn->IsBound(0));
780814 }
781815
782816 {
788822 EXPECT_EQ(Buffer_DynArr, pSRB->GetVariableByName(SHADER_TYPE_VERTEX, ResDesc.Name));
789823 Buffer_DynArr->SetArray(&pFormattedBuffSRV, 0, 1);
790824 Buffer_DynArr->SetArray(&pFormattedBuffSRV, 1, 1);
825 EXPECT_TRUE(Buffer_DynArr->IsBound(0));
826 EXPECT_TRUE(Buffer_DynArr->IsBound(1));
791827 }
792828
793829 {
810846 EXPECT_EQ(ResDesc.ArraySize, 1u);
811847 EXPECT_EQ(tex2D_Mut, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
812848 tex2D_Mut->Set(pRWTexSRVs[4]);
849 EXPECT_TRUE(tex2D_Mut->IsBound(0));
813850 }
814851
815852 {
825862 EXPECT_EQ(ResDesc.ArraySize, 2u);
826863 EXPECT_EQ(tex2D_MutArr, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
827864 tex2D_MutArr->SetArray(pRWTexSRVs + 5, 0, 2);
865 EXPECT_TRUE(tex2D_MutArr->IsBound(0));
828866 }
829867
830868 {
840878 EXPECT_EQ(ResDesc.ArraySize, 1u);
841879 EXPECT_EQ(tex2D_Dyn, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
842880 tex2D_Dyn->Set(pRWTexSRVs[6]);
881 EXPECT_TRUE(tex2D_Dyn->IsBound(0));
843882 tex2D_Dyn->Set(nullptr);
883 EXPECT_FALSE(tex2D_Dyn->IsBound(0));
844884 tex2D_Dyn->Set(pRWTexSRVs[7]);
885 EXPECT_TRUE(tex2D_Dyn->IsBound(0));
845886 }
846887
847888 {
857898 EXPECT_EQ(ResDesc.ArraySize, 2u);
858899 EXPECT_EQ(tex2D_DynArr, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
859900 tex2D_DynArr->SetArray(pSRVs, 0, 2);
901 EXPECT_TRUE(tex2D_DynArr->IsBound(0));
902 EXPECT_TRUE(tex2D_DynArr->IsBound(1));
860903 }
861904
862905 {
873916 EXPECT_EQ(ResDesc.ArraySize, 1u);
874917 EXPECT_EQ(UniformBuff_Mut, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
875918 UniformBuff_Mut->Set(pUBs[0]);
919 EXPECT_TRUE(UniformBuff_Mut->IsBound(0));
876920 }
877921
878922 {
883927 EXPECT_EQ(ResDesc.ArraySize, 1u);
884928 EXPECT_EQ(UniformBuff_Dyn, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
885929 UniformBuff_Dyn->Set(pUBs[1]);
930 EXPECT_TRUE(UniformBuff_Dyn->IsBound(0));
886931 UniformBuff_Dyn->Set(nullptr);
932 EXPECT_FALSE(UniformBuff_Dyn->IsBound(0));
887933 UniformBuff_Dyn->Set(pUBs[0]);
934 EXPECT_TRUE(UniformBuff_Dyn->IsBound(0));
888935 }
889936
890937 {
895942 EXPECT_EQ(ResDesc.ArraySize, 1u);
896943 EXPECT_EQ(Buffer_Mut, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
897944 Buffer_Mut->Set(spRawBuffSRVs[1]);
945 EXPECT_TRUE(Buffer_Mut->IsBound(0));
898946 }
899947
900948 {
906954 EXPECT_EQ(Buffer_MutArr, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
907955 Buffer_MutArr->SetArray(&pFormattedBuffSRV, 0, 1);
908956 Buffer_MutArr->SetArray(&pFormattedBuffSRV, 1, 1);
957 EXPECT_TRUE(Buffer_MutArr->IsBound(0));
958 EXPECT_TRUE(Buffer_MutArr->IsBound(1));
909959 }
910960
911961 {
916966 EXPECT_EQ(ResDesc.ArraySize, 1u);
917967 EXPECT_EQ(Buffer_Dyn, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
918968 Buffer_Dyn->Set(pFormattedBuffSRVs[2]);
969 EXPECT_TRUE(Buffer_Dyn->IsBound(0));
919970 Buffer_Dyn->Set(nullptr);
971 EXPECT_FALSE(Buffer_Dyn->IsBound(0));
920972 Buffer_Dyn->Set(pFormattedBuffSRVs[3]);
973 EXPECT_TRUE(Buffer_Dyn->IsBound(0));
921974 }
922975
923976 {
929982 EXPECT_EQ(Buffer_DynArr, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
930983 Buffer_DynArr->SetArray(&pFormattedBuffSRV, 0, 1);
931984 Buffer_DynArr->SetArray(&pFormattedBuffSRV, 1, 1);
985 EXPECT_TRUE(Buffer_DynArr->IsBound(0));
986 EXPECT_TRUE(Buffer_DynArr->IsBound(1));
932987 }
933988
934989 {
939994 EXPECT_EQ(ResDesc.ArraySize, 1u);
940995 EXPECT_EQ(rwtex2D_Mut, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
941996 rwtex2D_Mut->Set(pTexUAVs[2]);
997 EXPECT_TRUE(rwtex2D_Mut->IsBound(0));
942998 }
943999
9441000 {
9491005 EXPECT_EQ(ResDesc.ArraySize, 1u);
9501006 EXPECT_EQ(rwtex2D_Dyn, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
9511007 rwtex2D_Dyn->Set(pTexUAVs[2]);
1008 EXPECT_TRUE(rwtex2D_Dyn->IsBound(0));
9521009 rwtex2D_Dyn->Set(nullptr);
1010 EXPECT_FALSE(rwtex2D_Dyn->IsBound(0));
9531011 rwtex2D_Dyn->Set(pTexUAVs[3]);
1012 EXPECT_TRUE(rwtex2D_Dyn->IsBound(0));
9541013 }
9551014
9561015 {
9611020 EXPECT_EQ(ResDesc.ArraySize, 1u);
9621021 EXPECT_EQ(rwBuff_Mut, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
9631022 rwBuff_Mut->Set(pFormattedBuffUAV[1]);
1023 EXPECT_TRUE(rwBuff_Mut->IsBound(0));
9641024 }
9651025
9661026 {
9711031 EXPECT_EQ(ResDesc.ArraySize, 1u);
9721032 EXPECT_EQ(rwBuff_Dyn, pSRB->GetVariableByName(SHADER_TYPE_PIXEL, ResDesc.Name));
9731033 rwBuff_Dyn->Set(pFormattedBuffUAV[1]);
1034 EXPECT_TRUE(rwBuff_Dyn->IsBound(0));
9741035 rwBuff_Dyn->Set(nullptr);
1036 EXPECT_FALSE(rwBuff_Dyn->IsBound(0));
9751037 rwBuff_Dyn->Set(pFormattedBuffUAV[2]);
1038 EXPECT_TRUE(rwBuff_Dyn->IsBound(0));
9761039 }
9771040
9781041 {