I have a hierarchy of CALayers that I am setting as the diffuse property of my SCNNode's material. I taking snapshots of the current state of the scene (that only has the one node) to save as a PNG to a file using this code:
scene.rootNode.addChildNode(node)
node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer
// Set transform of node.
let renderTime = CACurrentMediaTime() + 1
let size = CGSize(width: 600, height: 600)
let renderer = SCNRenderer(
device: MTLCreateSystemDefaultDevice(),
options: nil)
let image = self.renderer.snapshot(
atTime: renderTime,
with: size,
antialiasingMode: .multisampling4X)
return image
Often this works, rendering the node as expected, but about 25-50% of the time, any CATextLayer I have as a sublayer of the layer returned in self.createLayer()
does not render the text. All other layers seem to be rendered just fine every time.
For example, an image that is supposed to look as such:
The layer itself is being rendered, as I can confirm by changing the background color of the text layer:
This seems to only be an issue that occurs immediately after creating the layers. If I dispatch the rendering code asynchronously, even without adding any delay, everything renders as expected:
scene.rootNode.addChildNode(node)
node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer
// Set transform of node.
let renderTime = CACurrentMediaTime() + 1
let size = CGSize(width: 600, height: 600)
DispatchQueue.main.async {
let image = self.renderer.snapshot(
atTime: renderTime,
with: size,
antialiasingMode: .multisampling4X)
completion(image)
}
The above workaround seems hacky and I don't trust it to be reliable. Also, I'd rather not force my callsites to call the method in an asynchronous manner.
Is there a property or method in SceneKit or CoreAnimation that I'm missing that I can use to make sure the layer is completely rendered before trying to render it to an image?
I can understand it is not reliable using DispatchMain as you are not sure the node has been rendered well. So there is a function tell you if the rendering has complete or not. Call snapshot inside that completion handle.
You may add other nodes to the array if you think it takes too long to load.