Coherent UI  2.5.3
A modern user interface library for games
 All Classes Namespaces Functions Variables Enumerations Enumerator Pages
Rendering Integration

There are two ways for getting a rendered view from Coherent UI:

  1. Shared Textures - this is the more efficient way because the rendered texture never goes down from GPU memory to system memory, but has more requirements for your rendering. This feature is fully supported on Windows and MacOSX.
  2. Shared Memory - the rendered texture is taken down to system memory in a shared memory segment between the GPU process of Coherent UI and your process.

In both cases the Coherent::UI::ViewListener::OnDraw method is called during the Coherent::UI::ViewContext::FetchSurfaces. If the view cannot be rendered at this moment, then the texture should be copied, so that it can be rendered later. Coherent::UI::ViewContext::FetchSurfaces (for buffered views) and Coherent::UI::View::FetchSurface (for on-demand views) can be called on a thread different than the one that updates the system.

Shared Textures

Shared textures are efficient way for shared rendering resources between different DirectX or OpenGL(on MacOSX) devices.

Windows:

class ViewEventListener : public Coherent::UI::ViewListener
{
public:
virtual void OnDraw(CoherentHandle handle, bool usesSharedMemory, int width, int height)
{
IDirect3DTexture9* texture = m_Buffers[handle];
IDirect3DSurface9* surfaceSource;
texture->GetSurfaceLevel(0, &surfaceSource);
IDirect3DSurface9* surfaceDest;
m_Texture->GetSurfaceLevel(0, &surfaceDest);
m_Device->StretchRect(surfaceSource, nullptr, surfaceDest, nullptr, D3DTEXF_NONE);
surfaceSource->Release();
surfaceDest->Release();
}
virtual void CreateSurface(bool, unsigned width, unsigned height, Coherent::UI::SurfaceResponse* response)
{
Coherent::UI::CoherentHandle sharedHandle = 0;
IDirect3DTexture9* texture = nullptr;
// create a shared texture
auto result = m_Device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture, &sharedHandle);
m_Buffers.insert(std::make_pair(sharedHandle, texture));
response->Signal(Coherent::UI::CoherentHandle(sharedHandle));
}
virtual void DestroySurface(CoherentHandle surface, bool usesSharedMemory)
{
HandleToBufferMap::iterator buffer = m_Buffers.find(surface);
if (buffer != m_Buffers.end())
{
m_Renderer->DestroyTexture(buffer->second);
m_Buffers.erase(buffer);
}
}
};
Warning
The format for DirectX9 must be D3DFMT_A8R8G8B8
The format for DirectX10 and DirectX11 must be B8G8R8A8_UNORM

MacOSX:

void CoherentViewListener::OnDraw(Coherent::UI::CoherentHandle handle, bool useSharedMem, int width, int height)
{
HandleToBuffersMap::iterator it = m_Buffers.find(handle);
if (it == m_Buffers.end())
{
return;
}
int texId = GetTexture(handle);
if (texId != GLUtility::INVALID_ID)
{
m_Context->GetGLUtility()->StretchTexture(
texId, m_Context->GetTextureFromHost(), true);
}
}
void CoherentViewListener::CreateSurface(bool sharedMem, unsigned width, unsigned height, Coherent::UI::SurfaceResponse* response)
{
Coherent::UI::CoherentHandle result(0);
int sharedHandle(0);
int id = m_Context->GetGLUtility()->CreateTexture(
width, height, &sharedHandle);
if (id == GLUtility::INVALID_ID)
{
response->Signal(Coherent::UI::CoherentHandle(0));
}
result = Coherent::UI::CoherentHandle(sharedHandle);
bool insert = m_Buffers.insert(std::make_pair(result, id)).second;
assert(insert && "duplicated handles");
response->Signal(result);
}
void CoherentViewListener::DestroySurface(Coherent::UI::CoherentHandle buffer, bool useSharedMem)
{
HandleToBuffersMap::iterator bufferTex = m_Buffers.find(buffer);
if (bufferTex != m_Buffers.end())
{
return;
}
IOSurfaceRef ioSurface = IOSurfaceLookup((IOSurfaceID)(size_t)bufferTex->first);
if (ioSurface)
{
CFRelease(ioSurface);
}
m_Context->GetGLUtility()->DestroyTexture(bufferTex->second);
m_Buffers.erase(bufferTex);
}
Note
On MacOSX for the shared textures case Coherent UI uses IOSurfaces. You can refer to the GLUtility class in the HelloHTMLCocoa sample for an example on how to create them and bind them to OpenGL textures.

