Improve your game UI performance with Coherent GT asynchronicity
I’m eager to introduce to you a major feature we’ve been working on for some time. Those of our clients who have already tried out Coherent GT have experienced how lightweight and performance-minded it is.
Coherent GT asynchronicity
To improve scalability and performance for heavy-UI apps we made a radical innovation in Coherent GT. Coherent GT can now run in an asynchronous mode. This means that you can move most of the heavy computation-wise functions in a separate threat which will boost the performance of your main thread by an order of magnitude. Note that you can still use the synchronous version, in fact, it will remain the default setting.
Knowing how Coherent GT works, under the hood, could be useful for optimizing your code, so I’ll briefly explain what’s going on.
Under async mode, If you are to call
Coherent::UIGT::View::Layout for example, instead of running the layout call immediately, Coherent GT will now post a job task to another thread (I’ll be calling this thread the UI thread from now on) via a queue. The UI thread waits for any tasks to be posted and executes them in order. Note that Coherent GT will wait for the previous call to
Coherent::UIGT::View::Layout for this view to complete before issuing the next command. In this way the UI thread will never be over-encumbered with tasks.
The UI thread communicates with the main thread via another task queue. For example, when the time for executing your event handlers (registered via
Coherent::UIGT::View::RegisterForEvent or through your custom view listener) comes, the UI thread will ask the main thread to run the callback. This guarantees that your code will run only on the main thread. Most of these events will be triggered during the call to
Coherent::UIGT::View::Layout , so make sure that you call that method if you need to be notified about any events going on.
Note that there are no changes to
Coherent::UIGT::ViewRenderer and you can still paint the view on your rendering thread without complications.
Give me data!
One of the tests which we performed to prove if the async mode would be successful was a small demo scene in UE4.
The scene below contains a ton of cubes, each moving at a random direction within a circle and having a nameplate attached to it. In order to simulate a busy main thread, besides updating its nameplate each cube also computes the first n prime numbers.
Here’s our average test data measured in milliseconds (lower is better):
|Metric Mode||Without UI||Sync UI||Async UI|
With async UI:
You can see that as long as your main thread takes more time per frame than the UI thread, your UI’s overhead is minimal on the main thread. Unless you are using your UI for mining bitcoins or trying to raise a zombie army, it is highly likely that this will always be the case. Because of this, you can run much more animations and effects without slowing down your application.
You might have noticed that there is some extra overhead on the GPU thread(~0.25ms) caused by the async mode. This overhead is due to the necessary synchronization between UE4’s rendering thread and our code, but you should worry not! The overhead will diminish the more you use your GPU thread as the UI thread will have more time to catch up, thus the GPU thread will wait less.
Another great news is that, now, Coherent GT is nearly “free” performance-wise in the main thread!
Public API changes
You might be wondering how much of your code will need to be changed to make use of the async mode – well, a single line.
Enabling the async mode is done through a new boolean flag in the
RunAsynchronous. Set this to
true (by default it is turned off) and you are good to go.
Of course, in general multithreading comes at a cost – you might run into race conditions or deadlocks. Coherent GT guarantees that no deadlocks will occur, however, we cannot promise the same for race conditions. To help you combat any incarnations of these foul beasts, we provide a fencing mechanism for synchronization with the main thread similar to what UE4 does with its rendering fences.
// I want to make sure that the view has been resized prior to the next paint
auto fence = gtsystem->Fence();
Due to various reasons, not all methods are susceptible to conversion to asynchronous tasks. The latter are marked with a warning in the documentation stating that they run synchronously no matter what the current system mode is. These include all methods that can change the currently loaded URL, initialize and uninitialize the system or destroy а view.
Another limitation you should keep in mind is that you must not kill the UI thread during uninitialization if you plan to create another instance of the system during the current run of the application. This might be the case if you need to restart the system in order to change any system settings.
By default, we’ll terminate the UI thread during
Coherent::UIGT::UISystem::Uninitialize. If you need to restart the system later, uninitialize the current one without terminating the UI thread by passing false to the same method.
Furthermre, due to the same limitation, you cannot initialize a system in one mode, uninitialize it and then initialize another system with a different sync mode during the same application run. In other words, changing the sync mode requires a restart of your application.
One last important note: the Live View feature will work under asynchronous mode from version 1.3.
All of our UE4 plugin users can change the mode through the editor menu (Coherent GT -> Options). As noted above, you’ll need to restart the editor for the changes to take place.
I hope that you are as excited as we are for the release of Coherent GT async mode! Many other cool features are currently in development so follow our blog, twitter or visit the forums for further announcements.