CATextLayer not rendering properly on SCNNode












0















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:



correct



Ends up missing the text "N":
incorrect



The layer itself is being rendered, as I can confirm by changing the background color of the text layer:



enter image description here



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?










share|improve this question



























    0















    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:



    correct



    Ends up missing the text "N":
    incorrect



    The layer itself is being rendered, as I can confirm by changing the background color of the text layer:



    enter image description here



    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?










    share|improve this question

























      0












      0








      0








      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:



      correct



      Ends up missing the text "N":
      incorrect



      The layer itself is being rendered, as I can confirm by changing the background color of the text layer:



      enter image description here



      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?










      share|improve this question














      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:



      correct



      Ends up missing the text "N":
      incorrect



      The layer itself is being rendered, as I can confirm by changing the background color of the text layer:



      enter image description here



      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?







      ios core-animation scenekit catextlayer scnscene






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 21 '18 at 1:08









      JsdodgersJsdodgers

      4,96621635




      4,96621635
























          2 Answers
          2






          active

          oldest

          votes


















          1














          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.



           scene.rootNode.addChildNode(node)
          node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer

          let renderer = SCNRenderer(
          device: MTLCreateSystemDefaultDevice(),
          options: nil)

          ----renderer.prepare([node]) { (success) in // this line is useful
          if (success){
          let renderTime = CACurrentMediaTime() + 1
          let size = CGSize(width: 600, height: 600)
          let image = renderer.snapshot(
          atTime: renderTime,
          with: size,
          antialiasingMode: .multisampling4X)
          complete(image)
          }
          }


          You may add other nodes to the array if you think it takes too long to load.






          share|improve this answer
























          • Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

            – Jsdodgers
            Nov 24 '18 at 4:57





















          0














          I've had success making PNG snapshots by just invoking SCNTransaction.flush() before the invoking sceneView.snapshot().






          share|improve this answer
























          • This just isn't working for me, unfortunately.

            – Jsdodgers
            Dec 6 '18 at 3:44











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403913%2fcatextlayer-not-rendering-properly-on-scnnode%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          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.



           scene.rootNode.addChildNode(node)
          node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer

          let renderer = SCNRenderer(
          device: MTLCreateSystemDefaultDevice(),
          options: nil)

          ----renderer.prepare([node]) { (success) in // this line is useful
          if (success){
          let renderTime = CACurrentMediaTime() + 1
          let size = CGSize(width: 600, height: 600)
          let image = renderer.snapshot(
          atTime: renderTime,
          with: size,
          antialiasingMode: .multisampling4X)
          complete(image)
          }
          }


          You may add other nodes to the array if you think it takes too long to load.






          share|improve this answer
























          • Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

            – Jsdodgers
            Nov 24 '18 at 4:57


















          1














          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.



           scene.rootNode.addChildNode(node)
          node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer

          let renderer = SCNRenderer(
          device: MTLCreateSystemDefaultDevice(),
          options: nil)

          ----renderer.prepare([node]) { (success) in // this line is useful
          if (success){
          let renderTime = CACurrentMediaTime() + 1
          let size = CGSize(width: 600, height: 600)
          let image = renderer.snapshot(
          atTime: renderTime,
          with: size,
          antialiasingMode: .multisampling4X)
          complete(image)
          }
          }


          You may add other nodes to the array if you think it takes too long to load.






          share|improve this answer
























          • Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

            – Jsdodgers
            Nov 24 '18 at 4:57
















          1












          1








          1







          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.



           scene.rootNode.addChildNode(node)
          node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer

          let renderer = SCNRenderer(
          device: MTLCreateSystemDefaultDevice(),
          options: nil)

          ----renderer.prepare([node]) { (success) in // this line is useful
          if (success){
          let renderTime = CACurrentMediaTime() + 1
          let size = CGSize(width: 600, height: 600)
          let image = renderer.snapshot(
          atTime: renderTime,
          with: size,
          antialiasingMode: .multisampling4X)
          complete(image)
          }
          }


          You may add other nodes to the array if you think it takes too long to load.






          share|improve this answer













          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.



           scene.rootNode.addChildNode(node)
          node.geometry?.firstMaterial?.diffuse.contents = self.createLayer() // CALayer

          let renderer = SCNRenderer(
          device: MTLCreateSystemDefaultDevice(),
          options: nil)

          ----renderer.prepare([node]) { (success) in // this line is useful
          if (success){
          let renderTime = CACurrentMediaTime() + 1
          let size = CGSize(width: 600, height: 600)
          let image = renderer.snapshot(
          atTime: renderTime,
          with: size,
          antialiasingMode: .multisampling4X)
          complete(image)
          }
          }


          You may add other nodes to the array if you think it takes too long to load.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 21 '18 at 2:15









          E.ComsE.Coms

          2,5152414




          2,5152414













          • Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

            – Jsdodgers
            Nov 24 '18 at 4:57





















          • Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

            – Jsdodgers
            Nov 24 '18 at 4:57



















          Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

          – Jsdodgers
          Nov 24 '18 at 4:57







          Excellent! This call isn't synchronous as I was hoping, but this is almost exactly what I was looking for! If there is a synchronous version, that would be even better, but this feels much safer than just dispatching and hoping everything works.

          – Jsdodgers
          Nov 24 '18 at 4:57















          0














          I've had success making PNG snapshots by just invoking SCNTransaction.flush() before the invoking sceneView.snapshot().






          share|improve this answer
























          • This just isn't working for me, unfortunately.

            – Jsdodgers
            Dec 6 '18 at 3:44
















          0














          I've had success making PNG snapshots by just invoking SCNTransaction.flush() before the invoking sceneView.snapshot().






          share|improve this answer
























          • This just isn't working for me, unfortunately.

            – Jsdodgers
            Dec 6 '18 at 3:44














          0












          0








          0







          I've had success making PNG snapshots by just invoking SCNTransaction.flush() before the invoking sceneView.snapshot().






          share|improve this answer













          I've had success making PNG snapshots by just invoking SCNTransaction.flush() before the invoking sceneView.snapshot().







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Dec 5 '18 at 22:36









          LenKLenK

          1,34811217




          1,34811217













          • This just isn't working for me, unfortunately.

            – Jsdodgers
            Dec 6 '18 at 3:44



















          • This just isn't working for me, unfortunately.

            – Jsdodgers
            Dec 6 '18 at 3:44

















          This just isn't working for me, unfortunately.

          – Jsdodgers
          Dec 6 '18 at 3:44





          This just isn't working for me, unfortunately.

          – Jsdodgers
          Dec 6 '18 at 3:44


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403913%2fcatextlayer-not-rendering-properly-on-scnnode%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          Xamarin.form Move up view when keyboard appear

          Post-Redirect-Get with Spring WebFlux and Thymeleaf

          Anylogic : not able to use stopDelay()