Coherent UI requests the client to create a surface for it's use. Depending on the way the view was created it will request either a handle to a shared texture or to shared memory. The Coherent::UI::SurfaceResponse allows for asynchronous creation of surfaces. You can call 'Signal' on the response object at any later time provided you do it on the same thread that calls the context 'Update' method. For the sake of simplicity all samples signal for the newly created surface immediately. A multithreaded game engine can use this feature if the rendering thread is not the same that interacts with the view context. When a new frame is available Coherent::UI::ViewListener::OnDraw gets called. Surfaces are double buffered for optimal performance so Coherent::UI::ViewListener::CreateSurface might be called many times. During resizes Coherent::UI::ViewListener::CreateSurface and Coherent::UI::ViewListener::DestroySurface might also be called many times. Both methods might be called in Coherent::UI::ViewContext::FetchSurfaces or Coherent::UI::ViewContext::Update.

Warning
Shared textures are not properly supported by Windows 7 prior to Service Pack 1 and you may experience random freezing of Coherent UI. For more information see Requirements.

For more details see:

Basic samples demonstating how to use shared textures are HelloHTMLDx9, HelloHMTLDx11 on Windows and HelloHTMLCocoa on MacOSX.

See Also
Coherent::UI::ViewListener::CreateSurface for surface format and other details.

Shared Memory

When using shared memory the view must be created with Coherent::UI::ViewInfo::UsesSharedMemory set to true. In any calls to the Coherent::UI::ViewListener::OnDraw for this view the supplied handle will be a handle to shared memory buffer containing the rendered view as bitmap data. The bitmap is in BGRA format, single unsigned char per component. The pixels are ordered top to bottom, left to right.

Note
On MacOSX the shared memory creation should be performed via a temporary file mapped as shared. This is due to a size limit that the platform enforces on shared memory created with shmget.

Windows version:

class ViewEventListener : public Coherent::UI::ViewListener
{
public:
virtual void OnDraw(CoherentHandle handle, bool usesSharedMemory, int width, int height)
{
// GetViewWidth() and GetViewHeight() are functions that
const size_t size = GetViewWidth() * GetViewHeight() * sizeof(unsigned);
void* bitmap = ::MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size);
// create a texture from the bitmap and draw it
// or save it to a jpeg file
::UnmapViewOfFile(bitmap);
}
virtual void CreateSurface(bool, unsigned width, unsigned height, Coherent::UI::SurfaceResponse* response)
{
// create large enough shared memory buffer for width * height pixels,
// ARGB unsigned char format.
const size_t size = width * height * sizeof(unsigned);
response->Signal(Coherent::UI::CoherentHandle(::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, size, nullptr)));
}
virtual void DestroySurface(CoherentHandle surface, bool usesSharedMemory)
{
::CloseHandle(surface);
}
};

Linux version:

class ViewEventListener : public Coherent::UI::ViewListener
{
public:
virtual void OnDraw(CoherentHandle handle, bool usesSharedMemory, int width, int height)
{
auto memory = ::shmat(handle, 0, SHM_RDONLY);
if(g_Texture != GL_INVALID_VALUE)
{
glBindTexture(GL_TEXTURE_2D, g_Texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, RENDER_AREA_WIDTH, RENDER_AREA_HEIGHT, GL_BGRA, GL_UNSIGNED_BYTE, memory);
glBindTexture(GL_TEXTURE_2D, 0);
}
::shmdt(memory);
}
virtual void CreateSurface(bool, unsigned width, unsigned height, Coherent::UI::SurfaceResponse* response)
{
response->Signal(Coherent::UI::CoherentHandle(shmget(IPC_PRIVATE, width * height * 4, 0600 | IPC_CREAT | IPC_EXCL)));
}
virtual void DestroySurface(Coherent::UI::CoherentHandle surface, bool usesSharedMemory)
{
shmctl(surface, IPC_RMID, nullptr);
}
};

MacOSX version:

