title: How to render to a MTLTexture using CoreGraphics
body: <p>How can I render the content of a CoreGraphics context into a MTLTexture with full color and transparency? (RGBA or BGRA)</p>
  
  <p>I am planning to use that MTLTexture for a material in a cross platform (macos/iOS) SceneKit project.</p>
  
  <p>This example uses grayscale only:</p>
  
  <p><a href="https://medium.com/@s1ddok/combine-the-power-of-coregraphics-and-metal-by-sharing-resource-memory-eabb4c1be615" rel="nofollow noreferrer">https://medium.com/@s1ddok/combine-the-power-of-coregraphics-and-metal-by-sharing-resource-memory-eabb4c1be615</a></p>
  
  <p>In the image the black background should be transparent. The right side (RGB) looks like misaligned. The texture only shows on iOS, doesn't work on macOS.</p>
  
  <p><a href="https://i.stack.imgur.com/hBNeg.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/hBNeg.png" alt="The black backround should be transparent"></a></p>
  
  <p>Update (What I attempted):</p>
  
  <pre><code>//        First, we will need to know a RAM page size from the system, for this reason we will use getpagesize() function. Now we have to calculate amount of aligned bytes per row and the total allocation size. To allocate aligned memory we will use posix_memalign(_,_,_) system function.
  
  
          let width = Int(256)
          let height = Int(256)
  
  
          #if os(macOS)
              let pixelRowAlignment = Int(1024)
              let bytesPerRow = alignUp(size: width, align: pixelRowAlignment)
          #else
              let pixelRowAlignment =
                  sceneRenderer.device?.minimumTextureBufferAlignment(for: .rgba8Unorm)
              let bytesPerRow = alignUp(size: width, align: pixelRowAlignment!) * 4
          #endif
  
  
  
  
          let pagesize = Int(getpagesize())
          let allocationSize = alignUp(size: bytesPerRow * height, align: pagesize)
  
          //Allocate the memory pointed by data:
          var data: UnsafeMutableRawPointer? = nil
          let result = posix_memalign(&amp;data, pagesize, allocationSize)
          if result != noErr {
              fatalError("Error during memory allocation")
          }
  
  
  
  //        Now we are ready to create a CGContext from this memory, you probably have done this many times, so this should be pretty straightforward:
  
          //Gray (ok!)
          let context = CGContext(data: data,
                                  width: width,
                                  height: height,
                                  bitsPerComponent: 8,
                                  bytesPerRow: bytesPerRow,
                                  space: CGColorSpaceCreateDeviceGray(),
                                  bitmapInfo: CGImageAlphaInfo.alphaOnly.rawValue)!
  
          //RGB (Weird)
  
  //        let context = CGContext(data: data,
  //                                width: width,
  //                                height: height,
  //                                bitsPerComponent: 8,
  //                                bytesPerRow: bytesPerRow,
  //                                space: CGColorSpaceCreateDeviceRGB(),
  //                                bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
  
  
          context.scaleBy(x: 1.0, y: -1.0)
          context.translateBy(x: 0, y: -CGFloat(context.height))
          context.setLineJoin(.round)
          context.setLineWidth(10)
  
          context.setLineCap(.round)
          context.setLineJoin(.round)
  
          context.setStrokeColor(SCNColor(rgb: 0xFFFF00).cgColor)
  
  
  
          context.clear(CGRect(x: 0, y: 0, width: width, height: height))
          context.setFillColor(SCNColor.clear.cgColor)
          context.fill(CGRect(x: 0, y: 0, width: width, height: height))
  
          context.beginPath()
          context.move(to: CGPoint(x: 0, y: 0))
          context.addLine(to: CGPoint(x: 150, y: 150))
          context.strokePath()
  
  
  //        Using the exact same memory we can now create a no-copy MTLBuffer, making it responsible for deallocating the memory. However you can of course manage this on your own by passing nil to the last argument.
  
          #if os(macOS)
  
  
          let buffer = sceneRenderer.device?
              .makeBuffer(bytesNoCopy: context.data!,
                          length: allocationSize,
                          options: .storageModeManaged,
                          deallocator: { pointer, length in free(data) })!
  
          #else
          let buffer = sceneRenderer.device?
              .makeBuffer(bytesNoCopy: context.data!,
                          length: allocationSize,
                          options: .storageModeShared,
                          deallocator: { pointer, length in free(data) })!
          #endif
  
  
  
  //        Now, the final step: we are creating a texture from this buffer. Storage mode of the texture must be the same with the buffer.
  
  
          let textureDescriptor = MTLTextureDescriptor()
          textureDescriptor.pixelFormat = .r8Unorm
          textureDescriptor.width = context.width
          textureDescriptor.height = context.height
          textureDescriptor.storageMode = (buffer?.storageMode)!
          // we are only going to read from this texture on GPU side
          textureDescriptor.usage = .shaderRead
  
          let texture : MTLTexture = (buffer?.makeTexture(descriptor: textureDescriptor,
                                                          offset: 0,
                                                          bytesPerRow: context.bytesPerRow))!
  
  
  
  
  //        You should take into account the memory synchronization that happens between CPU and GPU. You should not create a situation where this buffer can be simultaneously accessed by CoreGraphics and Metal, especially on macOS.
  
  
  
          let plane = SCNPlane(width: 1.0 , height: 1.0)
          let material = SCNMaterial()
          material.lightingModel = SCNMaterial.LightingModel.constant
          material.isDoubleSided = false
  
  
                              //Assign the previous MTLTexture to the maaterial
  
          material.diffuse.contents = texture
  
  
  
          material.blendMode = .alpha
  
          plane.firstMaterial = material
  
          let node = SCNNode(geometry: plane)
          node.simdPosition = float3(0,0,0)
  
          scene.rootNode.addChildNode(node)
  </code></pre>