Как выполнить рендеринг в текстуру MTL с использованием CoreGraphics - PullRequest
0 голосов
/ 06 апреля 2020

Как я могу отобразить содержимое контекста CoreGraphics в текстуру MTL с полным цветом и прозрачностью? (RGBA или BGRA)

Я планирую использовать эту MTLTexture для материала в кроссплатформенном (macos / iOS) проекте SceneKit.

В этом примере используется только оттенки серого:

https://medium.com/@s1ddok / объединить мощность-coregraphics-and-metal-by-share-resource-memory-eabb4c1be615

На изображении черный фон должен быть прозрачный. Правая сторона (RGB) выглядит смещенной. Текстура отображается только на iOS, не работает на macOS.

The black backround should be transparent

Обновление (что я пытался):

//        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)
...