Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SKRenderer -- the mystery class

Here I was getting all hyped about pulling off-screen Metal textures out of SpriteKit after watching the session 609 video from WWDC2017.

This was over a year ago!

And yet there are absolutely no overview docs on SKRenderer and there is no sample code either. https://developer.apple.com/documentation/spritekit/skrenderer

I find this very odd indeed. Does anyone here have any insight on this class, its docs or sample code?

BTW, the same goes for SKTransformNode.

like image 698
zzyzy Avatar asked Jun 19 '18 18:06

zzyzy


2 Answers

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.
like image 169
warrenm Avatar answered Sep 30 '22 07:09

warrenm


swift

   func render(renderCommandEncoder: MTLRenderCommandEncoder){
    skScene.size = Engine.previewViewSize



        currentTime = 0//allows looping skaction


    //sprite kit render
    skrender.update(atTime: currentTime )

    let viewport = CGRect(x: 0, y: 0, width: (Engine.previewViewSize.width), height: (Engine.previewViewSize.height))

    skScene.isPaused = false

    skrender.scene = skScene

    skrender.render(withViewport: viewport, renderCommandEncoder: renderCommandEncoder, renderPassDescriptor: Engine.currentRenderPassDescriptor, commandQueue: Engine.CommandQueue)


}
like image 34
masaldana2 Avatar answered Oct 04 '22 07:10

masaldana2