Agrarsense
TickManager.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 FrostBit Software Lab at the Lapland University of Applied Sciences
2//
3// This work is licensed under the terms of the MIT license.
4// For a copy, see <https://opensource.org/licenses/MIT>.
5
6#include "TickManager.h"
7
8#include "Engine/GameViewportClient.h"
9#include "Engine.h"
10
11#include "Async/ParallelFor.h"
12
14std::vector<FTickEntry> ATickManager::FullFrameTaskFunctions;
16std::vector<FTickEntry> ATickManager::EarlyTickFunctions;
17std::vector<FTickEntry> ATickManager::LateTickFunctions;
18std::vector<FTickEntry> ATickManager::TicksToRemove;
19
21{
22 Super::BeginPlay();
23
24 if (Instance == nullptr)
25 {
26 Instance = this;
27
28 // Subscribe to pre and post tick actor delegates
29 OnPreTickDelegate = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &ATickManager::PreTick);
30 OnPostTickDelegate = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ATickManager::PostTick);
31 }
32 else
33 {
34 Destroy();
35 }
36}
37
38void ATickManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
39{
40 Super::EndPlay(EndPlayReason);
41
42 if (Instance == this)
43 {
46 EarlyTickFunctions.clear();
47 LateTickFunctions.clear();
48 TicksToRemove.clear();
49 FWorldDelegates::OnWorldPreActorTick.Remove(OnPreTickDelegate);
50 FWorldDelegates::OnWorldPostActorTick.Remove(OnPostTickDelegate);
51 Instance = nullptr;
52 }
53}
54
55FTickEntry ATickManager::AddTick(UObject* Object, std::function<void(float)> Function, ETickType Type)
56{
57 if (Instance == nullptr)
58 {
59 if (GEngine || GEngine->GameViewport)
60 {
61 UWorld* World = GEngine->GameViewport->GetWorld();
62 World->SpawnActor<ATickManager>();
63 }
64 }
65
66 FTickEntry TickEntry(Object, Function, Type);
67
68 switch (Type)
69 {
70 case ETickType::FullFrameTaskParallel:
72 break;
73 case ETickType::FullFrameTaskBeforeLateTickParallel:
75 break;
76 case ETickType::EarlyTickParallel:
78 break;
79 case ETickType::LateTickParallel:
81 break;
82 default:
83 UE_LOG(LogTemp, Warning, TEXT("TickManager.cpp: Unsupported ETickType in AddTick."));
84 break;
85 }
86
87 return TickEntry;
88}
89
91{
92 // Add tick entry to remove into std::vector. These get deleted at the end of the frame.
93 TicksToRemove.push_back(TickEntry);
94}
95
96void ATickManager::PreTick(UWorld* World, ELevelTick TickType, float DeltaSeconds)
97{
98 // Start FullFrameTask that runs the entire frame
99 // if the task is small, it can finish at any time
100 if (FullFrameTaskFunctions.size() != 0)
101 {
102 FullFrameTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this, DeltaSeconds]()
103 {
105 }, TStatId(), nullptr, ENamedThreads::AnyBackgroundHiPriTask);
106 }
107
108 // Start FullFrameTaskBeforeLateTick that finishes before calling LateTickFunctions
109 // if the task is small, it can finish at any time
111 {
112 FullFrameTaskBeforeLateTick = FFunctionGraphTask::CreateAndDispatchWhenReady([this, DeltaSeconds]()
113 {
115 }, TStatId(), nullptr, ENamedThreads::AnyBackgroundHiPriTask);
116 }
117
118 // Tick all added early ticks in parallel before continuing
119 if (EarlyTickFunctions.size() != 0)
120 {
122 }
123}
124
125void ATickManager::PostTick(UWorld* World, ELevelTick TickType, float DeltaSeconds)
126{
127 // Wait till all FullFrameTaskBeforeLateTick ticks are completed
128 if (FullFrameTaskBeforeLateTick.IsValid())
129 {
130 FTaskGraphInterface::Get().WaitUntilTaskCompletes(FullFrameTaskBeforeLateTick);
131 }
132
133 // Tick all added late ticks in parallel
134 if (LateTickFunctions.size() != 0)
135 {
137 }
138
139 // Wait till all FullFrameTask ticks are completed
140 if (FullFrameTask.IsValid())
141 {
142 FTaskGraphInterface::Get().WaitUntilTaskCompletes(FullFrameTask);
143 }
144
145 FrameCleanup();
146}
147
148inline void ATickManager::TickFunctionsParallel(std::vector<FTickEntry>& functions, const float DeltaTime)
149{
150 bool foundNull = false;
151 //std::vector<FTickEntry> functionsCopy = functions;
152 const int size = functions.size();
153 ParallelFor(size, [&](int32 index)
154 {
155 FTickEntry& tickEntry = functions[index];
156 if (tickEntry.Owner != nullptr)
157 {
158 tickEntry.Tick(DeltaTime);
159 }
160 else
161 {
162 foundNull = true;
163 }
164 }, EParallelForFlags::Unbalanced);
165
166 if (foundNull)
167 {
168 functions.erase(std::remove_if(functions.begin(), functions.end(), [](const FTickEntry& Entry) { return Entry.Owner == nullptr; }), functions.end());
169 }
170}
171
172
174{
175 if (TicksToRemove.size() != 0)
176 {
177 for (const FTickEntry& tickEntry : TicksToRemove)
178 {
179 ETickType Type = tickEntry.TickType;
180 switch (Type)
181 {
182 case ETickType::FullFrameTaskParallel:
184 break;
185 case ETickType::FullFrameTaskBeforeLateTickParallel:
187 break;
188 case ETickType::EarlyTickParallel:
190 break;
191 case ETickType::LateTickParallel:
193 break;
194 default:
195 UE_LOG(LogTemp, Warning, TEXT("TickManager.cpp: Unsupported ETickType in RemoveTick."));
196 break;
197 }
198 }
199 TicksToRemove.clear();
200 }
201
202 // Destroy this Actor if we have no ticks
203 // TODO: Should we just keep this alive?
204 if (FullFrameTaskFunctions.size() == 0 &&
206 EarlyTickFunctions.size() == 0 &&
207 LateTickFunctions.size() == 0)
208 {
209 if (Instance != nullptr)
210 {
211#if WITH_EDITOR
212 UE_LOG(LogTemp, Warning, TEXT("TickManager.cpp: Destroying as there are no more ticks."));
213#endif
214 Instance->Destroy();
215 Instance = nullptr;
216 }
217 }
218}
ETickType
Definition: TickManager.h:24
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: TickManager.cpp:38
FDelegateHandle OnPreTickDelegate
Definition: TickManager.h:137
static std::vector< FTickEntry > LateTickFunctions
Definition: TickManager.h:154
static std::vector< FTickEntry > TicksToRemove
Definition: TickManager.h:155
void PreTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
Definition: TickManager.cpp:96
static std::vector< FTickEntry > FullFrameTaskBeforeLateTickFunctions
Definition: TickManager.h:152
static std::vector< FTickEntry > EarlyTickFunctions
Definition: TickManager.h:153
void PostTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
static void AddTickInternal(std::vector< FTickEntry > &to, const FTickEntry &newTickEntry)
Definition: TickManager.h:157
FGraphEventRef FullFrameTask
Definition: TickManager.h:148
FDelegateHandle OnPostTickDelegate
Definition: TickManager.h:140
static void RemoveTick(FTickEntry TickEntry)
Definition: TickManager.cpp:90
static ATickManager * Instance
Definition: TickManager.h:150
FGraphEventRef FullFrameTaskBeforeLateTick
Definition: TickManager.h:147
void FrameCleanup()
static std::vector< FTickEntry > FullFrameTaskFunctions
Definition: TickManager.h:151
virtual void BeginPlay() override
Definition: TickManager.cpp:20
void TickFunctionsParallel(std::vector< FTickEntry > &functions, const float DeltaTime)
static void RemoveTickInternal(std::vector< FTickEntry > &from, FTickEntry tickEntry)
Definition: TickManager.h:175
static FTickEntry AddTick(UObject *Object, std::function< void(float)> Function, ETickType Type)
Definition: TickManager.cpp:55
UObject * Owner
Definition: TickManager.h:60