Как я могу отобразить содержимое контекста CoreGraphics в текстуру MTL с полным цветом и прозрачностью? (RGBA или BGRA)
Я планирую использовать эту MTLTexture для материала в кроссплатформенном (macos / iOS) проекте SceneKit.
В этом примере используется только оттенки серого:
https://medium.com/@s1ddok / объединить мощность-coregraphics-and-metal-by-share-resource-memory-eabb4c1be615
На изображении черный фон должен быть прозрачный. Правая сторона (RGB) выглядит смещенной. Текстура отображается только на iOS, не работает на macOS.
Обновление (что я пытался):
// 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(&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)