void CoherentViewListener::OnDraw(Coherent::UI::CoherentHandle handle, bool useSharedMem, int width, int height)
{
size_t memSize = width * height * 4;
void* memory = mmap(
nullptr, memSize, PROT_READ, MAP_SHARED, handle.Handle, 0);
m_Context->GetGLUtility()->UpdateTextureRect(
m_Context->GetTextureFromHost(), 0, 0, width, height, memory);
munmap(memory, memSize);
}
void CoherentViewListener::CreateSurface(bool sharedMem, unsigned width, unsigned height, Coherent::UI::SurfaceResponse* response)
{
Coherent::UI::CoherentHandle result(0);
std::ostringstream stream;
stream << "/tmp/buffer_" << rand() << '_' << rand();
std::string name = stream.str();
int fd = open(name.c_str(), O_RDWR | O_CREAT, 0666);
if (fd == -1)
{
std::cerr << "Could create file " << name << " error " << errno;
}
ftruncate(fd, width * height * 4);
result = Coherent::UI::CoherentHandle(fd, name.c_str());
response->Signal(result);
}
void CoherentViewListener::DestroySurface(Coherent::UI::CoherentHandle buffer, bool useSharedMem)
{
close(buffer.Handle);
unlink(buffer.SharedMemoryName);
}

For example how the shared memory is used take a look at the HelloHTMLOGL (respectively HelloHTMLOGL_Linux) sample.

DirectX

For DirectX based rendering the recommended method is using shared textures, because of the better performance. Using shared textures is available in DirectX 10 and DirectX 11. For DirectX 9 the device must be created as IDirect3DDevice9Ex. Unfortunately this device is not available on Windows XP and shared memory should be used there too.

OpenGL

Using Coherent UI with OpenGL based rendering can be accomplished either with DirectX OpenGL interop, with shared textures (IOSurfaces) on MacOSX or with shared memory.

DirectX OpenGL Interop

Some OpenGL implementations support interoperability with DirectX. This allows sharing textures between OpenGL and DirectX and skips taking down the textures to the system memory. The sample HelloHTMLOGL uses this interoperability when compiled with USE_DX_OGL_INTEROP defined.

No rendering

If you don't have any rendering you can still obtain the rendered view using shared memory

Pure software rendering

By default Coherent UI uses a very fast GPU renderer. However it can also use a software-only rendering path. This might be useful when GPU resources are tight and maxed out by the client application and you don't need to show any 3D HTML or CSS elements. The option is enabled per-view by settings "ForceSoftwareRendering" in the ViewInfo on creation. Pure software rendered views work only with shared memory and are incompatible with on-demand views. When rendering in software some web features will not be available:

  • 3D CSS transforms
  • WebGL

We advise to always use the hardware rendering code-path with shared textures, as it supports all features and is the overall fastest one. Cases where a pure software rendering path could be beneficial are not uncommon though. Prerequisites usually needed to decide to use the software path are:

  • For some reason you have to use shared memory - in most cases the software path will be faster as no read-back has to be done from the GPU
  • The client application has no GPU cycles to spare
  • You don't need 3D transformations in the View
  • You don't need on-demand or time control for the View

The option is strictly per-view so there is no problem having both a software and a hardware rendered View instantiated in the same view context.

Fullscreen on Windows XP

If your application needs to run in fullscreen mode on Windows XP it has to either run in a fake fullscreen mode (normal windows with the size of the screen) or use software rendering. This is necessary because having a DirectX device in fullscreen on Windows XP makes all other DirectX devices unusable.

Fullscreen and ALT+TAB on Windows

If your application needs to support ALT+TAB in fullscreen mode on Windows platform (assuming its using DirectX, not OpenGL), it should either run in fake fullscreen mode or satisfy the described bellow conditions (for true fullscreen). Steps required in order to support DirectX9Ex:

  • Client should provide the focus window and Direct3D object, used for creating her graphics device. They should be passed to InitializeCoherentUI in a RenderingParameters struct.
  • Immediately reset her graphics device after initializing ViewContext.

Steps required for DirectX9:

  • Client should create her device in fake fullscreen mode.
  • Pass the Direct3D object and focus window, used for creating it.
  • Immediately after initializing needed ViewContext-s, reset her device with true fullscreen mode enabled.

This is due to the following restriction concerning applications with multiple graphics devices: when a device is created in true fullscreen mode, the Direct3DObject that created it is placed in exclusive mode and all other devices are placed in the lost state. This holds true unless they satisfy the following conditions:

  • They are created by the same D3DObject as the one that is already in fullscreen
  • Share the same focus window as the latter
  • Represent different adapter than any other device that is in fullscreen mode