In a Flutter project, when integrating Metal on the iOS side, I utilize the Texture
to display the corresponding view. The specific invocation method is not elaborated here, whether through MethodChannel
or ffi
.
To achieve the display using Texture
, it's necessary for the native side to inherit the FlutterTexture
protocol and implement the method - (CVPixelBufferRef _Nullable)copyPixelBuffer;
.
Below is the essential code snippet:
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
}
}
}
When used externally, it’s necessary to bind sharedContext.metalTexture
to the texture you want to display.
That’s all.