Integration into an Existing Metal Application

Introduction

This tutorial creates a standalone window, using the basic operating system features: a MetalKit view on OSX.

We create and setup a HOOPS Luminate window so that it can co-exist and render into the same window as our application, using the existing window application context. This illustrates how HOOPS Luminate can be used to enrich an existing application graphics or how it can be used to replace parts of a graphic pipeline.

This capability is very important as it allows a smooth integration of HOOPS Luminate into an existing Metal application, without breaking any legacy graphics layer, that could be difficult to rewrite from scratch otherwise.

Adding the HOOPS Luminate Window

The tutorial is divided into 4 steps:

  1. Creation of the OS window

  2. Creation of the MetalKit view

  3. Creation of the HOOPS Luminate window to work in this environment (with a simple scene setup)

  4. Rendering of both the original Metal code AND HOOPS Luminate for the same frame

The HOOPS Luminate window uses an auxiliary RED::WindowRenderInfo to source the application Metal parameters: the shared MTLCommandQueue instance.

RED_RC rc;
RED::Object* red_window = NULL;

RED::Object* resmgr = RFK::TutorialApplication::GetResourceManager();
RED::IResourceManager* iresmgr = resmgr->As< RED::IResourceManager >();

RED::WindowRenderInfo winfo;
winfo.SetBufferSwapping( false );
winfo.SetHostingMetalCommandQueue( view.commandQueue );

const int red_window_width  = view.drawableSize.width;
const int red_window_height = view.drawableSize.height;

red_window = RED::Factory::CreateREDWindow( *resmgr, (void*)view, red_window_width, red_window_height, &winfo, rc );

if( !red_window )
    RC_TEST( RED_FAIL );

RC_TEST( rc );

Note that we have chosen to disable the window buffer swapping so that this is the source application that’ll control the window buffer swapping. We also disable any buffer clear from the HOOPS Luminate window’s default VRL, so that we can draw the HOOPS Luminate frame after the application’s own Metal drawing.

Then, we setup a default scene and here goes the rendering code:

// Draw additional Metal content here

if (hasBeenResized)
{
    RC_ERROR( iwindow->Resize(drawableSize.width, drawableSize.height, iresmgr->GetState()) );
}

RC_ERROR( iresmgr->EndState() );

RC_ERROR( iwindow->SetHostingMetalRenderTarget( rendertarget, self.depthStencilTexture ) );

RC_ERROR( iwindow->FrameDrawing() );

if (self.useCopyAndFlipMetalTexture)
{
    RC_ERROR( iwindow->CopyAndFlipMetalTexture( rendertarget, self.currentDrawable.texture ) );
}
else
{
    // In this case : rendertarget == currentDrawable.texture,
    // content has been drawn to 'currentDrawable.texture', will appear upside-down
}

iresmgr->BeginState();

Before drawing HOOPS Luminate content, we provide the destination rendertarget textures (color and depth+stencil) using RED::IWindow::SetHostingMetalRenderTarget method.

Note

Because of the difference of coordinate systems, content drawn by HOOPS Luminate will appear upside down compared to pure Metal drawn content. We can either draw in an intermediate texture and use RED::IWindow::CopyAndFlipMetalTexture to restore a copy into a final texture, or adapt the Metal drawing to use an MTLViewport with a negative height.

As a result, we have the mix of the application Metal code and HOOPS Luminate scene rendered both in the same color and depth buffers.