Basic use of SKRenderer is pretty straightforward, but there are some oddities that make it somewhat quirky in practice.
First, the fundamentals. To instantiate a renderer, use the rendererWithDevice: method. This method takes a id<MTLDevice>, such as the system default device. Pardon the Objective-C; this will translate easily to Swift:
SKRenderer *renderer = [SKRenderer rendererWithDevice:mtlDevice];
To tell the renderer what to draw, we associate it with a previously-created scene:
renderer.scene = (SKScene *)scene;
If you want actions to run, you'll need to manually un-pause the scene, something that is normally done by SKView when presenting a scene:
scene.paused = NO;
To actually draw the scene, we'll need to provide a command buffer and render pass descriptor. Supposing you're using an MTKView to handle running the display link timer and manage a CAMetalLayer, you can write a delegate method like this, which updates the scene's time (and actions) via the renderer, then draws into the MTKView's drawable:
- (void)drawInMTKView:(nonnull MTKView *)view {
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor == nil) {
return;
}
[self.renderer updateAtTime:CACurrentMediaTime()];
id<MTLCommandBuffer> commandBuffer = [self.commandQueue commandBuffer];
CGRect viewport = CGRectMake(0, 0, view.drawableSize.width, view.drawableSize.height);
[self.renderer renderWithViewport:viewport
commandBuffer:commandBuffer
renderPassDescriptor:renderPassDescriptor];
// TODO: Add any additional Metal rendering here
[commandBuffer presentDrawable:view.currentDrawable];
[commandBuffer commit];
}
Remember to set the MTKView's framebufferOnly property to NO if you use this technique.
If you want to render offscreen into a texture you've created, you'll need to do a bit more manual work, but the concepts involved are the same. You can encode separate passes that render to the same texture by creating additional render pass descriptors/encoders; just remember to set the loadAction of the primary color attachment to MTLLoadActionLoad to preserve the contents of the texture across passes.
You can also use the renderWithViewport:renderCommandEncoder:renderPassDescriptor:commandQueue: to consolidate all drawing into a single pass.
Some caveats:
- As far as I can tell, the
viewport parameter is ignored.
- If you want the
SKScene to receive NSResponder actions, you'll need to manually forward them or insert the scene into the responder chain. This especially applies to key events, where the scene (or an object responsible for forwarding to it) needs to be first responder.
- None of the convenience methods for converting touch or mouse event locations will work when the scene isn't presented by an
SKView; you'll need to do some manual translation.