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.