/* * Copyright 2019-2021 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * In no event and under no legal theory, whether in tort (including negligence), * contract, or otherwise, unless required by applicable law (such as deliberate * and grossly negligent acts) or agreed to in writing, shall any Contributor be * liable for any damages, including any direct, indirect, special, incidental, * or consequential damages of any character arising as a result of this License or * out of the use or inability to use the software (including but not limited to damages * for loss of goodwill, work stoppage, computer failure or malfunction, or any and * all other commercial damages or losses), even if such Contributor has been advised * of the possibility of such damages. */ #include "pch.h" #include "ShaderVariableManagerD3D12.hpp" #include "RenderDeviceD3D12Impl.hpp" #include "PipelineResourceSignatureD3D12Impl.hpp" #include "BufferD3D12Impl.hpp" #include "BufferViewD3D12Impl.hpp" #include "SamplerD3D12Impl.hpp" #include "TextureD3D12Impl.hpp" #include "TextureViewD3D12Impl.hpp" #include "TopLevelASD3D12Impl.hpp" #include "ShaderVariableD3D.hpp" #include "ShaderResourceCacheD3D12.hpp" namespace Diligent { template void ProcessSignatureResources(const PipelineResourceSignatureD3D12Impl& Signature, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes, SHADER_TYPE ShaderStages, HandlerType Handler) { const bool UsingCombinedSamplers = Signature.IsUsingCombinedSamplers(); Signature.ProcessResources(AllowedVarTypes, NumAllowedTypes, ShaderStages, [&](const PipelineResourceDesc& ResDesc, Uint32 Index) // { const auto& ResAttr = Signature.GetResourceAttribs(Index); // Skip samplers combined with textures and immutable samplers if (ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER && (UsingCombinedSamplers || ResAttr.IsImmutableSamplerAssigned())) return; Handler(Index); }); } size_t ShaderVariableManagerD3D12::GetRequiredMemorySize(const PipelineResourceSignatureD3D12Impl& Signature, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes, SHADER_TYPE ShaderStages, Uint32& NumVariables) { NumVariables = 0; ProcessSignatureResources(Signature, AllowedVarTypes, NumAllowedTypes, ShaderStages, [&NumVariables](Uint32) // { ++NumVariables; }); return NumVariables * sizeof(ShaderVariableD3D12Impl); } // Creates shader variable for every resource from Signature whose type is one AllowedVarTypes void ShaderVariableManagerD3D12::Initialize(const PipelineResourceSignatureD3D12Impl& Signature, IMemoryAllocator& Allocator, const SHADER_RESOURCE_VARIABLE_TYPE* AllowedVarTypes, Uint32 NumAllowedTypes, SHADER_TYPE ShaderType) { #ifdef DILIGENT_DEBUG m_pDbgAllocator = &Allocator; #endif VERIFY_EXPR(m_NumVariables == 0); const auto MemSize = GetRequiredMemorySize(Signature, AllowedVarTypes, NumAllowedTypes, ShaderType, m_NumVariables); if (m_NumVariables == 0) return; auto* pRawMem = ALLOCATE_RAW(Allocator, "Raw memory buffer for shader variables", MemSize); m_pVariables = reinterpret_cast(pRawMem); Uint32 VarInd = 0; ProcessSignatureResources(Signature, AllowedVarTypes, NumAllowedTypes, ShaderType, [this, &VarInd](Uint32 ResIndex) // { ::new (m_pVariables + VarInd) ShaderVariableD3D12Impl{*this, ResIndex}; ++VarInd; }); VERIFY_EXPR(VarInd == m_NumVariables); m_pSignature = &Signature; } ShaderVariableManagerD3D12::~ShaderVariableManagerD3D12() { VERIFY(m_pVariables == nullptr, "Destroy() has not been called"); } void ShaderVariableManagerD3D12::Destroy(IMemoryAllocator& Allocator) { if (m_pVariables != nullptr) { VERIFY(m_pDbgAllocator == &Allocator, "Incosistent alloctor"); for (Uint32 v = 0; v < m_NumVariables; ++v) m_pVariables[v].~ShaderVariableD3D12Impl(); Allocator.Free(m_pVariables); m_pVariables = nullptr; } } const PipelineResourceDesc& ShaderVariableManagerD3D12::GetResourceDesc(Uint32 Index) const { VERIFY_EXPR(m_pSignature != nullptr); return m_pSignature->GetResourceDesc(Index); } const ShaderVariableManagerD3D12::ResourceAttribs& ShaderVariableManagerD3D12::GetResourceAttribs(Uint32 Index) const { VERIFY_EXPR(m_pSignature != nullptr); return m_pSignature->GetResourceAttribs(Index); } ShaderVariableD3D12Impl* ShaderVariableManagerD3D12::GetVariable(const Char* Name) const { for (Uint32 v = 0; v < m_NumVariables; ++v) { auto& Var = m_pVariables[v]; if (strcmp(Var.GetDesc().Name, Name) == 0) return &Var; } return nullptr; } ShaderVariableD3D12Impl* ShaderVariableManagerD3D12::GetVariable(Uint32 Index) const { if (Index >= m_NumVariables) { LOG_ERROR("Index ", Index, " is out of range"); return nullptr; } return m_pVariables + Index; } Uint32 ShaderVariableManagerD3D12::GetVariableIndex(const ShaderVariableD3D12Impl& Variable) { if (m_pVariables == nullptr) { LOG_ERROR("This shader variable manager has no variables"); return ~0u; } const auto Offset = reinterpret_cast(&Variable) - reinterpret_cast(m_pVariables); DEV_CHECK_ERR(Offset % sizeof(ShaderVariableD3D12Impl) == 0, "Offset is not multiple of ShaderVariableD3D12Impl class size"); auto Index = static_cast(Offset / sizeof(ShaderVariableD3D12Impl)); if (Index < m_NumVariables) return Index; else { LOG_ERROR("Failed to get variable index. The variable ", &Variable, " does not belong to this shader variable manager"); return ~0u; } } void ShaderVariableManagerD3D12::BindResources(IResourceMapping* pResourceMapping, Uint32 Flags) { DEV_CHECK_ERR(pResourceMapping != nullptr, "Failed to bind resources: resource mapping is null"); if ((Flags & BIND_SHADER_RESOURCES_UPDATE_ALL) == 0) Flags |= BIND_SHADER_RESOURCES_UPDATE_ALL; for (Uint32 v = 0; v < m_NumVariables; ++v) { m_pVariables[v].BindResources(pResourceMapping, Flags); } } namespace { class BindResourceHelper { public: BindResourceHelper(const PipelineResourceSignatureD3D12Impl& Signature, ShaderResourceCacheD3D12& ResourceCache, Uint32 ResIndex, Uint32 ArrayIndex); void operator()(IDeviceObject* pObj) const; private: void CacheCB(IDeviceObject* pBuffer) const; void CacheSampler(IDeviceObject* pBuffer) const; void CacheAccelStruct(IDeviceObject* pBuffer) const; void BindCombinedSampler(TextureViewD3D12Impl* pTexView) const; void BindCombinedSampler(BufferViewD3D12Impl* pTexView) const {} template void CacheResourceView(IDeviceObject* pBufferView, TViewTypeEnum dbgExpectedViewType) const; ID3D12Device* GetD3D12Device() const { return m_Signature.GetDevice()->GetD3D12Device(); } void SetResource(D3D12_CPU_DESCRIPTOR_HANDLE CPUDescriptorHandle, RefCntAutoPtr&& pObject) const { if (m_DstTableCPUDescriptorHandle.ptr != 0) { VERIFY(CPUDescriptorHandle.ptr != 0, "CPU descriptor handle must not be null for resources allocated in descriptor tables"); 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"); const auto d3d12HeapType = m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER ? D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER : D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; GetD3D12Device()->CopyDescriptorsSimple(1, m_DstTableCPUDescriptorHandle, CPUDescriptorHandle, d3d12HeapType); } m_ResourceCache.SetResource(m_RootIndex, m_OffsetFromTableStart, m_ResDesc.ResourceType, CPUDescriptorHandle, std::move(pObject)); } private: using ResourceAttribs = PipelineResourceSignatureD3D12Impl::ResourceAttribs; const PipelineResourceSignatureD3D12Impl& m_Signature; ShaderResourceCacheD3D12& m_ResourceCache; const PipelineResourceDesc& m_ResDesc; const ResourceAttribs& m_Attribs; // Must go before m_RootIndex, m_OffsetFromTableStart const ResourceCacheContentType m_CacheType; // Must go before m_RootIndex, m_OffsetFromTableStart const Uint32 m_RootIndex; // Must go before m_DstRes const Uint32 m_ArrayIndex; const Uint32 m_OffsetFromTableStart; // Must go before m_DstRes const ShaderResourceCacheD3D12::Resource& m_DstRes; D3D12_CPU_DESCRIPTOR_HANDLE m_DstTableCPUDescriptorHandle{}; }; BindResourceHelper::BindResourceHelper(const PipelineResourceSignatureD3D12Impl& Signature, ShaderResourceCacheD3D12& ResourceCache, Uint32 ResIndex, Uint32 ArrayIndex) : // clang-format off m_Signature {Signature}, m_ResourceCache {ResourceCache}, m_ResDesc {Signature.GetResourceDesc(ResIndex)}, m_Attribs {Signature.GetResourceAttribs(ResIndex)}, m_CacheType {ResourceCache.GetContentType()}, m_RootIndex {m_Attribs.RootIndex(m_CacheType)}, m_ArrayIndex {ArrayIndex}, m_OffsetFromTableStart{m_Attribs.OffsetFromTableStart(m_CacheType) + ArrayIndex}, m_DstRes {const_cast(ResourceCache).GetRootTable(m_RootIndex).GetResource(m_OffsetFromTableStart)} // clang-format on { VERIFY(ArrayIndex < m_ResDesc.ArraySize, "Array index is out of range, but it should've been corrected by VerifyAndCorrectSetArrayArguments()"); if (m_CacheType != ResourceCacheContentType::Signature && !m_Attribs.IsRootView()) { const auto IsSampler = (m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER); const auto RootParamGroup = VariableTypeToRootParameterGroup(m_ResDesc.VarType); // Static/mutable resources are allocated in GPU-visible descriptor heap, while dynamic resources - in CPU-only heap. m_DstTableCPUDescriptorHandle = ResourceCache.GetDescriptorTableHandle( IsSampler ? D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER : D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, RootParamGroup, m_RootIndex, m_OffsetFromTableStart); } #ifdef DILIGENT_DEBUG if (m_CacheType == ResourceCacheContentType::Signature) { VERIFY(m_DstTableCPUDescriptorHandle.ptr == 0, "Static shader resource cache should never be assigned descriptor space."); } else if (m_CacheType == ResourceCacheContentType::SRB) { if (m_Attribs.GetD3D12RootParamType() == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) { VERIFY(m_DstTableCPUDescriptorHandle.ptr != 0, "Shader resources allocated in descriptor tables must be assigned descriptor space."); } else { VERIFY_EXPR(m_Attribs.IsRootView()); VERIFY((m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_CONSTANT_BUFFER || m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_SRV || m_ResDesc.ResourceType == SHADER_RESOURCE_TYPE_BUFFER_UAV), "Only constant buffers and dynamic buffer views can be allocated as root views"); VERIFY(m_DstTableCPUDescriptorHandle.ptr == 0, "Resources allocated as root views should never be assigned descriptor space."); } } else { UNEXPECTED("Unknown content type"); } #endif } void BindResourceHelper::CacheCB(IDeviceObject* pBuffer) const { // We cannot use ValidatedCast<> here as the resource can be of wrong type RefCntAutoPtr pBuffD3D12{pBuffer, IID_BufferD3D12}; #ifdef DILIGENT_DEVELOPMENT VerifyConstantBufferBinding(m_ResDesc, m_ArrayIndex, pBuffer, pBuffD3D12.RawPtr(), m_DstRes.pObject.RawPtr(), m_Signature.GetDesc().Name); if (m_ResDesc.ArraySize != 1 && pBuffD3D12 && pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC && pBuffD3D12->GetD3D12Resource() == nullptr) { LOG_ERROR_MESSAGE("Attempting to bind dynamic buffer '", pBuffD3D12->GetDesc().Name, "' that doesn't have backing d3d12 resource to array variable '", m_ResDesc.Name, "[", m_ResDesc.ArraySize, "]', which is currently not supported in Direct3D12 backend. Either use non-array variable, or bind non-dynamic buffer."); } #endif if (pBuffD3D12) { if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr) { // Do not update resource if one is already bound unless it is dynamic. This may be // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor. return; } const auto CPUDescriptorHandle = pBuffD3D12->GetCBVHandle(); VERIFY(CPUDescriptorHandle.ptr != 0 || pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC, "Only dynamic constant buffers may have null CPU descriptor"); SetResource(CPUDescriptorHandle, std::move(pBuffD3D12)); } } void BindResourceHelper::CacheSampler(IDeviceObject* pSampler) const { RefCntAutoPtr pSamplerD3D12{pSampler, IID_SamplerD3D12}; if (pSamplerD3D12) { if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr) { if (m_DstRes.pObject != pSampler) { auto VarTypeStr = GetShaderVariableTypeLiteralName(m_ResDesc.VarType); LOG_ERROR_MESSAGE("Non-null sampler is already bound to ", VarTypeStr, " shader variable '", GetShaderResourcePrintName(m_ResDesc, m_ArrayIndex), "'. Attempting to bind another sampler is an error and will be ignored. ", "Use another shader resource binding instance or label the variable as dynamic."); } // Do not update resource if one is already bound unless it is dynamic. This may be // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor. return; } const auto CPUDescriptorHandle = pSamplerD3D12->GetCPUDescriptorHandle(); VERIFY(CPUDescriptorHandle.ptr != 0, "Samplers must always have valid CPU descriptors"); VERIFY(m_CacheType == ResourceCacheContentType::Signature || m_DstTableCPUDescriptorHandle.ptr != 0, "Samplers in SRB cache must always be allocated in root tables and thus assigned descriptor in the table"); SetResource(CPUDescriptorHandle, std::move(pSamplerD3D12)); } else { LOG_ERROR_MESSAGE("Failed to bind object '", pSampler->GetDesc().Name, "' to variable '", GetShaderResourcePrintName(m_ResDesc, m_ArrayIndex), "'. Incorect object type: sampler is expected."); } } void BindResourceHelper::CacheAccelStruct(IDeviceObject* pTLAS) const { RefCntAutoPtr pTLASD3D12{pTLAS, IID_TopLevelASD3D12}; #ifdef DILIGENT_DEVELOPMENT VerifyTLASResourceBinding(m_ResDesc, m_ArrayIndex, pTLAS, pTLASD3D12.RawPtr(), m_DstRes.pObject.RawPtr(), m_Signature.GetDesc().Name); #endif if (pTLASD3D12) { if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr) { // Do not update resource if one is already bound unless it is dynamic. This may be // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor. return; } const auto CPUDescriptorHandle = pTLASD3D12->GetCPUDescriptorHandle(); VERIFY(CPUDescriptorHandle.ptr != 0, "Acceleration structures must always have valid CPU descriptor handles"); VERIFY(m_CacheType == ResourceCacheContentType::Signature || m_DstTableCPUDescriptorHandle.ptr != 0, "Acceleration structures in SRB cache are always allocated in root tables and thus must have a descriptor"); SetResource(CPUDescriptorHandle, std::move(pTLASD3D12)); } } template struct ResourceViewTraits {}; template <> struct ResourceViewTraits { static const INTERFACE_ID& IID; static constexpr RESOURCE_DIMENSION ExpectedResDimension = RESOURCE_DIM_UNDEFINED; static bool VerifyView(const TextureViewD3D12Impl* pViewD3D12, const PipelineResourceDesc& ResDesc, Uint32 ArrayIndex) { return true; } }; const INTERFACE_ID& ResourceViewTraits::IID = IID_TextureViewD3D12; template <> struct ResourceViewTraits { static const INTERFACE_ID& IID; static constexpr RESOURCE_DIMENSION ExpectedResDimension = RESOURCE_DIM_BUFFER; static bool VerifyView(const BufferViewD3D12Impl* pViewD3D12, const PipelineResourceDesc& ResDesc, Uint32 ArrayIndex) { if (pViewD3D12 != nullptr) { const auto* const pBuffer = pViewD3D12->GetBuffer(); if (ResDesc.ArraySize != 1 && pBuffer->GetDesc().Usage == USAGE_DYNAMIC && pBuffer->GetD3D12Resource() == nullptr) { LOG_ERROR_MESSAGE("Attempting to bind dynamic buffer '", pBuffer->GetDesc().Name, "' that doesn't have backing d3d12 resource to array variable '", ResDesc.Name, "[", ResDesc.ArraySize, "]', which is currently not supported in Direct3D12 backend. Either use non-array variable, or bind non-dynamic buffer."); return false; } ValidateBufferMode(ResDesc, ArrayIndex, pViewD3D12); } return true; } }; const INTERFACE_ID& ResourceViewTraits::IID = IID_BufferViewD3D12; template void BindResourceHelper::CacheResourceView(IDeviceObject* pView, TViewTypeEnum dbgExpectedViewType) const { // We cannot use ValidatedCast<> here as the resource can be of wrong type RefCntAutoPtr pViewD3D12{pView, ResourceViewTraits::IID}; #ifdef DILIGENT_DEVELOPMENT VerifyResourceViewBinding(m_ResDesc, m_ArrayIndex, pView, pViewD3D12.RawPtr(), {dbgExpectedViewType}, ResourceViewTraits::ExpectedResDimension, false, // IsMultisample m_DstRes.pObject.RawPtr(), m_Signature.GetDesc().Name); ResourceViewTraits::VerifyView(pViewD3D12, m_ResDesc, m_ArrayIndex); #endif if (pViewD3D12) { if (m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC && m_DstRes.pObject != nullptr) { // Do not update resource if one is already bound unless it is dynamic. This may be // dangerous as CopyDescriptorsSimple() may interfere with GPU reading the same descriptor. return; } const auto CPUDescriptorHandle = pViewD3D12->GetCPUDescriptorHandle(); // Note that for dynamic structured buffers we still create SRV even though we don't really use it. VERIFY(CPUDescriptorHandle.ptr != 0, "Texture/buffer views should always have valid CPU descriptor handles"); BindCombinedSampler(pViewD3D12); SetResource(CPUDescriptorHandle, std::move(pViewD3D12)); } } void BindResourceHelper::BindCombinedSampler(TextureViewD3D12Impl* pTexView) const { if (m_ResDesc.ResourceType != SHADER_RESOURCE_TYPE_TEXTURE_SRV) { VERIFY(!m_Attribs.IsCombinedWithSampler(), "Only texture SRVs can be combined with sampler"); return; } if (!m_Attribs.IsCombinedWithSampler()) return; const auto& SamplerResDesc = m_Signature.GetResourceDesc(m_Attribs.SamplerInd); const auto& SamplerAttribs = m_Signature.GetResourceAttribs(m_Attribs.SamplerInd); VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER); if (SamplerAttribs.IsImmutableSamplerAssigned()) { // Immutable samplers should not be assigned cache space VERIFY_EXPR(SamplerAttribs.RootIndex(ResourceCacheContentType::Signature) == ResourceAttribs::InvalidSigRootIndex); VERIFY_EXPR(SamplerAttribs.RootIndex(ResourceCacheContentType::SRB) == ResourceAttribs::InvalidSRBRootIndex); VERIFY_EXPR(SamplerAttribs.SigOffsetFromTableStart == ResourceAttribs::InvalidOffset); VERIFY_EXPR(SamplerAttribs.SRBOffsetFromTableStart == ResourceAttribs::InvalidOffset); return; } auto* const pSampler = pTexView->GetSampler(); if (pSampler == nullptr) { LOG_ERROR_MESSAGE("Failed to bind sampler to variable '", SamplerResDesc.Name, ". Sampler is not set in the texture view '", pTexView->GetDesc().Name, '\''); return; } VERIFY_EXPR(m_ResDesc.ArraySize == SamplerResDesc.ArraySize || SamplerResDesc.ArraySize == 1); const auto SamplerArrInd = SamplerResDesc.ArraySize > 1 ? m_ArrayIndex : 0; BindResourceHelper BindSampler{m_Signature, m_ResourceCache, m_Attribs.SamplerInd, SamplerArrInd}; BindSampler(pSampler); } void BindResourceHelper::operator()(IDeviceObject* pObj) const { if (pObj) { static_assert(SHADER_RESOURCE_TYPE_LAST == 8, "Please update this function to handle the new resource type"); switch (m_ResDesc.ResourceType) { case SHADER_RESOURCE_TYPE_CONSTANT_BUFFER: CacheCB(pObj); break; case SHADER_RESOURCE_TYPE_TEXTURE_SRV: case SHADER_RESOURCE_TYPE_INPUT_ATTACHMENT: CacheResourceView(pObj, TEXTURE_VIEW_SHADER_RESOURCE); break; case SHADER_RESOURCE_TYPE_TEXTURE_UAV: CacheResourceView(pObj, TEXTURE_VIEW_UNORDERED_ACCESS); break; case SHADER_RESOURCE_TYPE_BUFFER_SRV: CacheResourceView(pObj, BUFFER_VIEW_SHADER_RESOURCE); break; case SHADER_RESOURCE_TYPE_BUFFER_UAV: CacheResourceView(pObj, BUFFER_VIEW_UNORDERED_ACCESS); break; case SHADER_RESOURCE_TYPE_SAMPLER: CacheSampler(pObj); break; case SHADER_RESOURCE_TYPE_ACCEL_STRUCT: CacheAccelStruct(pObj); break; default: UNEXPECTED("Unknown resource type ", static_cast(m_ResDesc.ResourceType)); } } else { if (m_DstRes.pObject != nullptr && m_ResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) { 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. ", "Use another shader resource binding instance or label the variable as dynamic if you need to bind another resource."); } m_ResourceCache.ResetResource(m_RootIndex, m_OffsetFromTableStart); if (m_Attribs.IsCombinedWithSampler()) { auto& SamplerResDesc = m_Signature.GetResourceDesc(m_Attribs.SamplerInd); auto& SamplerAttribs = m_Signature.GetResourceAttribs(m_Attribs.SamplerInd); VERIFY_EXPR(SamplerResDesc.ResourceType == SHADER_RESOURCE_TYPE_SAMPLER); if (!SamplerAttribs.IsImmutableSamplerAssigned()) { const auto SamplerArrInd = SamplerResDesc.ArraySize > 1 ? m_ArrayIndex : 0; const auto SamRootIndex = SamplerAttribs.RootIndex(m_CacheType); const auto SamOffsetFromTableStart = SamplerAttribs.OffsetFromTableStart(m_CacheType) + SamplerArrInd; const auto& DstSam = const_cast(m_ResourceCache).GetRootTable(SamRootIndex).GetResource(SamOffsetFromTableStart); if (DstSam.pObject != nullptr && SamplerResDesc.VarType != SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC) { 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. ", "Use another shader resource binding instance or label the variable as dynamic if you need to bind another sampler."); } m_ResourceCache.ResetResource(SamRootIndex, SamOffsetFromTableStart); } } } } } // namespace void ShaderVariableManagerD3D12::BindResource(IDeviceObject* pObj, Uint32 ArrayIndex, Uint32 ResIndex) { VERIFY(m_pSignature->IsUsingSeparateSamplers() || GetResourceDesc(ResIndex).ResourceType != SHADER_RESOURCE_TYPE_SAMPLER, "Samplers should not be set directly when using combined texture samplers"); BindResourceHelper BindResHelper{*m_pSignature, m_ResourceCache, ResIndex, ArrayIndex}; BindResHelper(pObj); } bool ShaderVariableManagerD3D12::IsBound(Uint32 ArrayIndex, Uint32 ResIndex) const { const auto& ResDesc = GetResourceDesc(ResIndex); const auto& Attribs = GetResourceAttribs(ResIndex); const auto CacheType = m_ResourceCache.GetContentType(); const auto RootIndex = Attribs.RootIndex(CacheType); const auto OffsetFromTableStart = Attribs.OffsetFromTableStart(CacheType) + ArrayIndex; VERIFY_EXPR(ArrayIndex < ResDesc.ArraySize); if (RootIndex < m_ResourceCache.GetNumRootTables()) { const auto& RootTable = const_cast(m_ResourceCache).GetRootTable(RootIndex); if (OffsetFromTableStart < RootTable.GetSize()) { const auto& CachedRes = RootTable.GetResource(OffsetFromTableStart); return !CachedRes.IsNull(); } } return false; } } // namespace Diligent