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 REDsdk 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 REDsdk 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 REDsdk into an existing Metal application, without breaking any legacy graphics layer, that could be difficult to rewrite from scratch otherwise.

Adding the REDsdk window

The tutorial is divided into 4 steps:

  1. Creation of the OS window.
  2. Creation of the MetalKit view.
  3. Creation of the REDsdk window to work in this environment (with a simple scene setup).
  4. Rendering of both the original Metal code AND REDsdk for the same frame.

The REDsdk 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 REDsdk window's default VRL, so that we can draw the REDsdk 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 REDsdk 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 REDsdk 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 REDsdk scene rendered both in the same color and depth buffers.