1.4.0.3
Gameface
Technical Overview
Note
As mentioned in the Product Overview, Gameface is powered by the Cohtml library. Respectively, all C++ classes reside in the cohtml namespace. The name of the library (Cohtml) is used through out the rest of this document instead of the name of the SDK (Gameface).

At a high level Cohtml does the following things to render your UI:

  • Load and parse HTML files.
  • Build a DOM of the page.
  • Load and parse CSS styles. Styling allows having very rich interfaces with complex layout and visuals.
  • Load image files and fonts.
  • Load and execute JavaScript code. JavaScript is used to code more advanced logic for the user interface.
  • Communicate with the client application to send/receive commands or input.
  • Render the page in a texture. Cohtml uses Renoir - Coherent Labs' internal rendering library that draws the page in a texture that can be used by the client application.

In Cohtml speak, a page along with its styles, DOM and JS code is called a View (the class cohtml::View). You can have as many Views as you need in your application.

Cohtml has an advanced task-based architecture that tries to make the best use of all cores and computational power of the device, while saving battery at the same time. Key technical features of Cohtml include:

  • Task-based architecture. Many operations in Cohtml run on work-threads, which improves scalability, overall performance and battery duration.
  • Data-oriented design. Internally Cohtml owes much of its performance to a modern data-oriented design.
  • High-performance rendering with advanced batching. An additional performance boost is given by the fact that only changed parts of a page are re-drawn each frame - not the whole UI.
  • Asynchronous resource loading. All resources are loaded in work-threads in order to not stall the main UI thread.
  • Full control over memory allocations, file loading, logging, rendering. Users can control how memory and files are loaded. Custom rendering backends can be easily developed with the provided interfaces.
  • Support for iOS, Android, Windows, PlayStation 4, Xbox One, UWP and Mac OS X. Linux support coming soon.
Note
Due to the asynchronous resource loading, styles are applied in the order in which the CSS declarations are parsed. This means that styles from external CSS files will be applied later and you may see the intermediate styles for the first few frames.

Multithreading

Key to the scalability and performance of Cohtml is its multithreaded architecture. At the moment Cohtml designates a "Main" or "UI" thread. This is the thread where the user has to interact with the UI logic - send events, input and execute JavaScript. All of the View's methods should be called on that thread. The other thread on which the user interacts with Cohtml is the "render" thread. This is usually the thread that submits commands to the GPU. On that thread the methods of the ViewRenderer should be called which will, in turn, execute the necessary rendering commands. Additional work includes View resource loading, View Layout, JavaScript garbage collection, etc. All these operations must be scheduled by the user on worker threads or dedicated threads should be created to handle those tasks.

Cohtml will not create threads behind the scenes as they might interfere with the workload of the application.

The different work "channels" are:

  • Main (UI): holds the DOM and executes the JavaScript code. The user submits input and sends events to the UI in this thread. This is the thread on which Library::Initialize was called. Often this is also the "main" thread of the application.
  • Render thread: the ViewRenderer methods are called on this thread. It should have access to the rendering subsystem and the rendering commands will be executed on it. This is usually the render thread of the application. It can change, but you can only execute one "Paint" concurrently.
  • N Worker threads & Layout threads: Cohtml generates work for worker threads for many activities including: resource loading; parsing, layout etc. The Layout operation can also happen in the Main thread in the View::Advance method. This is controlled by the LibraryParams::UseDedicatedLayoutThread flag. The Layout is a heavy operation and it's strongly recommended to let it happen on a worker thread. Using a layout thread is highly recommended as it will significantly reduce the View::Advance time and improve scalability. All these operations must be executed by the application through the Library::ExecuteWork method.

Worker threads execute Resources and Layout tasks. Resources tasks are global for all Cohtml Views as they share the image loading and caching, parsing etc. resources. However the Layout tasks can be scheduled per-View. This allows better scalability and having a separate thread per View Layout is thus possible although not recommended.

Note
Layout tasks should be prioritized over Resources tasks as each cohtml::View::Advance call will block until all the layout work for the previous frame is done.
Resources tasks must always be executed on a thread different than the Main (UI) thread to avoid deadlocks.
Both Resources and Layout work must be executed by the application at some point after it is notified, otherwise a deadlock might occur. It is not possible to execute work in the same call stack as the notification method of the work LibraryParams::OnWorkAvailable - it has to be scheduled. If you choose to dedicate threads only to Cohtml, this is simpler as they'll automatically pick up any new work.

The Cohtml samples contain a TaskScheduler sample class that shows different approaches on the threading integration of the library.

Integrating the task system of Cohtml in an application

The integration flow can go in two main ways:

  • Dedicate one or more worker threads to Cohtml's Resource and Layout tasks. This method is easy to implement, but might not provide the most optimal results:
    • Create one or more worker threads.
      • To process Layout on a separate worker thread, create two dedicated threads (one for Resources and one for Layout);
      • For operating systems using ChakraCore (such as iOS), a minimum of two threads for Resources alone are recommended to prevent hangs;
    • Run the Library::ExecuteWork method on the threads with WorkExecutionMode set to WEM_UntilQuit. The methods will not return until Library::StopWorkers is called, it should happen just before Library::Uninitialize.
  • Integrate with the engine task system. The second option gives more control over scheduling. Most modern game engines have a task-based architecture and task systems that allow running work on separate threads. The Cohtml work can be integrated with such a system and its task interleaved with the other engine work.
    • Make sure to assign a callback to cohtml::LibraryParams::OnWorkAvailable to receive notification when new Cohtml work is available.
    • When the callback is called - inspect the type of work (Resources or Layout) in the type parameter.
    • Schedule a call to cohtml::Library::ExecuteWork with the corresponding WorkType and WorkExecutionMode.
    • Make sure the work is done in another call stack (usually a different thread). Failing to execute the work might lead to deadlocks.

The following diagram shows the approaches to the execution models described:

threading_integration.png

You can also see the same distribution yourself in the performance panel of the Dev Tools (aka Inspector).