Coherent GT has support for multithreading which can greatly enhance your game's performance at an added complexity cost.
When using the async mode, most calls to Coherent GT are deferred and executed on another thread. We guarantee that your callbacks and event listeners will still be run on the main thread (the thread that they are registered on, that is).
Furthermore, Coherent GT will never deadlock but race conditions are unavoidable and as such the user must protect against them by himself.
Enabling the async mode is as simple as setting Coherent::UIGT::SystemSettings::RunAsynchronous to true
when creating the system:
SystemSettings settings; settings.RunAsynchronous = true; .. auto system = InitializeUIGTSystem(licenseKey, settings, severity, logHandler);
Note that some methods are always synchronous and will block your main thread no matter what the current mode is. These methods are marked with a warning in the documentation. Some of them are:
Asynchronicty implies that the JavaScript execution is also deferred on the UI thread. This may require some changes if your existing code depends on the fact that up to now, callbacks were executed in the same stack.
Consider the following communication:
C++:
void Bar() { // Code } // During initialization view->BindCall(Coherent::UIGT::MakeHandler("bar", Bar)); // During your main loop view->TriggerEvent("foo");
JS:
engine.on("foo", function fooHandler() { // Code engine.trigger("bar"); });
Calling view->TriggerEvent("foo")
in synchronous mode will execute the methods in the following simplified callstack:
Callstack |
---|
Bar |
JavaScript::fooHandler |
View::TriggerEvent |
GameLoop |
In async mode you get no such guarantee. fooHandler
will be executed whenever the UI thread gets to it and Bar
will be executed during the first Coherent::UIGT::View::Layout or Coherent::UIGT::View::ExecuteJSTimers after fooHandler
has completed. The following diagram demonstrates that:
Let's look at a more concrete example
C++:
void StartGame() { // Initialize the game } // During initialization view->BindCall(Coherent::UIGT::MakeHandler("StartGame", StartGame));
JS:
startButton.addEventListener("click", function () { engine.trigger("StartGame"); doSomethingAssumingTheGameHasStarted(); });
If you are running in a synchronous mode, this code will work fine. When the button is clicked, the engine will call StartGame
immediately, which will in turn initialize the game. Thus, by the time engine.call("StartGame")
returns you can assume that everything is ready.
This assumption does not hold in asynchronous mode as the execution of StartGame
will be delayed. To cope with this, use another event to notify JS that everything is ready:
C++:
void StartGame() { // Initialize the game view->TriggerEvent("GameStarted"); } // During initialization view->BindCall(Coherent::UIGT::MakeHandler("StartGame", StartGame));
JS:
startButton.addEventListener("click", function () { engine.call("StartGame"); }); engine.on("GameStarted", function () { doSomethingAssumingTheGameHasStarted(); });
In order to get the maximum performance from the async mode, try to issue the update calls to Coherent GT (Coherent::UIGT::UISystem::Advance, Coherent::UIGT::View::Layout) as early as possible in your game loop and paint the view as late as possible. This allows the UI thread to do as much work as possible before its results are actually needed.
With the async mode it is possible that a call to Coherent GT hasn't completed yet even though it looks like so. For example:
view->Resize(newWidth, newHeight); view->Paint(frameId);
There's great chance that the UI thread won't resize the view in time which means that you'll paint the view using its old dimensions. Most of the time this won't be a problem because by the next frame the resize will be done. Still, if you need to synchronize with the UI thread, Coherent GT provides a solution through the fencing API:
view->Resize(newWidth, newHeight); // Make sure that the view has been resized prior to the next paint auto fence = gtsystem->Fence(); ... gtsystem->WaitFor(fence); view->Paint(frameId);
Coherent::UIGT::UISystem::Fence and Coherent::UIGT::UISystem::WaitFor do nothing when invoked on a synchronous system so feel safe to use them anywhere and change the mode without changing any of your code.
You may not initialize two different instances of Coherent::UIGT::UISystem with different synchronicity mode during the same application run. Doing so will result in your HTML code rendered instead of your actual views.
Additionaly, due to the former limitation, if you plan on uninitializing the current UISystem
and initializing a new one (e.g. in order to change other system settings) you must pass false
to Coherent::UIGT::UISystem::Uninitialize(bool shouldTerminateUIThread).
true
to terminate the UI thread or call FreeUIGTLibraryResources. Forgetting to do that will leak all objects still alive in the UIThread.Knowing what Coherent GT does under the hood might be useful if you run into any problems.
If you are to call Coherent::UIGT::View::Layout for example, instead of running the layout immediately, Coherent GT will now post a job task to another thread (the UI thread) via a standard single-producer / single-consumer queue. The method will immediately return the id of the rendering commands it is going to record, so you can use it to later call Paint with it. The UI thread waits for any tasks to be posted and executes them in order. Note that Coherent will wait for the previous call to layout for this view to complete before issuing the next command in the interest of preventing overtasking the UI thread.
The UI thread can communicate to the main thread via another task queue. For example, when the time for execution of 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 runs only on the main thread. Most of these events will be triggered during the call to Coherent::UIGT::View::Layout so make sure to call that if you need to be notified about any events going on.