My SCNView is using Metal as the rendering API and I would like to know if there's a way to grab the rendered scene as a MTLTexture without having to use a separate SCNRenderer? Performance drops when I'm trying to both display the scene via the SCNView and re-rendering the scene offscreen to a MTLTexture via a SCNRenderer (I'm trying to grab the output every frame).
SCNView gives me access to the MTLDevice, MTLRenderCommandEncoder, and MTLCommandQueue that it uses, but not to the underlying MTLRenderPassDescriptor that I would need in order to get the MTLTexture (via renderPassDescriptor.colorAttachments[0].texture
)
Some alternatives I tried was trying to use SCNView.snapshot()
to get a UIImage and converting it but performance was even worse.
Updated for Swift 4:
Swift 4 doesn't support dispatch_once(), and @objc added to replacement functions. Here's the updated swizzle setup. This is tested working nicely for me.
In your AppDelegate:
Updated to add a currentSceneDrawable property to CAMetalLayer, so you can just use layer.currentSceneDrawable to access it, rather than having the extension store it externally.
** Warning: This may not be a proper method for App Store. But it's working.
Step 1: Swap the method of nextDrawable of CAMetalLayer with a new one using swizzling. Save the CAMetalDrawable for each render loop.
Step 2: Setup the swizzling in AppDelegate: didFinishLaunchingWithOptions
Step 3: Disable framebufferOnly for your's SCNView's CAMetalLayer (In order to call getBytes for MTLTexture)
Step 4: In your SCNView's delegate (SCNSceneRendererDelegate), play with the texture
Step 5 (Optional): You may need to confirm the drawable at CAMetalLayer you are getting is your target. (If more then one CAMetalLayer at the same time)