FDependsNodes allow duplicates during startup to avoid spending time on AddUnique, but they are supposed to remove the duplicates when the search finishes; FAssetRegistryImpl::OnInitialSearchCompleted calls SetPerformanceMode(Impl::EPerformanceMode::MostlyStatic) which is supposed to remove duplicates and sort dependencies on each FDependsNode.
What is the callstack to UpdateManagementDatabase and SetManageReferences you are seeing? Maybe it is occurring before the AssetRegistry’s search finishes.
But even with duplicates in the FDependsNodes package dependencies, it is not supposed to be possible to add duplicates in the ManageDependencies, because the SetManageReferences function uses a TMap to record whether each node discovered in the graph search has been added to the manager; this TMap is looked up in the call to FindOrAddNodeData in the loop at the bottom of SetManageReferences, and the loop body is early exited if bModifiedByCurrentManager was cleared by an earlier instance of the loop.
...
for (FDependsNode* ModifiedNode : CurrentManagerModifiedNodes)
{
FSetManageReferencesNodeData& ModifiedData = FindOrAddNodeData(ModifiedNode);
if (!ModifiedData.bModifiedByCurrentManager)
{
// A duplicate of a NodeData we already handled earlier in the list
continue;
}
ModifiedNode->SetIsReferencersSorted(false);
ModifiedNode->AddReferencer(ManagerNode);
ManagerNode->AddDependency(ModifiedNode, EDependencyCategory::Manage,
ModifiedData.CurrentManagerProperties);
ModifiedData.bManagedInThisRound = true;
ModifiedData.bModifiedByRound = true;
CurrentRoundModifiedNodes.Add(ModifiedNode);
ModifiedData.bModifiedByCurrentManager = false;
ModifiedData.bVisitedByCurrentManager = false;
ModifiedData.CurrentManagerProperties = EDependencyProperty::None;
}
...
The first thing to check is whether that for loop is the one adding the duplicates in ManageDependencies.
Turn off optimizations in AssetRegistry.cpp by adding UE_DISABLE_OPTIMIZATION at the top of the file, and add instrumentation in that for loop:
for (FDependsNode* ModifiedNode : CurrentManagerModifiedNodes)
{
FSetManageReferencesNodeData& ModifiedData = FindOrAddNodeData(ModifiedNode);
if (!ModifiedData.bModifiedByCurrentManager)
{
// A duplicate of a NodeData we already handled earlier in the list
continue;
}
ModifiedNode->SetIsReferencersSorted(false);
ModifiedNode->AddReferencer(ManagerNode);
// BEGIN_INSTRUMENTATION
if (ManagerNode->ContainsDependency(ModifiedNode, EDependencyCategory::Manage))
{
static volatile int HitBreakpoint = true; ++HitBreakpoint;
}
// END_INSTRUMENTATION
ManagerNode->AddDependency(ModifiedNode, EDependencyCategory::Manage,
ModifiedData.CurrentManagerProperties);
ModifiedData.bManagedInThisRound = true;
ModifiedData.bModifiedByRound = true;
CurrentRoundModifiedNodes.Add(ModifiedNode);
ModifiedData.bModifiedByCurrentManager = false;
ModifiedData.bVisitedByCurrentManager = false;
ModifiedData.CurrentManagerProperties = EDependencyProperty::None;
}
If it’s coming from there, we will need to add more instrumentation to find out how. Maybe the same manager is being passed into multiple calls of SetManageReferences from UpdateManagementDatabase, and we are redoing the graph search and repeatedly adding all of its nodes to it.
Also, what is the value of bShouldSortDependencies at the bottom of SetManageReferences?