EM_Task/AssetRegistry/Private/DependsNode.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

957 lines
39 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DependsNode.h"
#include "AssetRegistryPrivate.h"
#include "AssetRegistry/AssetRegistryState.h"
void FDependsNode::PrintNode() const
{
UE_LOG(LogAssetRegistry, Log, TEXT("*** Printing DependsNode: %s ***"), *Identifier.ToString());
UE_LOG(LogAssetRegistry, Log, TEXT("*** Dependencies:"));
PrintDependencies();
UE_LOG(LogAssetRegistry, Log, TEXT("*** Referencers:"));
PrintReferencers();
}
void FDependsNode::PrintDependencies() const
{
TSet<const FDependsNode*> VisitedNodes;
PrintDependenciesRecursive(TEXT(""), VisitedNodes);
}
void FDependsNode::PrintReferencers() const
{
TSet<const FDependsNode*> VisitedNodes;
PrintReferencersRecursive(TEXT(""), VisitedNodes);
}
template <uint32 FlagWidth>
void IterateDependencyList(const FDependsNode::FIterateDependenciesCallback& InCallback, UE::AssetRegistry::EDependencyCategory SearchCategory, const UE::AssetRegistry::FDependencyQuery& SearchFlags,
UE::AssetRegistry::EDependencyCategory ListCategory, UE::AssetRegistry::EDependencyProperty CategoryMask, const TArray<FDependsNode*>& 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
{
EDependencyProperty RequiredProperties = SearchFlags.Required & CategoryMask;
EDependencyProperty ExcludedProperties = SearchFlags.Excluded & CategoryMask;
typedef TPropertyCombinationSet<FlagWidth> FCombinationSet;
constexpr uint32 FlagSetWidth = FCombinationSet::StorageBitCount;
for (int32 ListIndex = 0; ListIndex < Dependencies.Num(); ++ListIndex)
{
FDependsNode* DependsNode = Dependencies[ListIndex];
FCombinationSet DependsNodeFlagsSet(*FlagBits, ListIndex * FlagSetWidth);
bool bDuplicate = false;
for (uint32 DependencyFlagBits: DependsNodeFlagsSet)
{
EDependencyProperty DependencyProperties = ByteToProperties(static_cast<uint8>(DependencyFlagBits));
if (((DependencyProperties & RequiredProperties) == RequiredProperties) && ((DependencyProperties & ExcludedProperties) == EDependencyProperty::None))
{
InCallback(DependsNode, ListCategory, DependencyProperties, bDuplicate);
}
bDuplicate = true;
}
}
}
}
template <uint32 FlagWidth>
void IterateDependencyList(const FDependsNode::FIterateDependenciesCallback& InCallback, const FDependsNode* SearchNode, UE::AssetRegistry::EDependencyCategory SearchCategory, const UE::AssetRegistry::FDependencyQuery& SearchFlags,
UE::AssetRegistry::EDependencyCategory ListCategory, UE::AssetRegistry::EDependencyProperty CategoryMask, const TArray<FDependsNode*>& 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
{
EDependencyProperty RequiredProperties = SearchFlags.Required & CategoryMask;
EDependencyProperty ExcludedProperties = SearchFlags.Excluded & CategoryMask;
typedef TPropertyCombinationSet<FlagWidth> FCombinationSet;
constexpr uint32 FlagSetWidth = FCombinationSet::StorageBitCount;
FDependsNode* DependsNode = Dependencies[ListIndex];
FCombinationSet DependsNodeFlagsSet(*FlagBits, ListIndex * FlagSetWidth);
bool bDuplicate = false;
for (uint32 DependencyFlagBits: DependsNodeFlagsSet)
{
EDependencyProperty DependencyProperties = ByteToProperties(static_cast<uint8>(DependencyFlagBits));
if (((DependencyProperties & RequiredProperties) == RequiredProperties) && ((DependencyProperties & ExcludedProperties) == EDependencyProperty::None))
{
InCallback(DependsNode, ListCategory, DependencyProperties, bDuplicate);
}
bDuplicate = true;
}
}
};
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<PackageFlagWidth>(InCallback, Category, Flags, EDependencyCategory::Package, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, ByteToPackageProperties, PackageIsSorted);
IterateDependencyList<SearchableNameFlagWidth>(InCallback, Category, Flags, EDependencyCategory::SearchableName, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted);
IterateDependencyList<ManageFlagWidth>(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<PackageFlagWidth>(InCallback, DependsNode, Category, Flags, EDependencyCategory::Package, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, ByteToPackageProperties, PackageIsSorted);
IterateDependencyList<SearchableNameFlagWidth>(InCallback, DependsNode, Category, Flags, EDependencyCategory::SearchableName, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted);
IterateDependencyList<ManageFlagWidth>(InCallback, DependsNode, Category, Flags, EDependencyCategory::Manage, EDependencyProperty::ManageMask, ManageDependencies, &ManageFlags, ByteToManageProperties, ManageIsSorted);
}
void FDependsNode::GetDependencies(TArray<FDependsNode*>& 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<FAssetIdentifier>& 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<FAssetDependency>& 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<FDependsNode*>& 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)
{
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<FAssetDependency>& 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<TPair<FAssetIdentifier, FPackageFlagSet>>& 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 <uint32 FlagWidth>
void AddDependency(FDependsNode* InDependency, UE::AssetRegistry::EDependencyProperty AddProperties,
UE::AssetRegistry::EDependencyProperty CategoryMask, TArray<FDependsNode*>& 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<FlagWidth> 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 (!!(Category & UE::AssetRegistry::EDependencyCategory::Package))
{
::AddDependency<PackageFlagWidth>(InDependency, Properties, EDependencyProperty::PackageMask, PackageDependencies, &PackageFlags, PackagePropertiesToByte, PackageIsSorted);
check((Category & ~EDependencyCategory::Package) == EDependencyCategory::None); // It is illegal to try to add a dependency as more than one category at a time
}
else if (!!(Category & EDependencyCategory::SearchableName))
{
::AddDependency<SearchableNameFlagWidth>(InDependency, Properties, EDependencyProperty::SearchableNameMask, NameDependencies, nullptr, nullptr, SearchableNameIsSorted);
check((Category & ~EDependencyCategory::SearchableName) == EDependencyCategory::None); // It is illegal to try to add a dependency as more than one category at a time
}
else if (!!(Category & UE::AssetRegistry::EDependencyCategory::Manage))
{
::AddDependency<ManageFlagWidth>(InDependency, Properties, EDependencyProperty::ManageMask, ManageDependencies, &ManageFlags, ManagePropertiesToByte, ManageIsSorted);
check((Category & ~EDependencyCategory::Manage) == EDependencyCategory::None); // It is illegal to try to add a dependency as more than one category at a time
}
else
{
check(false); // It is illegal to try to add a dependency without a category
}
}
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 <uint32 FlagWidth>
void RemoveDependency(FDependsNode* InDependency, TArray<FDependsNode*>& Dependencies, TBitArray<>* FlagBits, bool IsSorted)
{
check(FlagWidth == 0 || FlagBits != nullptr);
if (IsSorted)
{
int32 ListIndex = Algo::LowerBound(Dependencies, InDependency);
if (Dependencies.Num() <= ListIndex || Dependencies[ListIndex] != InDependency)
{
return;
}
Dependencies.RemoveAt(ListIndex);
if (FlagWidth != 0)
{
constexpr uint32 FlagSetWidth = TPropertyCombinationSet<FlagWidth>::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);
if (FlagWidth != 0)
{
constexpr uint32 FlagSetWidth = TPropertyCombinationSet<FlagWidth>::StorageBitCount;
FlagBits->RemoveAtSwap(ListIndex * FlagSetWidth, FlagSetWidth);
}
}
}
}
}
void FDependsNode::RemoveDependency(FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory Category)
{
if (!!(Category & UE::AssetRegistry::EDependencyCategory::Package))
{
::RemoveDependency<PackageFlagWidth>(InDependency, PackageDependencies, &PackageFlags, PackageIsSorted);
}
if (!!(Category & UE::AssetRegistry::EDependencyCategory::SearchableName))
{
::RemoveDependency<SearchableNameFlagWidth>(InDependency, NameDependencies, nullptr, SearchableNameIsSorted);
}
if (!!(Category & UE::AssetRegistry::EDependencyCategory::Manage))
{
::RemoveDependency<ManageFlagWidth>(InDependency, ManageDependencies, &ManageFlags, ManageIsSorted);
}
}
void FDependsNode::RemoveReferencer(FDependsNode* InReferencer)
{
::RemoveDependency<0>(InReferencer, Referencers, nullptr, ReferencersIsSorted);
}
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))
{
PackageDependencies.Empty();
PackageFlags.Empty();
}
if (!!(Category & EDependencyCategory::SearchableName))
{
NameDependencies.Empty();
}
if (!!(Category & EDependencyCategory::Manage))
{
ManageDependencies.Empty();
ManageFlags.Empty();
}
}
void FDependsNode::ClearReferencers()
{
Referencers.Empty();
}
void FDependsNode::RemoveManageReferencesToNode()
{
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);
}
else
{
Referencers.RemoveAtSwap(i);
}
}
}
}
template <uint32 FlagWidth>
void RemoveAll(const TUniqueFunction<bool(const FDependsNode*)>& ShouldRemove, TArray<FDependsNode*>& Dependencies, TBitArray<>* FlagBits, bool IsSorted)
{
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<FlagWidth>::StorageBitCount;
int32 WriteIndex = 0;
int32 ReadIndex = 0;
FDependsNode** DependencyData = Dependencies.GetData();
bool Keep = !ShouldRemove(DependencyData[ReadIndex]); // use a ! to guarantee it can't be anything other than zero or one
do
{
int32 RunStartIndex = ReadIndex++;
while (ReadIndex < OriginalNum && Keep == !ShouldRemove(DependencyData[ReadIndex]))
{
ReadIndex++;
}
int32 RunLength = ReadIndex - RunStartIndex;
checkSlow(RunLength > 0);
if (Keep)
{
// 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;
}
Keep = !Keep;
} while (ReadIndex < OriginalNum);
Dependencies.SetNum(WriteIndex);
FlagBits->SetNumUninitialized(WriteIndex * FlagSetWidth);
}
}
else
{
if (FlagWidth == 0)
{
Dependencies.RemoveAllSwap(ShouldRemove);
}
else
{
constexpr uint32 FlagSetWidth = TPropertyCombinationSet<FlagWidth>::StorageBitCount;
for (int32 ListIndex = 0; ListIndex < Dependencies.Num();)
{
if (ShouldRemove(Dependencies[ListIndex]))
{
Dependencies.RemoveAtSwap(ListIndex);
FlagBits->RemoveAtSwap(ListIndex * FlagSetWidth, FlagSetWidth);
}
else
{
++ListIndex;
}
}
}
}
}
void FDependsNode::RemoveLinks(const TUniqueFunction<bool(const FDependsNode*)>& ShouldRemove)
{
::RemoveAll<PackageFlagWidth>(ShouldRemove, PackageDependencies, &PackageFlags, PackageIsSorted);
::RemoveAll<SearchableNameFlagWidth>(ShouldRemove, NameDependencies, nullptr, SearchableNameIsSorted);
::RemoveAll<ManageFlagWidth>(ShouldRemove, ManageDependencies, &ManageFlags, ManageIsSorted);
::RemoveAll<0>(ShouldRemove, Referencers, nullptr, ReferencersIsSorted);
}
bool FDependsNode::ContainsDependency(FDependsNode* InDependency, UE::AssetRegistry::EDependencyCategory Category) const
{
using namespace UE::AssetRegistry;
auto ListContains = [InDependency](const TArray<FDependsNode*>& List, bool IsSorted)
{
return (IsSorted ? Algo::BinarySearch(List, InDependency) : List.Find(InDependency)) != INDEX_NONE;
};
if (!!(Category & EDependencyCategory::Package))
{
if (ListContains(PackageDependencies, PackageIsSorted))
{
return true;
}
}
if (!!(Category & EDependencyCategory::SearchableName))
{
if (ListContains(NameDependencies, SearchableNameIsSorted))
{
return true;
}
}
if (!!(Category & EDependencyCategory::Manage))
{
if (ListContains(ManageDependencies, ManageIsSorted))
{
return true;
}
}
return false;
}
void FDependsNode::PrintDependenciesRecursive(const FString& Indent, TSet<const FDependsNode*>& VisitedNodes) const
{
if (this == NULL)
{
UE_LOG(LogAssetRegistry, Log, TEXT("%sNULL"), *Indent);
}
else if (VisitedNodes.Contains(this))
{
UE_LOG(LogAssetRegistry, Log, TEXT("%s[CircularReferenceTo]%s"), *Indent, *Identifier.ToString());
}
else
{
UE_LOG(LogAssetRegistry, Log, TEXT("%s%s"), *Indent, *Identifier.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<const FDependsNode*>& VisitedNodes) const
{
if (this == NULL)
{
UE_LOG(LogAssetRegistry, Log, TEXT("%sNULL"), *Indent);
}
else if (VisitedNodes.Contains(this))
{
UE_LOG(LogAssetRegistry, Log, TEXT("%s[CircularReferenceTo]%s"), *Indent, *Identifier.ToString());
}
else
{
UE_LOG(LogAssetRegistry, Log, TEXT("%s%s"), *Indent, *Identifier.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<int32(FDependsNode*, bool)>& GetSerializeIndexFromNode, FSaveScratch& Scratch, const FAssetRegistrySerializationOptions& Options) const
{
Ar << const_cast<FAssetIdentifier&>(Identifier);
auto WriteDependencies = [&Ar, &GetSerializeIndexFromNode, &Scratch](const TArray<FDependsNode*>& InDependencies, const TBitArray<>* InFlagBits, int FlagSetWidth, bool bAsReferencer)
{
TArray<int32>& OutDependencies = Scratch.OutDependencies;
TBitArray<>& OutFlagBits = Scratch.OutFlagBits;
OutDependencies.Reset();
if (InFlagBits)
{
OutFlagBits.Reset();
}
for (int32 ListIndex = 0, End = InDependencies.Num(); ListIndex < End; ++ListIndex)
{
int32 SerializeIndex = GetSerializeIndexFromNode(InDependencies[ListIndex], bAsReferencer);
if (SerializeIndex < 0)
{
continue;
}
OutDependencies.Add(SerializeIndex);
if (InFlagBits)
{
OutFlagBits.AddRange(*InFlagBits, FlagSetWidth, ListIndex * FlagSetWidth);
}
}
Ar << OutDependencies;
if (InFlagBits)
{
// 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 * OutDependencies.Num();
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<FDependsNode*(int32)>& GetNodeFromSerializeIndex, FLoadScratch& Scratch)
{
Ar << Identifier;
auto ReadDependencies = [&Ar, &GetNodeFromSerializeIndex, &Scratch](TArray<FDependsNode*>& OutDependencies, TBitArray<>* OutFlagBits, int FlagSetWidth)
{
TArray<int32>& InDependencies = Scratch.InDependencies;
TArray<uint32>& InFlagBits = Scratch.InFlagBits;
TArray<FDependsNode*>& PointerDependencies = Scratch.PointerDependencies;
TArray<int32>& 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);
}
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;
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<FDependsNode*>& 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);
}
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 <uint32 FlagWidth>
void SortDependencyList(TArray<FDependsNode*>& Dependencies, TBitArray<>* Flags)
{
const int32 Num = Dependencies.Num();
if (Num == 0)
{
return;
}
FDependsNode** const DependencyData = Dependencies.GetData();
if (FlagWidth == 0)
{
// Sort dependencies+
Algo::Sort(TArrayView<FDependsNode*>(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);
}
}
else
{
check(Flags);
// Create an index array for sorting
TArray<int32> 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<FlagWidth> FCombinationSet;
constexpr uint32 FlagSetWidth = FCombinationSet::StorageBitCount;
TArray<FDependsNode*> 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;
if (!!(Category & EDependencyCategory::Package))
{
if (bValue && PackageIsSorted == 0)
{
SortDependencyList<PackageFlagWidth>(PackageDependencies, &PackageFlags);
}
PackageIsSorted = bValue ? 1 : 0;
}
if (!!(Category & EDependencyCategory::SearchableName))
{
if (bValue && SearchableNameIsSorted == 0)
{
SortDependencyList<SearchableNameFlagWidth>(NameDependencies, nullptr);
}
SearchableNameIsSorted = bValue ? 1 : 0;
}
if (!!(Category & EDependencyCategory::Manage))
{
if (bValue && ManageIsSorted == 0)
{
SortDependencyList<ManageFlagWidth>(ManageDependencies, &ManageFlags);
}
ManageIsSorted = bValue ? 1 : 0;
}
}
bool FDependsNode::IsReferencersSorted() const
{
return ReferencersIsSorted;
}
void FDependsNode::SetIsReferencersSorted(bool bValue)
{
if (bValue && ReferencersIsSorted == 0)
{
SortDependencyList<0>(Referencers, nullptr);
}
ReferencersIsSorted = bValue ? 1 : 0;
}