2.9.16
Coherent GT
A modern user interface library for games
Sample - HelloUWP

This sample demonstrates how to use Coherent GT in a UWP application.

Note
The example application is meant to be an example of how to integrate GT in a UWP game, not a complete UWP app. It may fail to pass some of the certification for the Windows Store.

Building the sample

The sample solution is based on a default game template that VS2015 provides. Some of the sample code has been removed (such as the 3D cube rendering) but the rest is pretty much the same.

The output will be in the Coherent/bin directory.

Prerequisites

This sample builds upon the HelloGT sample and assumes that you understand it.

Key points

  1. The GTClient class wraps all interactions with Coherent GT - initializing the system and system renderer and creating a single view (and its view renderer).
  2. The Game class manages the DX11 device and contains the main loop for the game.

Sample walk-through

The sample shows our Shadow of Saturn (SOS) UI which you've already seen in HelloGT. This controlled in Game::Initialize:

void Game::Initialize(IUnknown* window, int width, int height)
{
...
m_demoPages.push_back("coui://uiresources/sos/MainUI.html");
const char* initialURL = m_demoPages[m_currentDemoPage].c_str();
...
}

You can add multiple pages to m_demoPages and then change between them using the A and D keys (see Game::OnKeyDown).

The key part of the sample is the class GTClient. It implements the management of GT objects. For example it initializes the system, view and their renderers in its constructor

GTClient::GTClient(const char* url, ID3D11Device* device, const CoherentDx11RenderTarget& renderTarget)
{
...
m_System.reset(InitializeUIGTSystem(
COHERENT_UI_GT_LICENSE,
settings,
Coherent::LoggingGT::Trace,
m_LogHandler.get()));
if (!m_System)
{
assert(false && "System initialization failed!");
return;
}
info.Width = renderTarget.Width;
info.Height = renderTarget.Height;
...
m_View.reset(m_System->CreateView(info, url));
if (!m_View)
{
assert(false && "View initialization failed!");
return;
}
m_Backend.reset(new renoir::Dx11Backend(device, enableMemoryTracking));
auto customBackend = static_cast<renoir::Dx11Backend*>(m_Backend.get());
bool didInitialize = customBackend->InitializeStaticResources();
if (!didInitialize)
{
m_Backend.reset(nullptr);
assert(false && "Failed to initialize a rendering backend!");
}
m_UISystemRenderer.reset(m_System->CreateRenderer(m_Backend.get()));
Coherent::UIGT::NativeRenderTarget rt;
rt.Texture = renderTarget.RenderTargetView;
rt.DepthStencilTexture = renderTarget.DepthStencilView;
m_ViewRenderer.reset(m_UISystemRenderer->CreateViewRenderer(
m_View.get(), rt, info.Width, info.Height, renderTarget.SampleCount));
}

It also takes care of updating them:

// Advance returns a FrameId, which contains
// id of the recorded rendering commands and a bool,
// which marks if the id is valid or not
FrameId GTClient::Advance()
{
FrameId frameId;
if (m_System && m_View)
{
m_System->Advance();
frameId.SetValidId(m_View->Layout());
}
return frameId;
}
void GTClient::Render(unsigned frameId)
{
if (m_System && m_ViewRenderer)
{
m_ViewRenderer->Paint(frameId);
}
}
Note
Important: Keep in mind that Coherent GT's objects can only be used on specific threads:
  • Coherent::UIGT::ViewRenderer, Coherent::UIGT::UISystemRenderer, your renoir::RenderingBackend work on the rendering thread if one exists
  • All other objects can only be used on the thread that used to initialize GT which most often is your main thread. This means that you can't access GT objects when handling some events such as Application::Suspending and Application::Resuming as they may be called on threads other than the main thread.

If you are interested in in using a worker thread for Coherent GT, see Asynchronous mode.