Flutter - iOS 端接入 Metal 记录

223 阅读1分钟

Flutter 工程中,iOS 端接入 Metal,我使用的是 Texture 来展示对应 View。 具体调用方法是通过 MethodChannel 还是 ffi 这边不再赘述。

使用 Texture 来展示效果需要原生继承 FlutterTexture 协议并实现- (CVPixelBufferRef _Nullable)copyPixelBuffer;方法。

以下是核心代码:

import Foundation
import CoreVideo
import Metal
import Flutter

class MetalTexture: NSObject {
    var sourceImageBuf: CVMetalTexture?
    var pixelBuf: Unmanaged<CVPixelBuffer>?
    var textureCache: CVMetalTextureCache?
    
    init(width: Int, height: Int) {
        super.init()
        
        guard let defaultDevice = MTLCreateSystemDefaultDevice() else {
            fatalError("Could not create Metal Device")
        }
        
        guard let ioSurface = IOSurfaceCreate([
            kIOSurfaceWidth: width,
            kIOSurfaceHeight: height,
            kIOSurfaceBytesPerElement: 4,
            kIOSurfacePixelFormat: kCVPixelFormatType_32BGRA] as [CFString : Any] as CFDictionary) else {
            fatalError("IOSurfaceCreate error.")
        }
        
        guard CVPixelBufferCreateWithIOSurface(
            kCFAllocatorDefault,
            ioSurface,
            [kCVPixelBufferMetalCompatibilityKey: true] as CFDictionary,
            &pixelBuf) == kCVReturnSuccess else {
            fatalError("CVPixelBufferCreateWithIOSurface create CVPixelBuffer error")
        }
        
        guard CVMetalTextureCacheCreate(kCFAllocatorDefault,
                                        nil,
                                        defaultDevice,
                                        nil,
                                        &textureCache) == kCVReturnSuccess else {
            fatalError("Failed to create texture cache")
        }
        guard let textureCache = textureCache else { return }
        
        guard CVMetalTextureCacheCreateTextureFromImage(
            kCFAllocatorDefault,
            textureCache,
            pixelBuf!.takeUnretainedValue(),
            nil,
            .bgra8Unorm,
            width,
            height,
            0,
            &sourceImageBuf) == kCVReturnSuccess else {
            fatalError("CVMetalTextureCacheCreateTextureFromImage bind CVPixelBuffer to metal texture error")
        }
        if let image = sourceImageBuf {
            sharedContext.metalTexture = CVMetalTextureGetTexture(image)
        }
    }
}

extension MetalTexture: FlutterTexture {
    func copyPixelBuffer() -> Unmanaged<CVPixelBuffer>? {
        if let pixelBuf = pixelBuf?.takeUnretainedValue() {
            return Unmanaged.passRetained(pixelBuf)
        } else {
            return nil
        }
    }
}

外部使用的时候,需要将 sharedContext.metalTexture绑定到需要展示的纹理上。


以上。