UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
KahnTopologicalSort.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Algo/Sort.h"
6#include "Algo/Unique.h"
7#include "Containers/Array.h"
9#include "Containers/Map.h"
10#include "Containers/Set.h"
11#include "Misc/EnumClassFlags.h"
12#include "Templates/Invoke.h"
14#include "Traits/ElementType.h"
15
16namespace AlgoImpl
17{
23
43
44 // Helper functions
45 template <typename RangeType, typename GetElementDependenciesType>
49}
50
51namespace Algo
52{
55 {
56 None,
58 };
60
62 template <typename RangeType, typename GetElementDependenciesType>
65 {
66 using namespace AlgoImpl;
67 using ElementType = typename TElementType<RangeType>::Type;
68
71 KahnTopologicalSort_CreateWorkingGraph(Context, UniqueRange,
73
74 // Initialize the graph search
75 TArray<TArray<FKahnHandle>>& Referencers(Context.Referencers);
76 TArray<TArray<FKahnHandle>>& Dependencies(Context.Dependencies);
77 TArray<int32>& DependencyCount(Context.DependencyCount);
78 TSet<FKahnHandle>& RemainingVertices(Context.RemainingVertices);
81 int32 NumElements = Dependencies.Num();
82 SortedRange.Reserve(NumElements);
83 RemainingVertices.Reserve(NumElements);
84 for (FKahnHandle Vertex = 0; Vertex < NumElements; ++Vertex)
85 {
86 RemainingVertices.Add(Vertex);
87 }
88
89 // Sort graph so that vertices with no dependencies (leaves) always go first.
90 while (RemainingVertices.Num() > 0)
91 {
92 if (IndependentVertices.Num() == 0)
93 {
94 // If there are no independent vertices then there is a cycle in the graph
96 {
97 return false;
98 }
99
100 // When all remaining vertices are in cycles, find the maximal MutuallyReachableVertexSet
101 // (a cycle is an MRVS) that is independent of all the other MRVS's.
102 const TSet<FKahnHandle>& MRVS = FindMostIndependentMutuallyReachableVertexSet(Context);
103 check(!MRVS.IsEmpty());
104 for (FKahnHandle Vertex : MRVS)
105 {
106 // Add all the vertices in the MutuallyReachableVertexSet to the SortedRange in arbitrary order
108 // Mark that we should no longer consider any vertices in the MRVS when looking for NewIndependentVertices;
109 // they have already been removed
110 DependencyCount[Vertex] = INDEX_NONE;
111 }
112 }
113
116 {
117 for (FKahnHandle Referencer : Referencers[Vertex])
118 {
119 int32& ReferencerDependencyCount = DependencyCount[Referencer];
121 {
122 // Don't read or write dependencycount for referencers we removed due to a cycle
123 continue;
124 }
126 if (--ReferencerDependencyCount == 0)
127 {
128 NewIndependentVertices.Add(Referencer);
129 }
130 }
131#if DO_CHECK
132 int32 RemainingNum = RemainingVertices.Num();
133#endif
134 RemainingVertices.Remove(Vertex);
135#if DO_CHECK
136 check(RemainingVertices.Num() == RemainingNum - 1); // Confirm Vertex was in RemainingVertices
137#endif
138 SortedRange.Add(Vertex);
139 }
141 }
142
143 // Shuffle the input according to the SortOrder found by the graph search
145 CopyOriginal.Reserve(NumElements);
146 for (ElementType& Element : UniqueRange)
147 {
148 CopyOriginal.Add(MoveTemp(Element));
149 }
150 int32 SourceIndex = 0;
151 for (ElementType& TargetElement : UniqueRange)
152 {
154 }
155
156 return true;
157 }
158}
159
160namespace AlgoImpl
161{
166 template <typename RangeType, typename GetElementDependenciesType>
169 {
170 using ElementType = typename TElementType<RangeType>::Type;
171
172 TArray<TArray<FKahnHandle>>& Referencers(Context.Referencers);
173 TArray<TArray<FKahnHandle>>& Dependencies(Context.Dependencies);
174 TArray<int32>& DependencyCount(Context.DependencyCount);
175
176 int32 NumElements = GetNum(UniqueRange);
178 HandleOfElement.Reserve(NumElements);
180 for (const ElementType& Element : UniqueRange)
181 {
185 }
186
187 Referencers.SetNum(NumElements);
188 Dependencies.SetNum(NumElements);
189 DependencyCount.SetNum(NumElements);
190 Handle = 0;
191 for (const ElementType& Element : UniqueRange)
192 {
193 const auto& DependenciesInput = ::Invoke(GetElementDependencies, Element);
195
196 for (const ElementType& Dependency : DependenciesInput)
197 {
200 {
202 }
203 }
207 DependencyCount[Handle] = NumUniqueDependencies;
208 if (NumUniqueDependencies == 0)
209 {
211 }
213 {
216 }
217 ++Handle;
218 }
219 }
220
226 {
227 if (!Context.MRVSContext.IsSet())
228 {
229 int32 NumVertices = Context.RemainingVertices.Num();
231 InitContext.VisitedReferences.Reserve(NumVertices);
232 InitContext.VisitedDependencies.Reserve(NumVertices);
233 InitContext.Culprits.Reserve(NumVertices);
234 InitContext.Stack.Reserve(NumVertices);
235 InitContext.Cycle.Reserve(NumVertices);
236 }
237 const TArray<TArray<FKahnHandle>>& Dependencies = Context.Dependencies;
238 const TArray<TArray<FKahnHandle>>& Referencers = Context.Referencers;
239 FMutuallyReachableVertexSetContext& MRVSContext = *Context.MRVSContext;
240 TSet<FKahnHandle>& VisitedReferences = MRVSContext.VisitedReferences;
241 TSet<FKahnHandle>& VisitedDependencies = MRVSContext.VisitedDependencies;
242 TSet<FKahnHandle>& MaximalMRVS = MRVSContext.MaximalMRVS;
243 TSet<FKahnHandle>& Culprits = MRVSContext.Culprits;
244 TArray<FKahnHandle>& Stack = MRVSContext.Stack;
245 TArray<FKahnHandle>& Cycle = MRVSContext.Cycle;
246
247 // Copy Remaining Vertices into Culprits; we will be modifying the list
248 // as we search for the most-independent MRVS.
249 Culprits.Reset();
250 Culprits.Append(Context.RemainingVertices);
251
252 // Start from an arbitrary vertex
253 check(!Culprits.IsEmpty());
254 FKahnHandle StartingVertex = *Culprits.CreateIterator();
255
257 while (StartingVertex != INDEX_NONE)
258 {
259 // Find a cycle by arbitrarily following dependencies until we revisit a vertex.
260 VisitedReferences.Reset();
261 Stack.Reset();
262 VisitedReferences.Add(StartingVertex);
263 Stack.Add(StartingVertex);
267 {
269 for (FKahnHandle Dependency : Dependencies[CycleWitnessVertex])
270 {
271 if (Culprits.Contains(Dependency)) // Only consider edges to remaining vertices
272 {
274 break;
275 }
276 }
277 // Assert a dependency is found. This function is only called when every vertex has remaining dependencies.
280 VisitedReferences.Add(CycleWitnessVertex, &bCycleWitnessWasAlreadyInStack);
281 Stack.Add(CycleWitnessVertex);
282 }
283
284 // The cycle is everything on the stack after the first occurrence of CycleWitnessVertex.
285 // Cycle stacks will be like [7,7] or [7,3,2,7], always starting and ending with a vertex
286 // Pop the second occurrence of CycleWitnessVertex off the stack
287 check(Stack.Num() >= 2);
289 // Find the start of the Cycle
290 int32 StartIndex = Stack.Num() - 1;
291 while (StartIndex >= 0 && Stack[StartIndex] != CycleWitnessVertex)
292 {
293 --StartIndex;
294 }
295 check(StartIndex >= 0);
296 Cycle.Reset();
297 Cycle.Append(TArrayView<FKahnHandle>(Stack).Mid(StartIndex));
298
299 // We now have a cycle, which is an MRVS that may or may not be maximal. Expand it to a maximal MRVS by
300 // intersecting everything reachable from it with everything from which it is reachable.
301
302 // Find all references to the cycle. We start from VisitedReferences and Stack that we created when
303 // finding the cycle, since everything in that set is a referencer of the cycle
304 while (!Stack.IsEmpty())
305 {
307 for (FKahnHandle Referencer : Referencers[Vertex])
308 {
309 if (Culprits.Contains(Referencer)) // Only consider edges to remaining vertices
310 {
311 bool bAlreadyVisited;
312 VisitedReferences.Add(Referencer, &bAlreadyVisited);
313 if (!bAlreadyVisited)
314 {
315 Stack.Add(Referencer);
316 }
317 }
318 }
319 }
320
321 // Find all dependencies from the cycle.
322 VisitedDependencies.Reset();
323 Stack.Reset();
324 for (FKahnHandle Vertex : Cycle)
325 {
326 VisitedDependencies.Add(Vertex);
327 Stack.Add(Vertex);
328 }
329 while (!Stack.IsEmpty())
330 {
332 for (FKahnHandle Dependency : Dependencies[Vertex])
333 {
334 if (Culprits.Contains(Dependency)) // Only consider edges to remaining vertices
335 {
336 bool bAlreadyVisited;
337 VisitedDependencies.Add(Dependency, &bAlreadyVisited);
338 if (!bAlreadyVisited)
339 {
340 Stack.Add(Dependency);
341 }
342 }
343 }
344 }
345
346 MaximalMRVS = VisitedDependencies.Intersect(VisitedReferences);
347
348 // We now have a maximal MRVS, but there may be multiple maximal MRVS's, and we need to find the most independent
349 // one. Remove all elements of VisitedReferences from the remaining vertices in Culprits. We will not need to consider
350 // them when searching the graph for the more independent MRVS. Once removed, look for any dependencies from
351 // the MaximalMRVS to a remaining culprit. If any exist, then we can follow them to find a new maximal MRVS that
352 // is more independent than the current maximal MRVS.
353 for (FKahnHandle Vertex : VisitedReferences)
354 {
355 Culprits.Remove(Vertex);
356 }
357
359 for (FKahnHandle Vertex : MaximalMRVS)
360 {
361 for (FKahnHandle Dependency : Dependencies[Vertex])
362 {
363 if (Culprits.Contains(Dependency)) // Only consider edges to remaining vertices
364 {
366 break;
367 }
368 }
370 {
371 break;
372 }
373 }
374
375 // If we found a new StartingVertex, the loop will continue and follow it to the new MaximalMRVS
376 // Otherwise the current MaximalMRVS is our returned value
377 checkf(StartingVertexLoopCount++ < Context.RemainingVertices.Num(), TEXT("Infinite loop in FindMostIndependentMutuallyReachableVertexSet"));
378 }
379
380 return MaximalMRVS;
381 }
382}
#define check(expr)
Definition AssertionMacros.h:314
#define checkf(expr, format,...)
Definition AssertionMacros.h:315
@ INDEX_NONE
Definition CoreMiscDefines.h:150
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
AUTORTFM_INFER UE_FORCEINLINE_HINT constexpr auto Invoke(FuncType &&Func, ArgTypes &&... Args) -> decltype(((FuncType &&) Func)((ArgTypes &&) Args...))
Definition Invoke.h:44
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
constexpr bool EnumHasAnyFlags(Enum Flags, Enum Contains)
Definition EnumClassFlags.h:35
#define ENUM_CLASS_FLAGS(Enum)
Definition EnumClassFlags.h:6
@ Vertex
Definition MetalRHIPrivate.h:223
auto GetNum(const TStringConversion< Converter, DefaultConversionSize > &Conversion) -> decltype(Conversion.Length())
Definition StringConv.h:808
UE_INTRINSIC_CAST UE_REWRITE constexpr std::remove_reference_t< T > && MoveTemp(T &&Obj) noexcept
Definition UnrealTemplate.h:520
Definition ArrayView.h:139
Definition Array.h:670
UE_REWRITE SizeType Num() const
Definition Array.h:1144
void Reset(SizeType NewSize=0)
Definition Array.h:2246
UE_REWRITE bool IsEmpty() const
Definition Array.h:1133
void SetNum(SizeType NewNum, EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:2308
UE_NODEBUG UE_FORCEINLINE_HINT SizeType Add(ElementType &&Item)
Definition Array.h:2696
UE_NODEBUG UE_FORCEINLINE_HINT bool Find(const ElementType &Item, SizeType &Index) const
Definition Array.h:1302
ElementType Pop(EAllowShrinking AllowShrinking=UE::Core::Private::AllowShrinkingByDefault< AllocatorType >())
Definition Array.h:1196
UE_FORCEINLINE_HINT void Reserve(SizeType Number)
Definition Array.h:3016
Definition UnrealString.h.inl:34
Definition BinarySearch.h:10
void KahnTopologicalSort_CreateWorkingGraph(FKahnContext &Context, RangeType &UniqueRange, GetElementDependenciesType GetElementDependencies, TSet< FKahnHandle > &OutInitialIndependents)
Definition KahnTopologicalSort.h:167
int32 FKahnHandle
Definition KahnTopologicalSort.h:22
const TSet< FKahnHandle > & FindMostIndependentMutuallyReachableVertexSet(FKahnContext &Context)
Definition KahnTopologicalSort.h:225
Definition ParallelSort.h:13
UE_REWRITE void Sort(RangeType &&Range)
Definition Sort.h:16
ETopologicalSort
Definition KahnTopologicalSort.h:55
bool KahnTopologicalSort(RangeType &&UniqueRange, GetElementDependenciesType GetElementDependencies, ETopologicalSort Flags)
Definition KahnTopologicalSort.h:63
Definition KahnTopologicalSort.h:36
TArray< int32 > DependencyCount
Definition KahnTopologicalSort.h:40
TArray< TArray< FKahnHandle > > Dependencies
Definition KahnTopologicalSort.h:39
TSet< FKahnHandle > RemainingVertices
Definition KahnTopologicalSort.h:37
TArray< TArray< FKahnHandle > > Referencers
Definition KahnTopologicalSort.h:38
TOptional< FMutuallyReachableVertexSetContext > MRVSContext
Definition KahnTopologicalSort.h:41
Definition KahnTopologicalSort.h:26
TSet< FKahnHandle > VisitedDependencies
Definition KahnTopologicalSort.h:28
TSet< FKahnHandle > VisitedReferences
Definition KahnTopologicalSort.h:27
TSet< FKahnHandle > MaximalMRVS
Definition KahnTopologicalSort.h:29
TArray< FKahnHandle > Stack
Definition KahnTopologicalSort.h:31
TSet< FKahnHandle > Culprits
Definition KahnTopologicalSort.h:30
TArray< FKahnHandle > Cycle
Definition KahnTopologicalSort.h:32
Definition ElementType.h:30
Definition Optional.h:131