Is there a way to make two Actors tick in parallel.
I am profiling my scene using Unreal Insights and my Tick takes about 60ms.
In the breakdown it shows the two actor ticks (both around 30ms) laid out serially in the timeline.
The actors do not interact with each other, so I am wondering if it is possible to tick them at the same time so that I can half my frame Tick time.
Does this make sense?
if i get you right, you want to run gameplay code in parallel in several diffirent threads. Afaik UE doesnât support it for GameThread.
Unfortunately not. Afaik gameplay code running on a single thread on purpose to simplify itâs logic without diving into synchronization and other multithreading hell things: gameplay by its own is already complex enough.
While itâs probably technically possible, i guess it wonât be an easy task for epics to move engine to gameplay multithreading and would make things even harder for us, ue users.
As others pointed out, not possible on the blueprint side of things.
30ms is massive, you donât want that on the gamethread. Even if you could parallelize the two, youâd still be running at 30 fps which is awful.
I wonât even recommend moving away from Tick into a Timer, because that would generate stutters on timer basis, which sucks.
At this point, you need to either optimize/rework your stuff to smooth it up overtime, or run it in a separate dedicated thread so that it doesnât impact game thread. In both cases, youâll want to move to C++, and it probably wonât be cycling as fast as every Tick.
Here are some common optimization techniques :
Move from blueprints to C++. If you are doing some algorithm with lots of calculations, lots of nodes especially functions calls, then simply moving to C++ can improve performance by a tenfold.
Split your calculations over several ticks. If you are calling several functions which are heavy, reorganize your code so you can call FunctionA in a tick then FunctionB in the next tick then process results in the next tick. Cache results in class variables. Hereâs a simplified example :
// class member variables
int32 CalculationStep;
float ResultA;
float ResultB;
void AMyActor::Tick(float dt)
{
switch (CalculationStep)
{
case 0:
ResultA = SomeHeavyFunctionA();
break;
case 1:
ResultB = SomeHeavyFunctionB();
break;
case 2:
ProcessResults();
break;
}
CalculationStep = (CalculationStep + 1) % 3;
}
If you are using for loops, you can achieve something similar, by âglobalizingâ the index and the variables you need in loop body. For example :
//OLD CODE:
void AMyActor::Tick(float dt)
{
for (auto& Element : SomeArray)
DoSomeStuff(Element);
}
//NEW CODE:
// class member vars
int32 LoopIndex;
void AMyActor::Tick(float dt)
{
// limit to 100 iterations per frame
for (int32 NumIt = 0; NumIt < 100 && LoopIndex < SomeArray.Num(); ++NumIt, ++LoopIndex)
{
auto& Element = SomeArray[LoopIndex];
DoSomeStuff(Element);
}
if (LoopIndex == SomeArray.Num())
LoopIndex = 0;
}
Alternatively, if your code doesnât interact too much with the gameplay and doesnât rely on engine functions that require main thread, then you can create a thread to do the heavy calculations. Initialize it with all the data required to calculate, then let it report back on main thread once itâs done. This approach is not efficient if you need to constantly communicate between threads, so the algorithm/calculations should be able to run in isolation from the initial data alone and until the calculation is done. Of course you can update the data inbetween each âcycleâ, hopefully you get the point.
So, actually, I am using C++.
I am not looking to achieve 120fps or anything like that.
This is not for a game.
What is taking so long is updating millions of vertex positions and normals every frame.
I have a custom UDynamicMeshActor with about 1.5 million triangles.
I have played with all the different functions from FMeshNormals, etc, and found a good setup. But between the recalculation and then updating the UDynamicMeshComponent with FastNotifyPositionsUpdated( true );, it just takes a while
In my scene I have two of those actors, each takes 30ms per tick. So if I could get them to tick in parallel, in theory that would bring it down to 30ms for the total tick (instead of the 60ms I am getting right now).
Some things Iâd look at - are all vertices all in view at once and do the vertices have collision? If not chunk them up and donât update the chunks that are not in view. Or do some kind of LOD thing where distant chunks only get updated less or at a different granularity.
Hmm maybe you can set up your own sort of pipeline to calculate and update the meshes in parallel of the gameâs ticking system. Iâm thinking something like this :
bind a Tick to TG_PrePhysics, where you start an async task to do calculations (and updating if possible) in a separate thread, for each actor
bind a second Tick to TG_PostPhysics or TG_PostUpdateWork, and do a hard wait for each task to finish, retrieve result and finish updating
Or you could just start both tasks and wait both of them in a single Tick function, but itâd be significantly better to run it in parallel of all other ticks & physics (& possibly rendering). Maybe start with one function first and then try to upgrade it.
No collision at all. Unfortunately I have to assume all will be in view at once due to the nature of the output (full mesh view and translucent materials are common use cases).
Thanks, great suggestion.
I was thinking about doing something like this as well and your idea sounds like the only real option.
Iâll give it a go over the next few days / week and will let you know if this worked for me.
@Chatouille I had a look at the Async API and did something along the lines of what you suggested.
Since my tick consists basically of two parts:
Calculate new positions + normals
Update dynamic mesh to be drawn
I am running part 1) in Async.
So on frame 1, I start 1) and do nothing else.
On all consecutive frames, I wait for the Async from previous frame to be done (if not done yet) and then use the result for 2). I then immediately start Async 1) for the next frame.
Overall, this obviously means I am running 1 frame delay on my Actor, but since I am in control, I just make sure that all other actors that need to be synchronized for rendering are also getting a 1 frame delay.
Seems to be working quite well and 1), which has heavy calculations in it is no longer slowing me down much.