void CStateObjectInfo::AddCollection()

in Libraries/D3D12RaytracingFallback/src/StateObjectProcessing.cpp [1711:2085]


void CStateObjectInfo::AddCollection(const D3D12_EXISTING_COLLECTION_DESC* pCollection)
{
    if(!pCollection || !pCollection->pExistingCollection)
    {
        LOG_ERROR(L"NULL specified for D3D12_EXISTING_COLLECTION_DESC or pExistingCollection.");
        return;
    }
    CStateObjectInfo* pColInfo = m_pfnGetStateObjectInfoForExistingCollection(pCollection->pExistingCollection);
    if (!pColInfo)
    {
        LOG_ERROR(L"Failed to extract collection information from D3D12_EXISTING_COLLECTION_DESC.pExistingCollection: " 
            << pCollection->pExistingCollection);
        return;
    }
    m_ExistingCollectionList.emplace_back();
    auto& wrappedCollection = m_ExistingCollectionList.back();
    wrappedCollection.Init(pCollection);

    // Don't keep any references to pCollection, pColInfo or their contents -> app memory
    // Exception is DXILLibraryReflection, which is a shared pointer (dependency taken below)
    for(auto lib : pColInfo->m_DXILLibraryList) // copy shared pointer references
    {
        m_DXILLibraryList.push_back(lib);
    }

    // No manual export list, export everything
    if (0 == pCollection->NumExports)
    {
        for (auto exportInfo : pColInfo->m_ExportInfoMap)
        {
            auto exportNameUnmangled = pColInfo->m_ExportNameMangledToUnmangled.find(exportInfo.first);
            assert(exportNameUnmangled != pColInfo->m_ExportNameMangledToUnmangled.end());
            AddExport(
                exportInfo.first, 
                exportNameUnmangled->second, 
                exportInfo.second->m_pFunctionInfo,
                exportInfo.second->m_pOwningStateObject,
                pColInfo->AllowExternalDependenciesOnLocalDefinitions());
        }
        for(auto& hitGroup : pColInfo->m_HitGroupList)
        {
            AddHitGroup(&hitGroup,hitGroup.m_pOwningStateObject);
        }
        for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
        {
            for(auto& association : pColInfo->m_SubobjectToExportsAssociations[i])
            {
                if(association.m_Exports.size() == 0)
                {
                    continue; // skip default associations as they don't have affect outside their scope
                }
                CWrappedAssociation* pNewOrExistingAssociation = nullptr;
                for(auto& existing : m_SubobjectToExportsAssociations[i])
                {
                    if(association.m_pSubobject->Compare(existing.m_pSubobject))
                    {
                        // found a matching existing association
                        pNewOrExistingAssociation = &existing;
                    }
                }
                if(!pNewOrExistingAssociation)
                {
                    m_SubobjectToExportsAssociations[i].emplace_back();
                    pNewOrExistingAssociation = &m_SubobjectToExportsAssociations[i].back();
                    pNewOrExistingAssociation->m_pSubobject = TrackAssociateableSubobject(
                        association.m_SubobjectType,association.m_pSubobject->m_LocalSubobjectDefinition.pDesc,nullptr);
                    pNewOrExistingAssociation->m_SubobjectType = association.m_SubobjectType;

                }
                for(auto ex : association.m_Exports)
                {
                    pNewOrExistingAssociation->m_Exports.insert(LocalUniqueCopy(ex));
                }
            }            
        }
    }
    else
    {
        // Manual export list

        // Multimap of internal export names to external export(s) the library desc manually listed (if any)
        std::unordered_multimap<std::wstring, const D3D12_EXPORT_DESC*> ExportsToUse;
        std::unordered_set<const D3D12_EXPORT_DESC*> ExportMissing;

        for (UINT i = 0; i < pCollection->NumExports; i++)
        {
            LPCWSTR InternalName = pCollection->pExports[i].ExportToRename ? pCollection->pExports[i].ExportToRename : pCollection->pExports[i].Name;
            ExportsToUse.insert({ InternalName,&pCollection->pExports[i] });
            ExportMissing.insert(&pCollection->pExports[i]);
        }

        // Multimap of subobjects that were associated -> the exports associated to each one
        // The subobjects (CWrappedAssociation*) are from the incoming collection, after the map is 
        // completely generated, need to make a local CWrappedAssociation for each one
        std::unordered_multimap<const CWrappedAssociation*,LPCWSTR> ReferencedAssociations[NUM_ASSOCIATEABLE_SUBOBJECT_TYPES];
        std::unordered_set<const CWrappedAssociation*> UniqueReferencedAssociations[NUM_ASSOCIATEABLE_SUBOBJECT_TYPES];
        std::unordered_map<const CWrappedAssociation*,CWrappedAssociation*> OldToNewWrappedAssociations;
        std::unordered_set<LPCWSTR> OldMangledNamesExported;
        std::unordered_set<LPCWSTR> OldMangledNamesRenamed;
        // Examine functions
        {
            auto AddExportWrapper = [&ReferencedAssociations,&UniqueReferencedAssociations,&OldMangledNamesExported,&pColInfo,this]
                (LPCWSTR pExternalNameMangled, LPCWSTR pExternalNameUnmangled, LPCWSTR pOldMangledName, const CExportInfo* pColExportInfo)
            {
                for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
                {
                    for(auto a : pColExportInfo->m_Associations[i])
                    {
                        ReferencedAssociations[i].insert({a,LocalUniqueCopy(pExternalNameMangled)});
                        UniqueReferencedAssociations[i].insert(a);
                    }
                }
                OldMangledNamesExported.insert(pOldMangledName);
                AddExport(
                    pExternalNameMangled,
                    pExternalNameUnmangled,
                    pColExportInfo->m_pFunctionInfo,
                    pColExportInfo->m_pOwningStateObject,
                    pColInfo->AllowExternalDependenciesOnLocalDefinitions());
            };

            for (UINT i = 0; i < pCollection->NumExports; i++)
            {
                LPCWSTR InternalName = pCollection->pExports[i].ExportToRename ? pCollection->pExports[i].ExportToRename : pCollection->pExports[i].Name;
                auto matchesUnmangled = pColInfo->m_ExportNameUnmangledToMangled.equal_range(pColInfo->LocalUniqueCopy(InternalName));
                // cases: (1) ExportToRename is an unmangled name, Name is unmangled
                //        (2) ExportToRename is a mangled name, Name is unmangled
                //        (3) ExportToRename is null, Name is unmangled
                //        (4) ExportToRename is null, Name is mangled
                // AddExport will throw if the same external name is exported multiple times from the state object.
                if (matchesUnmangled.first != pColInfo->m_ExportNameUnmangledToMangled.end())
                {
                    for (auto matchUnmangledToMangled = matchesUnmangled.first; matchUnmangledToMangled != matchesUnmangled.second; matchUnmangledToMangled++)
                    {
                        auto matchMangledExportInfo = pColInfo->m_ExportInfoMap.find(matchUnmangledToMangled->second);
                        assert(matchMangledExportInfo != pColInfo->m_ExportInfoMap.end());

                        if (pCollection->pExports[i].ExportToRename)
                        {
                            // (1) - do a rename
                            AddExportWrapper(RenameMangledName(matchUnmangledToMangled->second, InternalName, pCollection->pExports[i].Name),
                                pCollection->pExports[i].Name, matchUnmangledToMangled->second, matchMangledExportInfo->second);
                            OldMangledNamesRenamed.insert(matchUnmangledToMangled->second);
                        }
                        else
                        {
                            // (3) - no rename
                            AddExportWrapper(matchUnmangledToMangled->second, InternalName, matchUnmangledToMangled->second, matchMangledExportInfo->second);
                        }
                        ExportMissing.erase(&pCollection->pExports[i]);
                    }
                }
                else
                {
                    auto matchMangledExportInfo = pColInfo->m_ExportInfoMap.find(pColInfo->LocalUniqueCopy(InternalName));
                    if (matchMangledExportInfo != pColInfo->m_ExportInfoMap.end())
                    {
                        if (pCollection->pExports[i].ExportToRename)
                        {
                            // (2) - do a rename
                            auto mangledOriginalName = pColInfo->LocalUniqueCopy(pCollection->pExports[i].ExportToRename);
                            auto unmangledOriginalExportName = pColInfo->m_ExportNameMangledToUnmangled.find(mangledOriginalName);
                            assert(unmangledOriginalExportName != pColInfo->m_ExportNameMangledToUnmangled.end());
                            AddExportWrapper(RenameMangledName(pCollection->pExports[i].ExportToRename, unmangledOriginalExportName->second, pCollection->pExports[i].Name),
                                pCollection->pExports[i].Name, mangledOriginalName, matchMangledExportInfo->second);
                            OldMangledNamesRenamed.insert(mangledOriginalName);
                        }
                        else
                        {
                            // (4) - no rename
                            auto mangledOriginalName = pColInfo->LocalUniqueCopy(pCollection->pExports[i].Name);
                            auto unmangledOriginalExportName = pColInfo->m_ExportNameMangledToUnmangled.find(mangledOriginalName);
                            assert(unmangledOriginalExportName != pColInfo->m_ExportNameMangledToUnmangled.end());
                            AddExportWrapper(pCollection->pExports[i].Name, unmangledOriginalExportName->second, mangledOriginalName, matchMangledExportInfo->second);
                        }
                        ExportMissing.erase(&pCollection->pExports[i]);
                    }
                }
            }
        }
        
        // Try hit groups
        {
            std::unordered_set<const D3D12_EXPORT_DESC*> HitGroupFound;
            for(auto& ex : ExportMissing)
            {
                LPCWSTR InternalName = ex->ExportToRename ? ex->ExportToRename : ex->Name;
                auto match = pColInfo->m_HitGroups.find(pColInfo->LocalUniqueCopy(InternalName));
                if(match != pColInfo->m_HitGroups.end())
                {
                    D3D12_HIT_GROUP_DESC newHgDesc = *match->second;
                    newHgDesc.HitGroupExport = ex->Name;
                    // See if the constituent shaders were renamed
                    const CWrappedHitGroup& hgDesc = *match->second;
                    for (UINT s = 0; s < 3; s++)
                    {
                        LPCWSTR pDependency = nullptr;
                        LPCWSTR DependencyType = nullptr;
                        switch (s)
                        {
                        case 0:
                            pDependency = hgDesc.AnyHitShaderImport;
                            DependencyType = L"AnyHitShaderImport";                      
                            break;
                        case 1:
                            pDependency = hgDesc.ClosestHitShaderImport;
                            DependencyType = L"ClosestHitShaderImport";
                            break;
                        case 2:
                            pDependency = hgDesc.IntersectionShaderImport;
                            DependencyType = L"IntersectionShaderImport";
                            break;
                        default:
                            assert(false);
                            break;
                        }
                        if (!pDependency)
                        {
                            continue;
                        }
                        // Was this dependency renamed?
                        LPCWSTR pName = nullptr;
                        auto matches = ExportsToUse.equal_range(pDependency);

                        size_t count = ExportsToUse.count(pDependency);
                        switch (count)
                        {
                        case 0:
                            // Currently an unresolved export, ok.
                            pName = pDependency;
                            break;
                        case 1:
                        {
                            auto match = ExportsToUse.find(pDependency);
                            pName = match->second->Name;
                            break;
                        }
                        default:
                            LOG_ERROR(L"HitGroupExport \"" << hgDesc.HitGroupExport <<
                                L"\" imports " << DependencyType << L" named " << PrettyPrintPossiblyMangledName(pDependency) <<
                                L" which is being renamed as part of collection creation to multiple export names. "
                                L"so it is ambiguous which rename applies to " << PrettyPrintPossiblyMangledName(pDependency) << L".");
                            break;
                        }
                        if (pName)
                        {
                            switch (s)
                            {
                            case 0:
                                newHgDesc.AnyHitShaderImport = pName;
                                break;
                            case 1:
                                newHgDesc.ClosestHitShaderImport = pName;
                                break;
                            case 2:
                                newHgDesc.IntersectionShaderImport = pName;
                                break;
                            default:
                                assert(false);
                                break;
                            }                        
                        }
                    }
                    for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
                    {
                        for(auto a : match->second->m_Associations[i])
                        {
                            ReferencedAssociations[i].insert({a,LocalUniqueCopy(newHgDesc.HitGroupExport)});
                            UniqueReferencedAssociations[i].insert(a);
                        }
                    }
                    OldMangledNamesExported.insert(match->first);
                    if(ex->ExportToRename)
                    {
                        OldMangledNamesRenamed.insert(match->first);
                    }
                    AddHitGroup(&newHgDesc,hgDesc.m_pOwningStateObject);
                    HitGroupFound.insert(ex);
                } // else unresolved reference so far, ok                
            }
            
            for(auto& found : HitGroupFound)
            {
                ExportMissing.erase(found);
            }
        }

        // Add associations
        for(ASSOCIATEABLE_SUBOBJECT_NAME i = (ASSOCIATEABLE_SUBOBJECT_NAME)0; i < NUM_ASSOCIATEABLE_SUBOBJECT_TYPES; ((UINT&)i)++)
        {
            for(auto& association : pColInfo->m_SubobjectToExportsAssociations[i])
            {
                if(association.m_Exports.size() == 0)
                {
                    continue; // skip default associations as they don't have affect outside their scope
                }
                CWrappedAssociation* pNewOrExistingAssociation = nullptr;
                for(auto& existing : m_SubobjectToExportsAssociations[i])
                {
                    if(association.m_pSubobject->Compare(existing.m_pSubobject))
                    {
                        // found a matching existing association
                        pNewOrExistingAssociation = &existing;
                    }
                }
                if(!pNewOrExistingAssociation)
                {
                    m_SubobjectToExportsAssociations[i].emplace_back();
                    pNewOrExistingAssociation = &m_SubobjectToExportsAssociations[i].back();
                    pNewOrExistingAssociation->m_pSubobject = TrackAssociateableSubobject(
                        association.m_SubobjectType,association.m_pSubobject->m_LocalSubobjectDefinition.pDesc,nullptr);
                    pNewOrExistingAssociation->m_SubobjectType = association.m_SubobjectType;

                }
                for(auto ex : association.m_Exports)
                {
                    // Export only if it wasn't in the collection's incoming export list and renamed,
                    // and it isn't an export that was resolved in the old collection but not now.
                    // The destination of these renames handled by the UniqueReferencedAssociations 
                    // processing further below.
                    bool bOldNameNoLongerExists = 
                            ((pColInfo->m_ExportInfoMap.find(ex) != pColInfo->m_ExportInfoMap.end()) &&
                             (OldMangledNamesExported.find(ex) == OldMangledNamesExported.end()));
                    bool bOldNameWasRenamed = (OldMangledNamesRenamed.find(ex) != OldMangledNamesRenamed.end());
                    if(!(bOldNameNoLongerExists || bOldNameWasRenamed))
                    {
                        pNewOrExistingAssociation->m_Exports.insert(LocalUniqueCopy(ex));
                    }
                }
                OldToNewWrappedAssociations.insert({&association,pNewOrExistingAssociation});
            }

            for(auto association : UniqueReferencedAssociations[i])
            {
                // Make a new wrapped association for each unique referenced wrapped association in collection
                auto match = OldToNewWrappedAssociations.find(association);
                CWrappedAssociation* pNewAssociation = nullptr;
                if(match == OldToNewWrappedAssociations.end()) // didn't get added above because it was an empty export list, explicit default association
                {
                    m_SubobjectToExportsAssociations[i].emplace_back();
                    pNewAssociation = &m_SubobjectToExportsAssociations[i].back();
                    pNewAssociation->m_pSubobject = TrackAssociateableSubobject(
                        association->m_SubobjectType,association->m_pSubobject->m_LocalSubobjectDefinition.pDesc,nullptr);
                    pNewAssociation->m_SubobjectType = association->m_SubobjectType;
                }
                else
                {
                    pNewAssociation = match->second;
                }

                // For each export referencing this association, point it to the new local wrapper
                auto refs = ReferencedAssociations[i].equal_range(association);
                assert(refs.first != ReferencedAssociations[i].end());
                for(auto ref = refs.first; ref != refs.second; ref++)
                {
                    pNewAssociation->m_Exports.insert(ref->second);
                }
            }
        }      

#ifdef INCLUDE_MESSAGE_LOG
        for(auto& ex : ExportMissing)
        {
            LPCWSTR InternalName = ex->ExportToRename ? ex->ExportToRename : ex->Name;
            size_t i = (ex - &pCollection->pExports[0]);
            LOG_ERROR(L"Manually listed export [" << i << L"], " << PrettyPrintPossiblyMangledName(InternalName) << L", doesn't exist in collection " << pCollection->pExistingCollection << L".");
        }
#else
        if(ExportMissing.size())
        {
            LOG_ERROR_NOMESSAGE;
        }
#endif 
    }
}