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;
15std::vector<FTickEntry> ATickManager::LateTickFunctions;
16std::vector<FTickEntry> ATickManager::TicksToRemove;
17
19{
20 Super::BeginPlay();
21
22 if (Instance == nullptr)
23 {
24 Instance = this;
25
26 // Subscribe to pre and post tick actor delegates
27 OnPreTickDelegate = FWorldDelegates::OnWorldPreActorTick.AddUObject(this, &ATickManager::PreTick);
28 OnPostTickDelegate = FWorldDelegates::OnWorldPostActorTick.AddUObject(this, &ATickManager::PostTick);
29 }
30 else
31 {
32 Destroy();
33 }
34}
35
36void ATickManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
37{
38 Super::EndPlay(EndPlayReason);
39
40 if (Instance == this)
41 {
43 LateTickFunctions.clear();
44 TicksToRemove.clear();
45 FWorldDelegates::OnWorldPreActorTick.Remove(OnPreTickDelegate);
46 FWorldDelegates::OnWorldPostActorTick.Remove(OnPostTickDelegate);
47 Instance = nullptr;
48 }
49}
50
51FTickEntry ATickManager::AddTick(UObject* Object, std::function<void(float)> Function, ETickType Type)
52{
53 if (Instance == nullptr)
54 {
55 if (GEngine || GEngine->GameViewport)
56 {
57 UWorld* World = GEngine->GameViewport->GetWorld();
58 World->SpawnActor<ATickManager>();
59 }
60 }
61
62 FTickEntry TickEntry(Object, Function, Type);
63
64 switch (Type)
65 {
66 case ETickType::FullFrameTaskParallel:
68 break;
69 case ETickType::LateTickParallel:
71 break;
72 default:
73 UE_LOG(LogTemp, Warning, TEXT("TickManager.cpp: Unsupported ETickType in AddTick."));
74 break;
75 }
76
77 return TickEntry;
78}
79
81{
82 // Add tick entry to remove into std::vector. These get deleted at the end of the frame.
83 TicksToRemove.push_back(TickEntry);
84}
85
86void ATickManager::PreTick(UWorld* World, ELevelTick TickType, float DeltaSeconds)
87{
88 // Start FullFrameTask that runs the entire frame
89 // if the task is small, it can finish at any time
90 if (FullFrameTaskFunctions.size() != 0)
91 {
92 FullFrameTask = FFunctionGraphTask::CreateAndDispatchWhenReady([this, DeltaSeconds]()
93 {
95 }, TStatId(), nullptr, ENamedThreads::AnyBackgroundHiPriTask);
96 }
97}
98
99void ATickManager::PostTick(UWorld* World, ELevelTick TickType, float DeltaSeconds)
100{
101 // Tick all added late ticks in parallel
102 if (LateTickFunctions.size() != 0)
103 {
105 }
106
107 // Wait till all FullFrameTask ticks are completed
108 if (FullFrameTask.IsValid())
109 {
110 FTaskGraphInterface::Get().WaitUntilTaskCompletes(FullFrameTask);
111 }
112
113 FrameCleanup();
114}
115
116inline void ATickManager::TickFunctionsParallel(std::vector<FTickEntry>& functions, const float DeltaTime)
117{
118 bool foundNull = false;
119 //std::vector<FTickEntry> functionsCopy = functions;
120 const int32 size = functions.size();
121 ParallelFor(size, [&](int32 index)
122 {
123 FTickEntry& tickEntry = functions[index];
124 if (tickEntry.Owner != nullptr)
125 {
126 tickEntry.Tick(DeltaTime);
127 }
128 else
129 {
130 foundNull = true;
131 }
132 }, EParallelForFlags::Unbalanced);
133
134 if (foundNull)
135 {
136 functions.erase(std::remove_if(functions.begin(), functions.end(), [](const FTickEntry& Entry) { return Entry.Owner == nullptr; }), functions.end());
137 }
138}
139
141{
142 if (TicksToRemove.size() != 0)
143 {
144 for (const FTickEntry& tickEntry : TicksToRemove)
145 {
146 ETickType Type = tickEntry.TickType;
147 switch (Type)
148 {
149 case ETickType::FullFrameTaskParallel:
151 break;
152 case ETickType::LateTickParallel:
154 break;
155 default:
156 UE_LOG(LogTemp, Warning, TEXT("TickManager.cpp: Unsupported ETickType in RemoveTick."));
157 break;
158 }
159 }
160 TicksToRemove.clear();
161 }
162
163 // Destroy this Actor if we have no ticks
164 // TODO: Should we just keep this alive?
165 if (FullFrameTaskFunctions.size() == 0 &&
166 LateTickFunctions.size() == 0)
167 {
168 if (Instance != nullptr)
169 {
170#if WITH_EDITOR
171 UE_LOG(LogTemp, Warning, TEXT("TickManager.cpp: Destroying as there are no more ticks."));
172#endif
173 Instance->Destroy();
174 Instance = nullptr;
175 }
176 }
177}
ETickType
Definition: TickManager.h:24
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override
Definition: TickManager.cpp:36
FDelegateHandle OnPreTickDelegate
Definition: TickManager.h:120
static std::vector< FTickEntry > LateTickFunctions
Definition: TickManager.h:134
static std::vector< FTickEntry > TicksToRemove
Definition: TickManager.h:135
void PreTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
Definition: TickManager.cpp:86
void PostTick(UWorld *World, ELevelTick TickType, float DeltaSeconds)
Definition: TickManager.cpp:99
static void AddTickInternal(std::vector< FTickEntry > &to, const FTickEntry &newTickEntry)
Definition: TickManager.h:137
FGraphEventRef FullFrameTask
Definition: TickManager.h:130
FDelegateHandle OnPostTickDelegate
Definition: TickManager.h:123
static void RemoveTick(FTickEntry TickEntry)
Definition: TickManager.cpp:80
static ATickManager * Instance
Definition: TickManager.h:132
void FrameCleanup()
static std::vector< FTickEntry > FullFrameTaskFunctions
Definition: TickManager.h:133
virtual void BeginPlay() override
Definition: TickManager.cpp:18
void TickFunctionsParallel(std::vector< FTickEntry > &functions, const float DeltaTime)
static void RemoveTickInternal(std::vector< FTickEntry > &from, FTickEntry tickEntry)
Definition: TickManager.h:155
static FTickEntry AddTick(UObject *Object, std::function< void(float)> Function, ETickType Type)
Definition: TickManager.cpp:51
UObject * Owner
Definition: TickManager.h:49