Files
UnrealEngine/Engine/Source/Runtime/AssetRegistry/Private/DependsNode.cpp
2025-05-18 13:04:45 +08:00

1222 lines
39 KiB
C++

// 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<const FDependsNode*> VisitedNodes;
PrintDependenciesRecursive(TEXT(""), VisitedNodes);
}
void FDependsNode::PrintReferencers() const
{
TSet<const FDependsNode*> VisitedNodes;
PrintReferencersRecursive(TEXT(""), VisitedNodes);
}
void FDependsNode::SetIdentifier(const FAssetIdentifier& InIdentifier)
{
Identifier = InIdentifier;
if (!InIdentifier.PackageName.IsNone())
{
TStringBuilder<FName::StringBufferSize> 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 <uint32 FlagWidth, typename VisitorType>
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<FlagWidth> 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<uint8>(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 <uint32 FlagWidth, typename CallbackType>
void IterateDependencyList(CallbackType&& 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
{
for (int32 ListIndex = 0; ListIndex < Dependencies.Num(); ++ListIndex)
{
FDependsNode* DependsNode = Dependencies[ListIndex];
EnumerateMatchingDependencyFlags<FlagWidth>(
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 <uint32 FlagWidth, typename CallbackType>
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<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
{
FDependsNode* DependsNode = Dependencies[ListIndex];
EnumerateMatchingDependencyFlags<FlagWidth>(
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<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
|| !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<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 (EnumHasAnyFlags(Category, UE::AssetRegistry::EDependencyCategory::Package))
{
::AddDependency<PackageFlagWidth>(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<SearchableNameFlagWidth>(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<ManageFlagWidth>(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 <uint32 FlagWidth>
void RemoveDependency(FDependsNode* InDependency,
TArray<FDependsNode*>& 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<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, ShrinkPolicy);
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, bAllowShrinking);
}
if (!!(Category & UE::AssetRegistry::EDependencyCategory::SearchableName))
{
::RemoveDependency<SearchableNameFlagWidth>(InDependency, NameDependencies, nullptr, SearchableNameIsSorted, bAllowShrinking);
}
if (!!(Category & UE::AssetRegistry::EDependencyCategory::Manage))
{
::RemoveDependency<ManageFlagWidth>(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 <uint32 FlagWidth>
void RemoveAll(const TUniqueFunction<bool(const FDependsNode*)>& ShouldRemove,
TArray<FDependsNode*>& 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<FlagWidth>::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<FlagWidth>::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<bool(const FDependsNode*)>& ShouldRemove)
{
const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No;
::RemoveAll<PackageFlagWidth>(ShouldRemove, PackageDependencies, &PackageFlags, PackageIsSorted, ShrinkPolicy);
::RemoveAll<SearchableNameFlagWidth>(ShouldRemove, NameDependencies, nullptr, SearchableNameIsSorted, ShrinkPolicy);
::RemoveAll<ManageFlagWidth>(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<PackageFlagWidth>([&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<SearchableNameFlagWidth>([&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<ManageFlagWidth>([&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<const FDependsNode*>& 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<const FDependsNode*>& 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<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<FSaveScratch::FSortInfo>& SortInfos = Scratch.SortInfos;
TArray<int32>& 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<FDependsNode* (int32)>& GetNodeFromSerializeIndex,
FLoadScratch& Scratch)
{
Ar << Identifier;
SetIdentifier(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);
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<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);
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 <uint32 FlagWidth>
void SortDependencyList(TArray<FDependsNode*>& 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<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, ShrinkPolicy);
}
}
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;
const EAllowShrinking ShrinkPolicy = bAllowShrinking ? EAllowShrinking::Yes : EAllowShrinking::No;
if (!!(Category & EDependencyCategory::Package))
{
if (bValue && PackageIsSorted == 0)
{
SortDependencyList<PackageFlagWidth>(PackageDependencies, &PackageFlags, ShrinkPolicy);
}
PackageIsSorted = bValue ? 1 : 0;
}
if (!!(Category & EDependencyCategory::SearchableName))
{
if (bValue && SearchableNameIsSorted == 0)
{
SortDependencyList<SearchableNameFlagWidth>(NameDependencies, nullptr, ShrinkPolicy);
}
SearchableNameIsSorted = bValue ? 1 : 0;
}
if (!!(Category & EDependencyCategory::Manage))
{
if (bValue && ManageIsSorted == 0)
{
SortDependencyList<ManageFlagWidth>(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;
}