// Copyright Epic Games, Inc. All Rights Reserved. #include "DependsNode.h" #include "AssetRegistry/AssetRegistryState.h" #include "AssetRegistryPrivate.h" #include "Misc/StringBuilder.h" void FDependsNode::PrintNode() const { UE_LOG(LogAssetRegistry, Log, TEXT("*** Printing DependsNode: %s ***"), *GetIdentifier().ToString()); UE_LOG(LogAssetRegistry, Log, TEXT("*** Dependencies:")); PrintDependencies(); UE_LOG(LogAssetRegistry, Log, TEXT("*** Referencers:")); PrintReferencers(); } void FDependsNode::PrintDependencies() const { TSet VisitedNodes; PrintDependenciesRecursive(TEXT(""), VisitedNodes); } void FDependsNode::PrintReferencers() const { TSet VisitedNodes; PrintReferencersRecursive(TEXT(""), VisitedNodes); } void FDependsNode::SetIdentifier(const FAssetIdentifier& InIdentifier) { Identifier = InIdentifier; if (!InIdentifier.PackageName.IsNone()) { TStringBuilder PackageNameStr(InPlace, InIdentifier.PackageName); bScriptPath = PackageNameStr.ToView().StartsWith(TEXT("/Script/"), ESearchCase::CaseSensitive) ? 1 : 0; } else { bScriptPath = 0; } } /** * Helper function for IterateDependencyList, which itself is a helper function for FDependsNode functions. * This function is given an index into one of the lists. All of the data for the list and how to interpret it is also * passed in. * The function looks of the set of DependencyProperty flag groups for the index, and iterates over that set, and * calls the callback on each flag group that matches the requested SearchFlags. */ template void EnumerateMatchingDependencyFlags(const TBitArray<>* FlagBits, UE::AssetRegistry::EDependencyProperty CategoryMask, UE::AssetRegistry::EDependencyProperty(*ByteToProperties)(uint8), int32 ListIndex, const UE::AssetRegistry::FDependencyQuery& SearchFlags, VisitorType&& Visitor) { using namespace UE::AssetRegistry; typedef TPropertyCombinationSet FCombinationSet; constexpr uint32 FlagSetWidth = FCombinationSet::StorageBitCount; EDependencyProperty RequiredProperties = SearchFlags.Required & CategoryMask; EDependencyProperty ExcludedProperties = SearchFlags.Excluded & CategoryMask; FCombinationSet DependsNodeFlagsSet(*FlagBits, ListIndex * FlagSetWidth); bool bDuplicate = false; for (uint32 DependencyFlagBits : DependsNodeFlagsSet) { EDependencyProperty DependencyProperties = ByteToProperties(static_cast(DependencyFlagBits)); if (!EnumHasAllFlags(DependencyProperties, RequiredProperties)) { continue; } if (EnumHasAnyFlags(DependencyProperties, ExcludedProperties)) { continue; } bool bPassesRequiredUnions = true; for (EDependencyProperty RequiredUnion : SearchFlags.RequiredUnions) { EDependencyProperty RequiredUnionProperty = RequiredUnion & CategoryMask; if (RequiredUnionProperty != EDependencyProperty::None && !EnumHasAnyFlags(DependencyProperties, RequiredUnionProperty)) { bPassesRequiredUnions = false; break; } } if (!bPassesRequiredUnions) { continue; } Visitor(DependencyProperties, bDuplicate); bDuplicate = true; } } /** * Helper function for GetDependencies, IterateOverDependencies, and others that need to find dependencies in * each of an FDependsNode's list of dependencies, while filtering by the list of DependencyProperties associated with * each dependency. * This function is given all of the data for the list and how to interpret it, and a requested SearchFlags. * The function iterates over all dependencies in the list, and iterates over the set of DependencyProperty flag * groups for each dependency, and calls the callback with the dependency and with each flag group on the dependency * that matches the requested SearchFlags. */ template void IterateDependencyList(CallbackType&& InCallback, UE::AssetRegistry::EDependencyCategory SearchCategory, const UE::AssetRegistry::FDependencyQuery& SearchFlags, UE::AssetRegistry::EDependencyCategory ListCategory, UE::AssetRegistry::EDependencyProperty CategoryMask, const TArray& Dependencies, const TBitArray<>* FlagBits, UE::AssetRegistry::EDependencyProperty(*ByteToProperties)(uint8), bool IsSorted) { using namespace UE::AssetRegistry; if (!(SearchCategory & ListCategory)) { return; } if (FlagWidth == 0) { for (int32 ListIndex = 0; ListIndex < Dependencies.Num(); ++ListIndex) { InCallback(Dependencies[ListIndex], ListCategory, EDependencyProperty::None, false /* bDuplicate */); } } else { for (int32 ListIndex = 0; ListIndex < Dependencies.Num(); ++ListIndex) { FDependsNode* DependsNode = Dependencies[ListIndex]; EnumerateMatchingDependencyFlags( FlagBits, CategoryMask, ByteToProperties, ListIndex, SearchFlags, [&InCallback, DependsNode, ListCategory](EDependencyProperty MatchingDependencyProperties, bool bDuplicate) { InCallback(DependsNode, ListCategory, MatchingDependencyProperties, bDuplicate); }); } } } /** * Helper function for IterateOverDependencies, ContainsDependency, and others that need to find a given dependency in * one of an FDependsNode's list of dependencies, while filtering by the list of DependencyProperties associated with * each dependency. * This function is given all of the data for the list and how to interpret it, and a requested SearchNode and * SearchFlags. * The function looks up the SearchNode in the list of dependencies, and iterates over the set of DependencyProperty flag * groups for each dependency, and calls the callback with each flag group on the dependency that matches the requested * SearchFlags. */ template void IterateDependencyList(CallbackType&& InCallback, const FDependsNode* SearchNode, UE::AssetRegistry::EDependencyCategory SearchCategory, const UE::AssetRegistry::FDependencyQuery& SearchFlags, UE::AssetRegistry::EDependencyCategory ListCategory, UE::AssetRegistry::EDependencyProperty CategoryMask, const TArray& Dependencies, const TBitArray<>* FlagBits, UE::AssetRegistry::EDependencyProperty(*ByteToProperties)(uint8), bool IsSorted) { using namespace UE::AssetRegistry; if (!(SearchCategory & ListCategory)) { return; } auto ReportIndex = [&](int32 ListIndex) { if (FlagWidth == 0) { InCallback(Dependencies[ListIndex], ListCategory, EDependencyProperty::None, false /* bDuplicate */); } else { FDependsNode* DependsNode = Dependencies[ListIndex]; EnumerateMatchingDependencyFlags( FlagBits, CategoryMask, ByteToProperties, ListIndex, SearchFlags, [&InCallback, DependsNode, ListCategory](EDependencyProperty MatchingDependencyProperties, bool bDuplicate) { InCallback(DependsNode, ListCategory, MatchingDependencyProperties, bDuplicate); }); } }; if (IsSorted) { int32 ListIndex = Algo::BinarySearch(Dependencies, SearchNode); if (ListIndex != INDEX_NONE) { ReportIndex(ListIndex); } } else { for (int32 ListIndex = 0, Num = Dependencies.Num(); ListIndex < Num; ++ListIndex) { if (Dependencies[ListIndex] == SearchNode) { ReportIndex(ListIndex); } } } } void FDependsNode::IterateOverDependencies(const FIterateDependenciesCallback& InCallback, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { using namespace UE::AssetRegistry; IterateDependencyList(InCallback, Category, Flags, EDependencyCategory::Package, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, ByteToPackageProperties, PackageIsSorted); IterateDependencyList(InCallback, Category, Flags, EDependencyCategory::SearchableName, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted); IterateDependencyList(InCallback, Category, Flags, EDependencyCategory::Manage, EDependencyProperty::ManageMask, ManageDependencies, &ManageFlags, ByteToManageProperties, ManageIsSorted); } void FDependsNode::IterateOverDependencies(const FIterateDependenciesCallback& InCallback, const FDependsNode* DependsNode, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { using namespace UE::AssetRegistry; IterateDependencyList(InCallback, DependsNode, Category, Flags, EDependencyCategory::Package, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, ByteToPackageProperties, PackageIsSorted); IterateDependencyList(InCallback, DependsNode, Category, Flags, EDependencyCategory::SearchableName, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted); IterateDependencyList(InCallback, DependsNode, Category, Flags, EDependencyCategory::Manage, EDependencyProperty::ManageMask, ManageDependencies, &ManageFlags, ByteToManageProperties, ManageIsSorted); } void FDependsNode::GetDependencies(TArray& OutDependencies, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { IterateOverDependencies([&OutDependencies](FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory InCategory, UE::AssetRegistry::EDependencyProperty InProperties, bool bDuplicate) { if (!bDuplicate) { OutDependencies.Add(InDependency); } }, Category, Flags); } void FDependsNode::GetDependencies(TArray& OutDependencies, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { IterateOverDependencies([&OutDependencies](const FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory InCategory, UE::AssetRegistry::EDependencyProperty InProperties, bool bDuplicate) { if (!bDuplicate) { OutDependencies.Add(InDependency->GetIdentifier()); } }, Category, Flags); } void FDependsNode::GetDependencies(TArray& OutDependencies, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { IterateOverDependencies([&OutDependencies](const FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory InCategory, UE::AssetRegistry::EDependencyProperty InProperties, bool bDuplicate) { OutDependencies.Add(FAssetDependency{ InDependency->GetIdentifier(), InCategory, InProperties }); }, Category, Flags); } void FDependsNode::GetReferencers(TArray& OutReferencers, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { for (FDependsNode* Referencer : Referencers) { bool bShouldAdd = false; // If type specified, filter if (Category != UE::AssetRegistry::EDependencyCategory::All || Flags.Required != UE::AssetRegistry::EDependencyProperty::None || Flags.Excluded != UE::AssetRegistry::EDependencyProperty::None || !Flags.RequiredUnions.IsEmpty()) { Referencer->IterateOverDependencies([&bShouldAdd](const FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory InCategory, UE::AssetRegistry::EDependencyProperty InProperties, bool bDuplicate) { bShouldAdd = true; }, this, Category, Flags); } else { bShouldAdd = true; } if (bShouldAdd) { OutReferencers.Add(Referencer); } } } void FDependsNode::GetReferencers(TArray& OutReferencers, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { for (FDependsNode* Referencer : Referencers) { Referencer->IterateOverDependencies([&OutReferencers, Referencer](const FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory InCategory, UE::AssetRegistry::EDependencyProperty InProperties, bool bDuplicate) { OutReferencers.Add(FAssetDependency{ Referencer->GetIdentifier(), InCategory, InProperties }); }, this, Category, Flags); } } void FDependsNode::GetPackageReferencers(TArray>& OutReferencers) { for (FDependsNode* Referencer : Referencers) { int32 Index = Algo::BinarySearch(Referencer->PackageDependencies, this); if (Index != INDEX_NONE) { FPackageFlagSet FlagList(Referencer->PackageFlags, Index * PackageFlagSetWidth); OutReferencers.Emplace(Referencer->GetIdentifier(), FlagList); } } } template void AddDependency(FDependsNode* InDependency, UE::AssetRegistry::EDependencyProperty AddProperties, UE::AssetRegistry::EDependencyProperty CategoryMask, TArray& Dependencies, TBitArray<>* FlagBits, uint8(*PropertiesToByte)(UE::AssetRegistry::EDependencyProperty), bool IsSorted) { using namespace UE::AssetRegistry; bool bIsNew = false; int32 ListIndex = IsSorted ? Algo::LowerBound(Dependencies, InDependency) : Dependencies.Num(); if (Dependencies.Num() <= ListIndex || Dependencies[ListIndex] != InDependency) { Dependencies.Insert(InDependency, ListIndex); bIsNew = true; } if (FlagWidth == 0) { return; } else { uint8 DependencyProperties = PropertiesToByte(CategoryMask & AddProperties); typedef TPropertyCombinationSet FCombinationSet; constexpr uint32 FlagSetWidth = FCombinationSet::StorageBitCount; FCombinationSet DependsNodeFlagsSet; if (bIsNew) { FlagBits->InsertUninitialized(ListIndex * FlagSetWidth, FlagSetWidth); } else { DependsNodeFlagsSet.Load(*FlagBits, ListIndex * FlagSetWidth); } DependsNodeFlagsSet.Add(DependencyProperties); DependsNodeFlagsSet.Save(*FlagBits, ListIndex * FlagSetWidth); } } void FDependsNode::AddDependency(FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory Category, UE::AssetRegistry::EDependencyProperty Properties) { using namespace UE::AssetRegistry; if (EnumHasAnyFlags(Category, UE::AssetRegistry::EDependencyCategory::Package)) { ::AddDependency(InDependency, Properties, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, PackagePropertiesToByte, PackageIsSorted); // It is illegal to try to add a dependency as more than one category at a time check((Category & ~EDependencyCategory::Package) == EDependencyCategory::None); } else if (EnumHasAnyFlags(Category, EDependencyCategory::SearchableName)) { ::AddDependency(InDependency, Properties, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted); // It is illegal to try to add a dependency as more than one category at a time check((Category & ~EDependencyCategory::SearchableName) == EDependencyCategory::None); } else if (EnumHasAnyFlags(Category, UE::AssetRegistry::EDependencyCategory::Manage)) { ::AddDependency(InDependency, Properties, EDependencyProperty::ManageMask, ManageDependencies, &ManageFlags, ManagePropertiesToByte, ManageIsSorted); // It is illegal to try to add a dependency as more than one category at a time check((Category & ~EDependencyCategory::Manage) == EDependencyCategory::None); } else { // It is illegal to try to add a dependency without a category check(false); } } void FDependsNode::AddPackageDependencySet(FDependsNode* InDependency, const FPackageFlagSet& PropertyCombinationSet) { int32 Index = PackageIsSorted ? Algo::LowerBound(PackageDependencies, InDependency) : PackageDependencies.Num(); if (PackageDependencies.Num() <= Index || PackageDependencies[Index] != InDependency) { PackageDependencies.Insert(InDependency, Index); PackageFlags.InsertUninitialized(Index * PackageFlagSetWidth, PackageFlagSetWidth); } PropertyCombinationSet.Save(PackageFlags, Index * PackageFlagSetWidth); } void FDependsNode::AddReferencer(FDependsNode* InReferencer) { using namespace UE::AssetRegistry; ::AddDependency<0>(InReferencer, EDependencyProperty::None, EDependencyProperty::None, Referencers, nullptr, nullptr, ReferencersIsSorted); } template void RemoveDependency(FDependsNode* InDependency, TArray& Dependencies, TBitArray<>* FlagBits, bool IsSorted, bool bAllowShrinking) { const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No; check(FlagWidth == 0 || FlagBits != nullptr); if (IsSorted) { int32 ListIndex = Algo::LowerBound(Dependencies, InDependency); if (Dependencies.Num() <= ListIndex || Dependencies[ListIndex] != InDependency) { return; } Dependencies.RemoveAt(ListIndex, ShrinkPolicy); if (FlagWidth != 0) { constexpr uint32 FlagSetWidth = TPropertyCombinationSet::StorageBitCount; FlagBits->RemoveAt(ListIndex * FlagSetWidth, FlagSetWidth); } } else { // When unsorted, in addition to be unsorted, the list may contain multiple elements of a single dependency for (int32 ListIndex = 0; ListIndex < Dependencies.Num(); ) { if (Dependencies[ListIndex] != InDependency) { ++ListIndex; } else { Dependencies.RemoveAtSwap(ListIndex, ShrinkPolicy); if (FlagWidth != 0) { constexpr uint32 FlagSetWidth = TPropertyCombinationSet::StorageBitCount; FlagBits->RemoveAtSwap(ListIndex * FlagSetWidth, FlagSetWidth); } } } } } void FDependsNode::RemoveDependency(FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory Category) { if (!!(Category & UE::AssetRegistry::EDependencyCategory::Package)) { ::RemoveDependency(InDependency, PackageDependencies, &PackageFlags, PackageIsSorted, bAllowShrinking); } if (!!(Category & UE::AssetRegistry::EDependencyCategory::SearchableName)) { ::RemoveDependency(InDependency, NameDependencies, nullptr, SearchableNameIsSorted, bAllowShrinking); } if (!!(Category & UE::AssetRegistry::EDependencyCategory::Manage)) { ::RemoveDependency(InDependency, ManageDependencies, &ManageFlags, ManageIsSorted, bAllowShrinking); } } void FDependsNode::RemoveReferencer(FDependsNode* InReferencer) { ::RemoveDependency<0>(InReferencer, Referencers, nullptr, ReferencersIsSorted, bAllowShrinking); } void FDependsNode::RefreshReferencers() { if (IsReferencersSorted()) { Referencers.RemoveAll([this](FDependsNode* Referencer) { return !Referencer->ContainsDependency(this); }); } else { Referencers.RemoveAllSwap([this](FDependsNode* Referencer) { return !Referencer->ContainsDependency(this); }); } } void FDependsNode::ClearDependencies(UE::AssetRegistry::EDependencyCategory Category) { using namespace UE::AssetRegistry; if (!!(Category & EDependencyCategory::Package)) { if (bAllowShrinking) { PackageDependencies.Empty(); PackageFlags.Empty(); } else { PackageDependencies.Empty(PackageDependencies.Max()); PackageFlags.Reset(); } } if (!!(Category & EDependencyCategory::SearchableName)) { NameDependencies.Empty(bAllowShrinking ? 0 : NameDependencies.Max()); } if (!!(Category & EDependencyCategory::Manage)) { if (bAllowShrinking) { ManageDependencies.Empty(); ManageFlags.Empty(); } else { ManageDependencies.Empty(ManageDependencies.Max()); ManageFlags.Reset(); } } } void FDependsNode::ClearReferencers() { Referencers.Empty(bAllowShrinking ? 0 : Referencers.Max()); } void FDependsNode::RemoveManageReferencesToNode() { const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No; UE::AssetRegistry::EDependencyCategory InCategory = UE::AssetRegistry::EDependencyCategory::Manage; // Iterate referencers array, possibly removing for (int32 i = Referencers.Num() - 1; i >= 0; i--) { Referencers[i]->RemoveDependency(this, InCategory); if (!Referencers[i]->ContainsDependency(this, UE::AssetRegistry::EDependencyCategory::All & ~InCategory)) { if (ReferencersIsSorted) { Referencers.RemoveAt(i, ShrinkPolicy); } else { Referencers.RemoveAtSwap(i, ShrinkPolicy); } } } } template void RemoveAll(const TUniqueFunction& ShouldRemove, TArray& Dependencies, TBitArray<>* FlagBits, bool IsSorted, EAllowShrinking ShrinkPolicy) { check(FlagWidth == 0 || FlagBits != nullptr); if (IsSorted) { if (FlagWidth == 0) { Dependencies.RemoveAll(ShouldRemove); } else { // This block is the same functionality as TArray::RemoveAll, // but it needs to handle removing the corresponding FlagBits const int32 OriginalNum = Dependencies.Num(); if (!OriginalNum) { return; // nothing to do, loop assumes one item so need to deal with this edge case here } constexpr uint32 FlagSetWidth = TPropertyCombinationSet::StorageBitCount; int32 WriteIndex = 0; int32 ReadIndex = 0; FDependsNode** DependencyData = Dependencies.GetData(); // When calling ShouldRemove, use a ! to guarantee it can't be anything other than zero or one bool bKeep = !ShouldRemove(DependencyData[ReadIndex]); do { int32 RunStartIndex = ReadIndex++; while (ReadIndex < OriginalNum && bKeep == !ShouldRemove(DependencyData[ReadIndex])) { ReadIndex++; } int32 RunLength = ReadIndex - RunStartIndex; checkSlow(RunLength > 0); if (bKeep) { // this was a keep run, we need to move it if (WriteIndex != RunStartIndex) { FMemory::Memmove(&DependencyData[WriteIndex], &DependencyData[RunStartIndex], sizeof(DependencyData[0]) * RunLength); FlagBits->SetRangeFromRange(WriteIndex * FlagSetWidth, FlagSetWidth * RunLength, FlagBits->GetData(), RunStartIndex * FlagSetWidth); } WriteIndex += RunLength; } bKeep = !bKeep; } while (ReadIndex < OriginalNum); Dependencies.SetNum(WriteIndex, ShrinkPolicy); FlagBits->SetNumUninitialized(WriteIndex * FlagSetWidth); } } else { if (FlagWidth == 0) { Dependencies.RemoveAllSwap(ShouldRemove, ShrinkPolicy); } else { constexpr uint32 FlagSetWidth = TPropertyCombinationSet::StorageBitCount; for (int32 ListIndex = 0; ListIndex < Dependencies.Num(); ) { if (ShouldRemove(Dependencies[ListIndex])) { Dependencies.RemoveAtSwap(ListIndex, ShrinkPolicy); FlagBits->RemoveAtSwap(ListIndex * FlagSetWidth, FlagSetWidth); } else { ++ListIndex; } } } } } void FDependsNode::RemoveLinks(const TUniqueFunction& ShouldRemove) { const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No; ::RemoveAll(ShouldRemove, PackageDependencies, &PackageFlags, PackageIsSorted, ShrinkPolicy); ::RemoveAll(ShouldRemove, NameDependencies, nullptr, SearchableNameIsSorted, ShrinkPolicy); ::RemoveAll(ShouldRemove, ManageDependencies, &ManageFlags, ManageIsSorted, ShrinkPolicy); ::RemoveAll<0>(ShouldRemove, Referencers, nullptr, ReferencersIsSorted, ShrinkPolicy); } bool FDependsNode::ContainsDependency(const FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory Category, const UE::AssetRegistry::FDependencyQuery& Flags) const { using namespace UE::AssetRegistry; bool bExists = false; if (!!(Category & EDependencyCategory::Package)) { IterateDependencyList([&bExists] (const FDependsNode* DependsNode, EDependencyCategory MatchingCategory, EDependencyProperty MatchingDependencyProperties, bool bDuplicate) { bExists = true; }, InDependency, Category, Flags, EDependencyCategory::Package, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, ByteToPackageProperties, PackageIsSorted); if (bExists) { return true; } } if (!!(Category & EDependencyCategory::SearchableName)) { IterateDependencyList([&bExists] (const FDependsNode* DependsNode, EDependencyCategory MatchingCategory, EDependencyProperty MatchingDependencyProperties, bool bDuplicate) { bExists = true; }, InDependency, Category, Flags, EDependencyCategory::SearchableName, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted); if (bExists) { return true; } } if (!!(Category & EDependencyCategory::Manage)) { IterateDependencyList([&bExists] (const FDependsNode* DependsNode, EDependencyCategory MatchingCategory, EDependencyProperty MatchingDependencyProperties, bool bDuplicate) { bExists = true; }, InDependency, Category, Flags, EDependencyCategory::Manage, EDependencyProperty::ManageMask, ManageDependencies, &ManageFlags, ByteToManageProperties, ManageIsSorted); if (bExists) { return true; } } return false; } void FDependsNode::PrintDependenciesRecursive(const FString& Indent, TSet& VisitedNodes) const { PRAGMA_DISABLE_DEPRECATION_WARNINGS if (!IsThisNotNull(this, "FDependsNode::PrintDependenciesRecursive")) PRAGMA_ENABLE_DEPRECATION_WARNINGS { UE_LOG(LogAssetRegistry, Log, TEXT("%sNULL"), *Indent); } else if ( VisitedNodes.Contains(this) ) { UE_LOG(LogAssetRegistry, Log, TEXT("%s[CircularReferenceTo]%s"), *Indent, *GetIdentifier().ToString()); } else { UE_LOG(LogAssetRegistry, Log, TEXT("%s%s"), *Indent, *GetIdentifier().ToString()); VisitedNodes.Add(this); IterateOverDependencies([&Indent, &VisitedNodes](FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory InCategory, UE::AssetRegistry::EDependencyProperty InProperties, bool bDuplicate) { if (!bDuplicate) { InDependency->PrintDependenciesRecursive(Indent + TEXT(" "), VisitedNodes); } }); } } void FDependsNode::PrintReferencersRecursive(const FString& Indent, TSet& VisitedNodes) const { PRAGMA_DISABLE_DEPRECATION_WARNINGS if (!IsThisNotNull(this, "FDependsNode::PrintReferencersRecursive")) PRAGMA_ENABLE_DEPRECATION_WARNINGS { UE_LOG(LogAssetRegistry, Log, TEXT("%sNULL"), *Indent); } else if ( VisitedNodes.Contains(this) ) { UE_LOG(LogAssetRegistry, Log, TEXT("%s[CircularReferenceTo]%s"), *Indent, *GetIdentifier().ToString()); } else { UE_LOG(LogAssetRegistry, Log, TEXT("%s%s"), *Indent, *GetIdentifier().ToString()); VisitedNodes.Add(this); for (FDependsNode* Node : Referencers) { Node->PrintReferencersRecursive(Indent + TEXT(" "), VisitedNodes); } } } int32 FDependsNode::GetConnectionCount() const { return PackageDependencies.Num() + NameDependencies.Num() + ManageDependencies.Num() + Referencers.Num(); } void FDependsNode::SerializeSave(FArchive& Ar, const TUniqueFunction& GetSerializeIndexFromNode, FSaveScratch& Scratch, const FAssetRegistrySerializationOptions& Options) const { Ar << const_cast(Identifier); auto WriteDependencies = [&Ar, &GetSerializeIndexFromNode, &Scratch](const TArray& InDependencies, const TBitArray<>* InFlagBits, int FlagSetWidth, bool bAsReferencer) { TArray& SortInfos = Scratch.SortInfos; TArray& OutDependencies = Scratch.OutDependencies; TBitArray<>& OutFlagBits = Scratch.OutFlagBits; SortInfos.Reset(InDependencies.Num()); for (int32 ListIndex = 0, End = InDependencies.Num(); ListIndex < End; ++ListIndex) { int32 SerializeIndex = GetSerializeIndexFromNode(InDependencies[ListIndex], bAsReferencer); if (SerializeIndex < 0) { continue; } SortInfos.Add({ SerializeIndex, ListIndex }); } // Sort the serialized dependencies to make the output deterministic. // OutDependencies and OutFlagBits (if present) are associated arrays and have to be sorted together Algo::Sort(SortInfos, [](const FSaveScratch::FSortInfo& A, const FSaveScratch::FSortInfo& B) { return A.SerializeIndex < B.SerializeIndex; }); int32 NumOutDependencies = SortInfos.Num(); OutDependencies.Reset(NumOutDependencies); for (const FSaveScratch::FSortInfo& SortInfo : SortInfos) { OutDependencies.Add(SortInfo.SerializeIndex); } Ar << OutDependencies; if (InFlagBits) { OutFlagBits.Reset(); for (const FSaveScratch::FSortInfo& SortInfo : SortInfos) { OutFlagBits.AddRange(*InFlagBits, FlagSetWidth, SortInfo.ListIndex * FlagSetWidth); } // We don't use BitArray::operator<< because we want to avoid the reallocation that operator<< does on load // and we want to avoid saving BitArray.Num when it can be derived from NumDependencies. int32 NumFlagBits = FlagSetWidth * NumOutDependencies; check(OutFlagBits.Num() == NumFlagBits); int32 NumFlagWords = FBitSet::CalculateNumWords(NumFlagBits); Ar.Serialize(OutFlagBits.GetData(), NumFlagWords * sizeof(uint32)); } }; WriteDependencies(PackageDependencies, &PackageFlags, PackageFlagSetWidth, false); WriteDependencies(Options.bSerializeSearchableNameDependencies ? NameDependencies : FDependsNodeList(), nullptr, 0, false); WriteDependencies(Options.bSerializeManageDependencies ? ManageDependencies : FDependsNodeList(), Options.bSerializeManageDependencies ? &ManageFlags : nullptr, ManageFlagSetWidth, false); WriteDependencies(Referencers, nullptr, 0, true); } void FDependsNode::SerializeLoad(FArchive& Ar, const TUniqueFunction& GetNodeFromSerializeIndex, FLoadScratch& Scratch) { Ar << Identifier; SetIdentifier(Identifier); auto ReadDependencies = [&Ar, &GetNodeFromSerializeIndex, &Scratch](TArray& OutDependencies, TBitArray<>* OutFlagBits, int FlagSetWidth) { TArray& InDependencies = Scratch.InDependencies; TArray& InFlagBits = Scratch.InFlagBits; TArray& PointerDependencies = Scratch.PointerDependencies; TArray& SortIndexes = Scratch.SortIndexes; int32 NumFlagBits = 0; InDependencies.Reset(); Ar << InDependencies; int32 NumDependencies = InDependencies.Num(); if (OutFlagBits) { // We don't use BitArray::operator<< because we want to avoid the reallocation that operator<< does on load // and we want to avoid saving BitArray.Num when it can be derived from NumDependencies. NumFlagBits = FlagSetWidth * NumDependencies; const int32 NumFlagWords = FBitSet::CalculateNumWords(NumFlagBits); InFlagBits.SetNumUninitialized(NumFlagWords); Ar.Serialize(InFlagBits.GetData(), NumFlagWords * sizeof(uint32)); } PointerDependencies.Reset(NumDependencies); for (int32 SerializeIndex : InDependencies) { FDependsNode* DependsNode = GetNodeFromSerializeIndex(SerializeIndex); if (!DependsNode) { Ar.SetError(); return; } PointerDependencies.Add(DependsNode); } SortIndexes.Reset(NumDependencies); for (int Index = 0; Index < NumDependencies; ++Index) { SortIndexes.Add(Index); } Algo::Sort(SortIndexes, [&PointerDependencies](int32 A, int32 B) { return PointerDependencies[A] < PointerDependencies[B]; }); OutDependencies.Empty(NumDependencies); for (int32 SortIndex : SortIndexes) { OutDependencies.Add(PointerDependencies[SortIndex]); } if (OutFlagBits) { OutFlagBits->SetNumUninitialized(NumFlagBits); uint32* InFlagBitsData = InFlagBits.GetData(); for (int32 WriteIndex = 0; WriteIndex < NumDependencies; ++WriteIndex) { int32 ReadIndex = SortIndexes[WriteIndex]; OutFlagBits->SetRangeFromRange(WriteIndex * FlagSetWidth, FlagSetWidth, InFlagBitsData, ReadIndex * FlagSetWidth); } } }; ReadDependencies(PackageDependencies, &PackageFlags, PackageFlagSetWidth); ReadDependencies(NameDependencies, nullptr, 0); ReadDependencies(ManageDependencies, &ManageFlags, ManageFlagSetWidth); ReadDependencies(Referencers, nullptr, 0); SetIsDependenciesInitialized(true); } void FDependsNode::SerializeLoad_BeforeFlags(FArchive& Ar, FAssetRegistryVersion::Type Version, FDependsNode* PreallocatedDependsNodeDataBuffer, int32 NumDependsNodes, bool bSerializeDependencies, uint32 HardBits, uint32 SoftBits, uint32 HardManageBits, uint32 SoftManageBits) { Ar << Identifier; SetIdentifier(Identifier); int32 NumHard, NumSoft, NumName, NumSoftManage, NumHardManage, NumReferencers; Ar << NumHard; Ar << NumSoft; Ar << NumName; Ar << NumSoftManage; if (Version < FAssetRegistryVersion::AddedHardManage) { NumHardManage = 0; } else { Ar << NumHardManage; } Ar << NumReferencers; // Empty dependency arrays and reserve space PackageDependencies.Empty(NumHard + NumSoft); NameDependencies.Empty(bSerializeDependencies ? NumName : 0); ManageDependencies.Empty(bSerializeDependencies ? NumSoftManage + NumHardManage : 0); Referencers.Empty(NumReferencers); auto SerializeNodeArray = [&Ar, PreallocatedDependsNodeDataBuffer, NumDependsNodes](int32 Num, TArray& OutNodes, TBitArray<>* OutFlagBits, uint32 FlagSetWidth, uint32 FlagSetBits, bool bShouldOverwriteFlag, bool bAllowWrite) { for (int32 DependencyIndex = 0; DependencyIndex < Num; ++DependencyIndex) { int32 Index = 0; Ar << Index; if (Index < 0 || Index >= NumDependsNodes) { Ar.SetError(); return; } if (bAllowWrite) { FDependsNode* DependsNode = &PreallocatedDependsNodeDataBuffer[Index]; int32 OutNodeIndex = Algo::LowerBound(OutNodes, DependsNode); if (OutNodes.Num() <= OutNodeIndex || OutNodes[OutNodeIndex] != DependsNode) { OutNodes.Insert(DependsNode, OutNodeIndex); if (OutFlagBits) { OutFlagBits->InsertRange(&FlagSetBits, OutNodeIndex * FlagSetWidth, FlagSetWidth); } } else if (bShouldOverwriteFlag) { if (OutFlagBits) { OutFlagBits->SetRangeFromRange(OutNodeIndex * FlagSetWidth, FlagSetWidth, &FlagSetBits); } } } } }; // Read the bits for each type, but don't write anything if serializing that type isn't allowed SerializeNodeArray(NumHard, PackageDependencies, &PackageFlags, PackageFlagSetWidth, HardBits, true, true); SerializeNodeArray(NumSoft, PackageDependencies, &PackageFlags, PackageFlagSetWidth, SoftBits, false, true); SerializeNodeArray(NumName, NameDependencies, nullptr, 0, 0, true, bSerializeDependencies); SerializeNodeArray(NumSoftManage, ManageDependencies, &ManageFlags, ManageFlagSetWidth, SoftManageBits, true, bSerializeDependencies); SerializeNodeArray(NumHardManage, ManageDependencies, &ManageFlags, ManageFlagSetWidth, HardManageBits, true, bSerializeDependencies); SerializeNodeArray(NumReferencers, Referencers, nullptr, 0, 0, true, true); SetIsDependenciesInitialized(true); } void FDependsNode::GetPropertySetBits_BeforeFlags(uint32& HardBits, uint32& SoftBits, uint32& HardManageBits, uint32& SoftManageBits) { { FDependsNode::FPackageFlagSet FlagSet; FlagSet.Add(PackagePropertiesToByte( UE::AssetRegistry::EDependencyProperty::Hard | UE::AssetRegistry::EDependencyProperty::Game | UE::AssetRegistry::EDependencyProperty::Build)); FlagSet.Save(&HardBits); } { FDependsNode::FPackageFlagSet FlagSet; FlagSet.Add(PackagePropertiesToByte( UE::AssetRegistry::EDependencyProperty::Game | UE::AssetRegistry::EDependencyProperty::Build)); FlagSet.Save(&SoftBits); } HardManageBits = 0x1; SoftManageBits = 0x0; } template void SortDependencyList(TArray& Dependencies, TBitArray<>* Flags, EAllowShrinking ShrinkPolicy) { const int32 Num = Dependencies.Num(); if (Num == 0) { return; } FDependsNode** const DependencyData = Dependencies.GetData(); if (FlagWidth == 0) { // Sort dependencies+ Algo::Sort(TArrayView(DependencyData, Num)); // Remove duplicates, which are now adjacent int32 WriteIndex; for (WriteIndex = 0; WriteIndex < Num - 1; ++WriteIndex) { if (DependencyData[WriteIndex] == DependencyData[WriteIndex + 1]) { break; } } if (WriteIndex < Num - 1) { ++WriteIndex; for (int32 ReadIndex = WriteIndex + 1; ReadIndex < Num; ++ReadIndex) { if (DependencyData[ReadIndex] != DependencyData[WriteIndex-1]) { DependencyData[WriteIndex++] = DependencyData[ReadIndex]; } } Dependencies.SetNum(WriteIndex, ShrinkPolicy); } } else { check(Flags); // Create an index array for sorting TArray Order; Order.SetNum(Num); for (int n = 0; n < Num; ++n) { Order[n] = n; } // Sort the index array Algo::Sort(Order, [&DependencyData](int32 A, int32 B) { return DependencyData[A] < DependencyData[B]; }); // Remove duplicate in the dependency array, which are now adjacent, and merge their corresponding flags typedef TPropertyCombinationSet FCombinationSet; constexpr uint32 FlagSetWidth = FCombinationSet::StorageBitCount; TArray NewDependencies; TBitArray<> NewFlags; NewDependencies.Reserve(Num); NewFlags.Reserve(Num*FlagSetWidth); int ReadIndex = Order[0]; NewDependencies.Add(DependencyData[ReadIndex]); NewFlags.AddRange(*Flags, FlagSetWidth, ReadIndex * FlagSetWidth); for (int OrderIndex = 1; OrderIndex < Num; ++OrderIndex) { ReadIndex = Order[OrderIndex]; FDependsNode* DependencyToAdd = DependencyData[ReadIndex]; if (DependencyToAdd != NewDependencies.Last()) { NewDependencies.Add(DependencyToAdd); NewFlags.AddRange(*Flags, FlagSetWidth, ReadIndex * FlagSetWidth); } else { const int ExistingBitIndex = (NewDependencies.Num() - 1) * FlagSetWidth; FCombinationSet ExistingSet; ExistingSet.Load(NewFlags, ExistingBitIndex); FCombinationSet NewSet; NewSet.Load(*Flags, ReadIndex * FlagSetWidth); ExistingSet.AddRange(NewSet); ExistingSet.Save(NewFlags, ExistingBitIndex); } } Swap(Dependencies, NewDependencies); Swap(*Flags, NewFlags); } } bool FDependsNode::IsDependencyListSorted(UE::AssetRegistry::EDependencyCategory Category) const { using namespace UE::AssetRegistry; if (Category == EDependencyCategory::Package) { return PackageIsSorted; } else if (Category == EDependencyCategory::SearchableName) { return SearchableNameIsSorted; } else if (Category == EDependencyCategory::Manage) { return ManageIsSorted; } else { check(false); return true; } } void FDependsNode::SetIsDependencyListSorted(UE::AssetRegistry::EDependencyCategory Category, bool bValue) { using namespace UE::AssetRegistry; const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No; if (!!(Category & EDependencyCategory::Package)) { if (bValue && PackageIsSorted == 0) { SortDependencyList(PackageDependencies, &PackageFlags, ShrinkPolicy); } PackageIsSorted = bValue ? 1 : 0; } if (!!(Category & EDependencyCategory::SearchableName)) { if (bValue && SearchableNameIsSorted == 0) { SortDependencyList(NameDependencies, nullptr, ShrinkPolicy); } SearchableNameIsSorted = bValue ? 1 : 0; } if (!!(Category & EDependencyCategory::Manage)) { if (bValue && ManageIsSorted == 0) { SortDependencyList(ManageDependencies, &ManageFlags, ShrinkPolicy); } ManageIsSorted = bValue ? 1 : 0; } } bool FDependsNode::IsReferencersSorted() const { return ReferencersIsSorted; } void FDependsNode::SetIsReferencersSorted(bool bValue) { if (bValue && ReferencersIsSorted == 0) { const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No; SortDependencyList<0>(Referencers, nullptr, ShrinkPolicy); } ReferencersIsSorted = bValue ? 1 : 0; } bool FDependsNode::IsDependenciesInitialized() const { return DependenciesInitialized; } void FDependsNode::SetIsDependenciesInitialized(bool bValue) { DependenciesInitialized = bValue ? 1 : 0; }