Run the QuickStart.bat / QuickStart.sh scripts to open the Gameface launcher. This application will take you through the most important parts of the archive you received. The following document describes how to start integrating Gameface with your engine.
The sample applications coming with Gameface are available in the Samples folder. The Samples/Common folder contains platform-independent code that can be consulted on integrating Cohtml in a C++ application. Central in all samples is the CohtmlApplication
class that resides in the Common/CohtmlApplication folder. This class is the one in charge of initializing, running and closing Cohtml. All the platform-specific code is in the samples themselves for Android, iOS and Windows Desktop.
The Android Studio sample project can be opened by selecting the Samples/Android folder. The iOS/MacOS sample applications can be compiled with the corresponding Samples/CohtmlSamples.xcworkspace files. For Windows and other all other platforms use the Samples/CohtmlSamples.sln for the respective platform. Platform specific projects require Visual Studio 2017 with the corresponding SDK installed.
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).For a simple and straightforward implementation of the steps required to start Cohtml check out the MinimalHelloCohtml sample (for Windows and DirectX 11 only).
In general, an application that uses Cohtml has to go through the following steps:
All the operations are shown in code in the CohtmlApplication.cpp file. Let's explore each one of them:
To build an object using Cohtml you must:
Note: You also need to distribute some shared/dynamic libraries with your application. They are explained below on a per-platform basis. If you want your UI to have HTTP or Video rendering capabilities you should also distribute the HttpServer and MediaDecoders libraries respectively.
On Windows the relevant link dependencies are:
On Xbox One the relevant link dependencies are:
On PlayStation 4 the relevant link dependencies are:
On Android you have to link against:
r10e android ndk
, you will get unresolved symbol error for __page_size
, which you have to define, e.g. #include <unistd.h>unsigned int __page_size = getpagesize();
On iOS you have to link against:
bool CohtmlApplication::Initialize(const Options& options){// Initialize the application logging system.m_Logger.reset(new Logger("TestApp.log"));gLogger = m_Logger.get();// Cohtml initilization begin here// 1. We need to initilize the Cohtml LibraryLibraryParams params;params.LoggingSeverity = cohtml::Logging::Info; // What logging messages we want to receiveparams.Allocator = &m_MemoryTracker; // All memory allocations go through a user-supplied allocatorparams.WritableDirectory = options.WritableDirectory; // We have to provide a writable folder where Cohtml will output logged inforamtionparams.FileSystemReader = options.FileReader; // The file reader will be used to read local resources like fonts.params.UseDedicatedLayoutThread = options.MakeDedicatedLayoutThread; // If we'll be doing the Layout work in an auxilliary thread or in the Advance methodparams.SharedLibraryLocation = options.SharedLibraryLocation;// If we simulate a task system - we want to be notified when there are new Cohtml work tasks we have to// execute in helper threads. This is not needed if we dedicate worker threads completely to Cohtml and is// actually a performance penalty.if (m_TaskSchedulerMode == TaskScheduler::Mode::SimulateTaskSystem){params.OnWorkAvailable = &OnWorkAvailable;params.OnWorkAvailableUserData = this;}// 2. Initialize the library now.m_Library = Library::Initialize(COHTML_LICENSE_KEY, params);if (!m_Library){APP_LOG(Error, "Unable to initialize COHTML Library!");return false;}// 2.1 Start the Task Scheduler - it will start doing Cohtml work on auxilliary threads// The Task Scheduler in these samples can be used as an example on how to integrate// Cohtml in an engine's task (job) system. It supports different modes in order to better// illustrate the options available for integration.m_TaskScheduler.reset(new TaskScheduler(m_TaskSchedulerMode, m_Library, m_HasDedicatedLayoutThread));// 3. We have to create a "System". Each system can hold multiple "Views" and provide// a common context for them.cohtml::SystemSettings sysSettings;// The resource handler is used for loading View-specific resources from local files.if (options.ResourceHandler){sysSettings.ResourceHandler = options.ResourceHandler;}else{sysSettings.ResourceHandler = &m_ResourceLoader;}m_System = m_Library->CreateSystem(sysSettings);if (!m_System){APP_LOG(Error, "Unable to initialize COHTML System!");return false;}m_Views.resize(options.ViewsCount);for (auto i = 0u; i < options.ViewsCount; ++i){auto& current = m_Views[i];// 4. Now create the View. The View holds a single UI instance - the HTML DOM, styles and a// JavaScript context. This could be the whole HUD of the application.cohtml::ViewSettings viewSettings;viewSettings.Width = options.Width; // The logical Width of the ViewviewSettings.Height = options.Height; // The logical Height of the View// Called when the View is completely advance and laid-out// @note This can be called on any thread!// For example attached is &Application::WakeRenderingThread;viewSettings.OnViewAdvanceComplete = options.OnViewAdvanceComplete;// Passed to the OnViewAdvanceComplete callbackviewSettings.UserData = m_UserData;// 5. Set up the listener for this view.// The Listener gets notifications for various events of the view, such as when the view can accept JavaScript bindings.current.Listener.Application = this;viewSettings.Listener = ¤t.Listener;// 6. Create our View nowcurrent.View = m_System->CreateView(viewSettings);if (!current.View){APP_LOG(Error, "Unable to create COHTML View!");return false;}current.Listener.View = current.View;current.ScriptCreatedCallback = options.OnScriptCreated;current.ShowVirtualKeyboardCallback = options.OnShowVirtualKeyboard;// This method can be very useful for debugging. Enable to render dirty rects and regions.//current.View->ShowPaintRectangles(true); // useful for debugging// 7. Load a page in the View. Local pages need to have the special "coui" protocol.// For instance a valid page to load is: "coui://uiresources/HUD.html"if (options.InitialURL != nullptr && strlen(options.InitialURL) > 0){current.View->LoadURL(options.InitialURL);}else{APP_LOG(Error, "Initial URL is not set!");return false;}if (options.RedrawAll){// Method useful for debugging. Enable to re-draw everyting on every frame.current.View->ContinuousRepaint(true);}current.NameplatesPtr.reset(new Nameplates(current.View));}APP_LOG(Info, "Initialized application!");return true;}
bool CohtmlApplication::InitializeRenderThread(const RendererOptions& options){// 1. Create the System renderer. It holds resources shared by all ViewRenderers within a Systemcohtml::SystemRendererSettings sysRendSettings;m_SystemRenderer = m_System->CreateSystemRenderer(sysRendSettings);if (!m_SystemRenderer){APP_LOG(Error, "Unable to create COHTML System renderer!");return false;}// 2. Set the rendering backend that will be used by the SystemRenderer// All Views created from the same System will share resources here (text atlases, textures etc.)m_SystemRenderer->RegisterRenderThread(options.Backend);auto i = 0u;for (auto& current : m_Views){// 3. Create the ViewRenderer that will draw the pagecohtml::ViewRendererSettings viewRendSettings;current.ViewRenderer = m_SystemRenderer->CreateViewRenderer(current.View, viewRendSettings);if (!current.ViewRenderer){APP_LOG(Error, "Unable to create COHTML View renderer!");return false;}// 4. Set the user texture where the renderer will draw the Viewif (options.NativeTextures != nullptr){current.ViewRenderer->SetRenderTarget(options.NativeTextures[i].ViewNativeTexture,options.NativeTextures[i].NativeDepthStencilTexture,options.NativeTextures[i].ViewNativeTextureWidth,options.NativeTextures[i].ViewNativeTextureHeight,options.NativeTextures[i].ViewNativeTextureSamples);}++i;}m_HasInitializedRenderer = true;APP_LOG(Info, "Initialized Render Thread!");return true;}
Each frame in the main thread you have to "Advance" the Views - update its internal clock and let animations happen. Update position of nameplates.
void CohtmlApplication::Advance(double timeMiliseconds){if (!m_HasInitializedRenderer)return;for (auto& current : m_Views){// Call each frame to drive the animationsif (current.View){auto frameId = current.View->Advance(timeMiliseconds);// This is a sample workload in the samplecurrent.NameplatesPtr->Update(float(timeMiliseconds));// Post a render task on the render threadstd::lock_guard<std::mutex> l(FramesMutex);current.FramesToPaint.push(frameId);}}// Advance the Systemm_System->Advance(timeMiliseconds);}
At the same time, in the render thread we have to let Cohtml update the UI textures.
void CohtmlApplication::Render(){for (auto& current : m_Views){// Call each render frame to draw the Viewif (current.ViewRenderer){std::lock_guard<std::mutex> l(FramesMutex);while (!current.FramesToPaint.empty()){auto frameId = current.FramesToPaint.front();current.FramesToPaint.pop();current.ViewRenderer->Paint(frameId, true);}}}}
When the Paint method has finished we compose the UI texture on-top of the application. This is done differently on the platforms. On iOS for instance consult the GameViewController.mm file where in the drawInRect
method the UI texture is copied on the final iOS framebuffer.
You can load another page in the current View by using the Load
method. It will load all resources in the new page and re-create the scripting context. You will have to re-bind all native methods upon a page change.
The uninitialization of objects must happen in the following order:
On the render thread:
cohtml::ViewRenderer::Destroy()
of each Viewcohtml::SystemRenderer::FreeRenderingResources();
and cohtml::SystemRenderer::Destroy();
On the main thread:
cohtml::View::Destroy()
each Viewcohtml::System::Destroy()
cohtml::Library::StopWorkers()
cohtml::Library::Uninitialize()
All resources that are owned by the application -> resource readers, logger, memory allocator and rendering backend must be destroyed after the Cohtml objects that use them.
When building in debug, your app should be automatically signed with auto-generated debug keystore. When you build for release, however you have to sign the app yourself. To do that follow these steps in Android Studio:
The Player application allows you to quickly preview any UI created for Cohtml. Double click Player.bat / Player.sh to launch it and drag and drop your file. Alternatively, open Player.bat / Player.sh in your favorite text editor and follow the instructions there to change the settings with which the Player starts.