void CStateObjectInfo::ResolveSubobjectAssociations()

in Libraries/D3D12RaytracingFallback/src/StateObjectProcessing.cpp [798:1394]


void CStateObjectInfo::ResolveSubobjectAssociations()
{
    for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
    {
        for(auto& association : m_SubobjectToExportsAssociations[i])
        {
            if(association.m_Exports.size())
            {
                // Mark that the object being associated has been used in an explicit association, if applicable,
                // (so it doesn't apply for use in default associations)
                assert(ASN_INVALID != AssociateableSubobjectName(association.m_SubobjectType));
                association.m_pSubobject->m_bUsedInExplicitAssociation = true;
                association.m_pSubobject->m_bReferenced = true;
            }
            for( auto ex : association.m_Exports)
            {
                bool bFoundGoodMatch = false;
                auto match = m_ExportInfoMap.find(ex);
                if(match == m_ExportInfoMap.end())
                {
                    // Try unmangled name searching
                    if(m_ExportNameUnmangledToMangled.count(ex))
                    {
                        auto mangledMatches = m_ExportNameUnmangledToMangled.equal_range(ex);
                        for(auto mangledMatch = mangledMatches.first; mangledMatch != mangledMatches.second; mangledMatch++)
                        {
                            match = m_ExportInfoMap.find(mangledMatch->second);
                            match->second->m_Associations[AssociateableSubobjectName(association.m_SubobjectType)].push_back(&association);                        
                        }
                        bFoundGoodMatch = true;
                    };
                }
                else
                {
                    bFoundGoodMatch = true;
                    match->second->m_Associations[AssociateableSubobjectName(association.m_SubobjectType)].push_back(&association);
                }
                // Try hit groups
                auto hgMatch = m_HitGroups.find(ex);
                if(hgMatch != m_HitGroups.end())
                {
                    bFoundGoodMatch = true;
                    hgMatch->second->m_Associations[AssociateableSubobjectName(association.m_SubobjectType)].push_back(&association);
                }

                if(!bFoundGoodMatch)
                {
                    if(!AllowExternalDependenciesOnLocalDefinitions())
                    {
                        LOG_ERROR(L"Subobject association of type " <<  m_sAssociateableSubobjectData[AssociateableSubobjectName(association.m_SubobjectType)].StringAPIName 
                                    << L" made to export named " << PrettyPrintPossiblyMangledName(ex) << L" which doesn't exist." <<
                                    ((D3D12_STATE_OBJECT_TYPE_COLLECTION == m_SOType) ? 
                                    L" To allow this subobject to be associated with external functions (to be resolved later, when this state object is combined with other state object(s)), "
                                    L"use a D3D12_STATE_OBJECT_CONFIG subobject with D3D12_STATE_OBJECT_FLAG_ALLOW_EXTERNAL_DEPENDENCIES_ON_LOCAL_DEFINITIONS set in Flags." : L""));
                    }
                    m_bAssociationsToUnresolvedFunctions = true;
                }
            }
        }
        // For each associatable subobject type, determine if there's a suitable default subobject for the current scope.
        // Meaning: a single subobject of a given type that eiter:
        // (a) has an association defined with no exports explicitly setting it as the default
        // (b) doesn't have any explicit associations, implicitly setting it as the default if (a) isn't satisified
        auto& AssociateableData = m_AssociateableSubobjectData[i];
        if(AssociateableData.pUsedInExplicitDefaultAssociation)
        {
            assert(ASN_INVALID != AssociateableSubobjectName(AssociateableData.pUsedInExplicitDefaultAssociation->m_SubobjectType));
            AssociateableData.pLocalDefaultSubobject = AssociateableData.pUsedInExplicitDefaultAssociation;
        }
        if(!AssociateableData.pLocalDefaultSubobject)
        {
            bool bFoundSingleUnassociated = false;
            CAssociateableSubobjectInfo* pDefaultAssociationCandidate = nullptr;
            for(auto a : AssociateableData.Associateable)
            {
                if(!a->m_bUsedInExplicitAssociation)
                {
                    if(bFoundSingleUnassociated)
                    {
                        if(pDefaultAssociationCandidate->Compare(a))
                        {
                            // Found duplicate identical unassociated subobjects, still consider that a default candidate. 
                            continue;
                        }
                        bFoundSingleUnassociated = false;
                        break;
                    }
                    pDefaultAssociationCandidate = a;
                    bFoundSingleUnassociated = true;
                }
            }
            if(bFoundSingleUnassociated)
            {
                // Found a default, now log it as an association that exports can point to
                m_SubobjectToExportsAssociations[i].emplace_back();
                CWrappedAssociation* pWrapped = &m_SubobjectToExportsAssociations[i].back();
                pWrapped->m_pSubobject = pDefaultAssociationCandidate;
                pWrapped->m_SubobjectType = pDefaultAssociationCandidate->m_LocalSubobjectDefinition.Type;  
                AssociateableData.pLocalDefaultSubobject = pWrapped;
            }
        }
    }

    bool bHitGroupAssociationsApplied = false;
    auto ApplyHitGroupAssociations = [this,&bHitGroupAssociationsApplied]()
    {
        if(bHitGroupAssociationsApplied)
        {
            return;
        }
        bHitGroupAssociationsApplied = true;
        // Apply hit group associations to any constituent shaders that don't have an equivalent association.
        // If there are inconsistencies with existing associations they will be discovered further below.
        // Also check if there are too many associations to a hit group
        for(auto& hg : m_HitGroups)
        {
            for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
            {
                auto& associations = hg.second->m_Associations[i];
                if(0 == associations.size())
                {
                    continue;
                }
                for(auto& a : associations)
                {
                    for(UINT s = 0; s < 3; s++)
                    {
                        LPCWSTR pDependency = nullptr;
                        switch (s)
                        {
                        case 0:
                            pDependency = hg.second->AnyHitShaderImport;                      
                            break;
                        case 1:
                            pDependency = hg.second->ClosestHitShaderImport;
                            break;
                        case 2:
                            pDependency = hg.second->IntersectionShaderImport;
                            break;
                        default:
                            assert(false);
                            break;
                        }
                        if (!pDependency)
                        {
                            continue;
                        }
                        // Does unmangled search give hit(s)?  
                        CExportInfo* pMatch = nullptr;
                        LPCWSTR pMatchMangledName = nullptr;
                        size_t count = m_ExportNameUnmangledToMangled.count(pDependency);
                        switch (count)
                        {
                        case 0:
                        {
                            // Try mangled
                            auto match = m_ExportInfoMap.find(pDependency);
                            if (match != m_ExportInfoMap.end())
                            {
                                pMatch = match->second;
                                pMatchMangledName = match->first;
                            }
                            break;
                        }
                        case 1:
                        {
                            auto mangledMatch = m_ExportNameUnmangledToMangled.find(pDependency);
                            auto match = m_ExportInfoMap.find(mangledMatch->second);
                            pMatch = match->second;
                            pMatchMangledName = match->first;
                            break;
                        }
                        default:
                            // Error case validated elsewhere, ignore
                            break;
                        }
                        if (pMatch)
                        {
                            pMatch->m_Associations[i].push_back(a);
                            a->m_Exports.insert(pMatchMangledName);
                            a->m_pSubobject->m_bReferenced = true;
                        }
                    }
                }
            }
        }
    };
   
    // Apply default associations while looking to see if there are too many...
    for(auto ex : m_ExportInfoMap)
    {
        CExportInfo* pExportInfo = ex.second;
        if(SupportedShaderType((ShaderKind)ex.second->m_pFunctionInfo->ShaderKind))
        {            
            for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
            {
                size_t count = pExportInfo->m_Associations[i].size();
                switch(count)
                {
                case 0:
                    if(m_AssociateableSubobjectData[i].pLocalDefaultSubobject) 
                    {
                        pExportInfo->m_Associations[i].push_back(m_AssociateableSubobjectData[i].pLocalDefaultSubobject);
                        m_AssociateableSubobjectData[i].pLocalDefaultSubobject->m_Exports.insert(ex.first);
                        m_AssociateableSubobjectData[i].pLocalDefaultSubobject->m_pSubobject->m_bReferenced = true;
                    }
                    else
                    {
                        ApplyHitGroupAssociations();
                        if(MatchRule_RequiredAndMatchingForAllExports == m_sAssociateableSubobjectData[i].MatchRule)
                        {
                            if(!AllowLocalDependenciesOnExternalDefinitions())
                            {
                                auto unMangled = m_ExportNameMangledToUnmangled.find(ex.first);
                                assert(unMangled != m_ExportNameMangledToUnmangled.end());
    #ifdef INCLUDE_MESSAGE_LOG
                                if(m_AssociateableSubobjectData[i].bContinuePrintingMissingSubobjectMessages)
                                {
                                    if((m_AssociateableSubobjectData[i].Associateable.size() == 0) && 
                                    (m_AssociateableSubobjectData[i].InheritedSoNotAssociateable.size() == 0))
                                    {
                                        LOG_ERROR(L"Subobject association of type " << m_sAssociateableSubobjectData[i].StringAPIName 
                                                    << L" must be defined for all relevant exports, yet no such subobject exists at all.  And example of an export needing this association is " <<
                                                    PrettyPrintPossiblyMangledName(ex.first) << L"." <<
                                                    ((D3D12_STATE_OBJECT_TYPE_COLLECTION == m_SOType) ? 
                                                    L" If the intent is this will be resolved later, when this state object is combined with other state object(s), "
                                                    L"use a D3D12_STATE_OBJECT_CONFIG subobject with D3D12_STATE_OBJECT_FLAG_ALLOW_LOCAL_DEPENDENCIES_ON_EXTERNAL_DEFINITIONS set in Flags." : L""));                            

                                        m_AssociateableSubobjectData[i].bContinuePrintingMissingSubobjectMessages = false;
                                    }
                                    else
    #endif
                                    {
                                        LOG_ERROR(L"Export " << PrettyPrintPossiblyMangledName(ex.first) << L" is missing a required subobject association of type " 
                                        << m_sAssociateableSubobjectData[i].StringAPIName << L"." <<
                                        ((D3D12_STATE_OBJECT_TYPE_COLLECTION == m_SOType) ? 
                                            L" If the intent is this will be resolved later, when this state object is combined with other state object(s), "
                                            L"use a D3D12_STATE_OBJECT_CONFIG subobject with D3D12_STATE_OBJECT_FLAG_ALLOW_LOCAL_DEPENDENCIES_ON_EXTERNAL_DEFINITIONS set in Flags." : L""));                            
    
                                    }
    #ifdef INCLUDE_MESSAGE_LOG
                                }
    #endif                            
                            }
                            ex.second->m_bUnresolvedAssociations = true;
                            m_bFunctionsWithUnresolvedAssociations = true;
                        }
                    }
                    break;
                case 1:
                    break;
                default:
                    if(m_sAssociateableSubobjectData[i].bAtMostOneAssociationPerExport)
                    {
                        CAssociateableSubobjectInfo* pRefSubobject = nullptr;
                        for(auto a : pExportInfo->m_Associations[i] )
                        {
                            if(pRefSubobject)
                            {
                                if(!pRefSubobject->Compare(a->m_pSubobject))
                                {
                                    auto unMangled = m_ExportNameMangledToUnmangled.find(ex.first);
                                    assert(unMangled != m_ExportNameMangledToUnmangled.end());
                                    LOG_ERROR( L"Export " << PrettyPrintPossiblyMangledName(ex.first) << L" has multiple subobject associations of type " 
                                    << m_sAssociateableSubobjectData[i].StringAPIName << L" when only one is expected, or if there are multiple subobjects associated they must have matching definitions.");
                                    break;                                    
                                }
                            }
                            else
                            {
                                pRefSubobject = a->m_pSubobject;
                            }
                        }
                    }
                    break;
                }
            }           
        }
    }
 
    // Check subobject association consistency across call graphs and hit groups
    m_TraversalGlobals.AssociateableSubobjectIndex = (ASSOCIATEABLE_SUBOBJECT_NAME)0;
    for(auto& i = m_TraversalGlobals.AssociateableSubobjectIndex; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
    {
        assert(m_sAssociateableSubobjectData[i].bAtMostOneAssociationPerExport); // this code doesn't support
                                                                                 // validating that if multiple associations of 
                                                                                 // the same type are allowed per export, that 
                                                                                 // the call graph has the same set per node.
        const auto& MatchRule = m_sAssociateableSubobjectData[i].MatchRule;
        switch(m_sAssociateableSubobjectData[i].MatchScope)
        {
        case MatchScope_FullStateObject:
        case MatchScope_LocalStateObject:
        {
            bool bMatchScopeLocal = (MatchScope_LocalStateObject == m_sAssociateableSubobjectData[i].MatchScope);
            bool bSkip = true;
            switch(MatchRule)
            {
            case MatchRule_RequiredAndMatchingForAllExports:
            case MatchRule_IfExistsMustExistAndMatchForAllExports:
            case MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry:
                bSkip = false;
                break;
            case MatchRule_NoRequirements:
                break;
            default:
                assert(false);
                break;
            }
            if(bSkip)
            {
                break;
            }
            auto& pRefSubobject = m_TraversalGlobals.pReferenceSubobject;
            pRefSubobject = nullptr;
            bool bAssignedRef = false;
            for(auto function : m_ExportInfoMap)
            {
                if(bMatchScopeLocal && (function.second->m_pOwningStateObject != this))
                {
                    continue;
                }
                auto& currAssociation = function.second->m_Associations[i];
                auto pCurrSubobject = currAssociation.size() ? currAssociation.front()->m_pSubobject : nullptr; // just take first
                if(bAssignedRef)
                {
                    switch(MatchRule)
                    {
                    case MatchRule_RequiredAndMatchingForAllExports:
                    case MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry: // "ShaderEntry" clause isn't meanigful for full graph matching but the rest is
                        assert(!bMatchScopeLocal); // would need to differentiate error message if this case is added.
                        if(pRefSubobject && pCurrSubobject && !pRefSubobject->Compare(pCurrSubobject)) // seperately validated they all have to be assiged, so here only need to compare if they both exist
                        {
                            LOG_ERROR(L"For subobjects of type " <<
                            m_sAssociateableSubobjectData[i].StringAPIName << 
                            ((MatchRule_RequiredAndMatchingForAllExports == MatchRule)? 
                                L", every function in a state object must be associated to either the same sububject definition, or if there are different subobjects their respective definitions must match. "
                            : L" it is optional to associate them to any given function, but for any function in a state object that has this type of subobject associated, it must either match the subobject (if any) associated with other functions in the state object, or if there are different subobjects their respective definitions must match. ")
                            << L"In this case function " << PrettyPrintPossiblyMangledName(function.first) << L" has a different definition for this subobject type than another function in the same state object: " <<
                            PrettyPrintPossiblyMangledName(m_TraversalGlobals.pNameOfExportWithReferenceSubobject) << L".");            
                        }
                        break;
                    case MatchRule_IfExistsMustExistAndMatchForAllExports:
                        if(((pCurrSubobject != nullptr) ^ (pRefSubobject != nullptr))||(pCurrSubobject && pRefSubobject && !pRefSubobject->Compare(pCurrSubobject)))
                        {
                            LOG_ERROR(L"For subobjects of type " <<
                            m_sAssociateableSubobjectData[i].StringAPIName << 
                            L" it is optional to associate them to any given function, but once any function defined in this state object " <<
                            ((D3D12_STATE_OBJECT_TYPE_COLLECTION == m_SOType) 
                            ? (bMatchScopeLocal ? L"(not including definition in any state objects that enclose this one or are peers)" : L"(including definition in any state objects that enclose this collection or are peers) ")
                            : (bMatchScopeLocal ? L"(not including definition in any contained collections)" : L"(including definition in any contained collections) ")) <<
                            L"has this type of subobject associated, all functions either have the same subobject associated, or if there are different subobjects their respective definitions must match. "
                            << L"In this case function " <<
                            PrettyPrintPossiblyMangledName(function.first) << L" has a different definition for (or presence of) this subobject type than another function in the same state object: " <<
                            PrettyPrintPossiblyMangledName(m_TraversalGlobals.pNameOfExportWithReferenceSubobject) << L".");   
                        }
                        break;
                    default:
                        assert(false);
                        break;
                    }
                }
                else
                {
                    switch(MatchRule)
                    {
                    case MatchRule_RequiredAndMatchingForAllExports:
                    case MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry:
                        if(pCurrSubobject)
                        {
                            bAssignedRef = true;
                        }
                        break;
                    case MatchRule_IfExistsMustExistAndMatchForAllExports:
                        bAssignedRef = true;
                        break;
                    default:
                        assert(false);
                        break;
                    }
                    pRefSubobject = pCurrSubobject;
#ifdef INCLUDE_MESSAGE_LOG
                    m_TraversalGlobals.pNameOfExportWithReferenceSubobject = function.first;
#endif                    
                }
            }            
            break;
        }
        case MatchScope_CallGraph:
        {
            switch(MatchRule)
            {
            case MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry:
            case MatchRule_IfExistsMustExistAndMatchForAllExports:
            {
                for(auto& ex : m_ExportInfoList)
                {
                    if(ShaderKind::Library == (ShaderKind)ex.m_pFunctionInfo->ShaderKind)
                    {
                        TraverseFunctionsFindFirstSubobjectInLibraryFunctionSubtrees(ex.m_MangledName);
                    }
                }
                m_TraversalGlobals.GraphTraversalIndex++; // considering traversals for all exports as one merge graph traversal for efficiency 
                break;
            case MatchRule_NoRequirements:
            case MatchRule_RequiredAndMatchingForAllExports:
                break;                
            default:
                assert(false);
                break;
            }
            }
            for(auto& ex : m_ExportInfoList)
            {            
                m_TraversalGlobals.bRootIsEntryFunction = (ShaderKind::Library != (ShaderKind)ex.m_pFunctionInfo->ShaderKind);
                if(  ((MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry == MatchRule) && m_TraversalGlobals.bRootIsEntryFunction)
                  || (MatchRule_IfExistsMustExistAndMatchForAllExports == MatchRule))
                {
                    m_TraversalGlobals.bAssignedRef = false;
                    m_TraversalGlobals.pReferenceSubobject = nullptr; // will get assigned on first node visit
                }
                else
                {
                    m_TraversalGlobals.bAssignedRef = true;
                    m_TraversalGlobals.pReferenceSubobject = ex.m_pFirstSubobjectInLibraryFunctionSubtree;
                }
                TraverseFunctionsSubobjectConsistency(ex.m_MangledName);
            }
            m_TraversalGlobals.GraphTraversalIndex++; // considering traversals for all exports as one merge graph traversal for efficiency            
            break;
        }
        case MatchScope_NoRequirements:
            break;
        default:
            assert(false);
            break;
        }
        // Check hit groups
        switch(m_sAssociateableSubobjectData[i].MatchScope)
        {
        case MatchScope_CallGraph:
        case MatchScope_FullStateObject:
        case MatchScope_LocalStateObject:
        {
            bool bMatchScopeLocal = (MatchScope_LocalStateObject == m_sAssociateableSubobjectData[i].MatchScope);
            for(auto& hg : m_HitGroups)
            {
                auto& currHitGroupGlobalAssociations = hg.second->m_Associations[i];
                auto pHitGroupSubobject = currHitGroupGlobalAssociations.size() ? currHitGroupGlobalAssociations.front()->m_pSubobject : nullptr; // just take first
                auto pRefSubobject = pHitGroupSubobject; // initial reference is what's assigned to the hit group, but if it's null, don't count it as being unassociated
                if(bMatchScopeLocal && (this != hg.second->m_pOwningStateObject))
                {
                    pRefSubobject = nullptr;
                }
                const auto& MatchRule = m_sAssociateableSubobjectData[i].MatchRule;
                if(!pRefSubobject && ((MatchScope_FullStateObject == m_sAssociateableSubobjectData[i].MatchScope) || bMatchScopeLocal) && (MatchRule_RequiredAndMatchingForAllExports == MatchRule))
                {
                    break; // for FullStateObject, would have already validated that all exports match, and if the HitGroup has no association, nothing to validate
                }
                UINT DependencyIndexWhereSubobjectCameFrom = (UINT)-1;
                const CWrappedHitGroup& hgDesc = *hg.second;
                LPCWSTR pNameOfHitGroupDependencyForRefSubobject = nullptr;
                for(UINT s = 0; s < 3; s++)
                {
                    LPCWSTR pDependency = nullptr;
                    switch (s)
                    {
                    case 0:
                        pDependency = hgDesc.AnyHitShaderImport;
                        break;
                    case 1:
                        pDependency = hgDesc.ClosestHitShaderImport;
                        break;
                    case 2:
                        pDependency = hgDesc.IntersectionShaderImport;
                        break;
                    default:
                        assert(false);
                        break;
                    }
                    if(!pDependency)
                    {
                        continue;
                    }
                    // Does unmangled search give hit(s)?  
                    CExportInfo* pMatch = nullptr;
                    size_t count = m_ExportNameUnmangledToMangled.count(pDependency);
                    switch (count)
                    {
                    case 0:
                    {
                        // Try mangled
                        auto match = m_ExportInfoMap.find(pDependency);
                        if (match != m_ExportInfoMap.end())
                        {
                            pMatch = match->second;
                        }
                        break;
                    }
                    case 1:
                    {
                        auto mangledMatch = m_ExportNameUnmangledToMangled.find(pDependency);
                        auto match = m_ExportInfoMap.find(mangledMatch->second);
                        pMatch = match->second;
                        break;
                    }
                    default:
                        // Error case validated elsewhere, ignore
                        break;
                    }
                    if(bMatchScopeLocal && pMatch && (this != pMatch->m_pOwningStateObject))
                    {
                        continue;
                    }
                    if (pMatch)
                    {
                        auto& currAssociations = pMatch->m_Associations[i];
                        auto pNewSubobject = currAssociations.size() ? currAssociations.front()->m_pSubobject : nullptr; // just take first
                        switch(MatchRule)
                        {
                        case MatchRule_RequiredAndMatchingForAllExports:
                            break;
                        case MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry:
                        case MatchRule_IfExistsMustExistAndMatchForAllExports:
                            break;
                        case MatchRule_NoRequirements:
                        default:
                            assert(false);
                            break;
                        }
                        switch(MatchRule)
                        {
                        case MatchRule_RequiredAndMatchingForAllExports:
                        case MatchRule_IfExistsMustMatchOthersThatExistPlusShaderEntry:
                            if(pRefSubobject && pNewSubobject && !pRefSubobject->Compare(pNewSubobject))
                            {
                                assert(!bMatchScopeLocal); // would need to differentiate error message if this case is needed
#ifdef INCLUDE_MESSAGE_LOG
                                bool bSubobjectCameFromHitGroup = (DependencyIndexWhereSubobjectCameFrom == -1) ? true : false;
#endif                                
                                LOG_ERROR(L"For subobjects of type " <<
                                m_sAssociateableSubobjectData[i].StringAPIName << 
                                (MatchRule_RequiredAndMatchingForAllExports == m_sAssociateableSubobjectData[i].MatchRule? 
                                    L", for any member of a HitGroup that has this type of subobject associated, it must either match the subobject associated with other members, or if there are different subobjects their respective definitions must match. "
                                : L" it is optional to associate them to any given member of a HitGroup, but for any member that has this type of subobject associated, it must either match the subobject (if any) associated with other members of the HitGroup, or if there are different subobjects their respective definitions must match. ") <<
                                L"In this case " << 
                                (bSubobjectCameFromHitGroup ? L"overall HitGroupExport" : GetHitGroupDependencyTypeName(DependencyIndexWhereSubobjectCameFrom)) << 
                                L" \"" <<
                                (bSubobjectCameFromHitGroup ? hgDesc.HitGroupExport : pNameOfHitGroupDependencyForRefSubobject) <<
                                L"\" has a different definition for this subobject type than " << GetHitGroupDependencyTypeName(s) << L": " <<
                                PrettyPrintPossiblyMangledName(pDependency) << L".");                                        
                            }
                            break;
                        case MatchRule_IfExistsMustExistAndMatchForAllExports:
                            if( ( ((pRefSubobject != nullptr) || (DependencyIndexWhereSubobjectCameFrom != -1)) && // hit group contributed a non-null subobject, or else a hit group member provided the reference subobject
                                  ((pRefSubobject != nullptr) ^ (pNewSubobject != nullptr))
                                )                                
                                || (pRefSubobject && pNewSubobject && !pRefSubobject->Compare(pNewSubobject)))
                            {
#ifdef INCLUDE_MESSAGE_LOG
                                bool bSubobjectCameFromHitGroup = (DependencyIndexWhereSubobjectCameFrom == -1) ? true : false;
#endif                                
                                LOG_ERROR(L"For subobjects of type " <<
                                m_sAssociateableSubobjectData[i].StringAPIName <<                                     
                                    L", if any member of a HitGroup defined in this state object " << 
                                    ((D3D12_STATE_OBJECT_TYPE_COLLECTION == m_SOType) 
                                    ? (bMatchScopeLocal ? L"(not defined in any state objects that enclose this one or are peers)" : L"(including definition in state objects that enclose this collection or are peers) ")
                                    : (bMatchScopeLocal ? L"(not defined within any contained collections)" : L"(including definition within any contained collections) ")) <<
                                    L"has this type of subobject associated, all other members of the HitGroup must either have the same subobject associated, or if there are different subobjects their respective definitions must match. "
                                        L"In this case " << 
                                (bSubobjectCameFromHitGroup ? L"overall HitGroupExport" : GetHitGroupDependencyTypeName(DependencyIndexWhereSubobjectCameFrom)) << 
                                L" \"" <<
                                (bSubobjectCameFromHitGroup ? hgDesc.HitGroupExport : pNameOfHitGroupDependencyForRefSubobject) <<
                                L"\" has a different definition for this subobject type than " << GetHitGroupDependencyTypeName(s) << L": " <<
                                PrettyPrintPossiblyMangledName(pDependency) << L".");                                        
                            }
                            break;
                        }
                        if(!pRefSubobject)
                        {
                            pRefSubobject = pNewSubobject;
                            pNameOfHitGroupDependencyForRefSubobject = pDependency;                            
#ifdef INCLUDE_MESSAGE_LOG
                            DependencyIndexWhereSubobjectCameFrom = s;
#endif
                        }
                    }
                }
            }
            break;
        }
        default:
            assert(false);
            break;
        }        
    }    
}