Metal Camera开发1:读取渲染结果生成UIImage第5节:读取Metal渲染结果并生成UIImage介绍了读取屏幕(MTLTexture的数据)并由Core Graphics创建UIImage,类似于OpenGL ES,这一过程按我理解是存在多余的内存拷贝操作(GPU管理的内存块拷贝到CPU管理的内存块)。为提高性能与简化视频编码步骤,本文档介绍Metal渲染到CVPixelBuffer的实现。
文档结构:
- OpenGL ES渲染到CVPixelBuffer实现代码
- Metal渲染到CVPixelBuffer实现代码
- 参考资料
- 致谢
1. OpenGL ES渲染到CVPixelBuffer实现代码
1.1. 创建GL_TEXTURE_2D及glFramebufferTexture2D
GPUImage使用了此方式,基于我之前的代码测试可正常运行。最关键的地方是创建CVPixelBuffer需指定kCVPixelBufferIOSurfacePropertiesKey属性为空字典。基于OpenGL ES 3.0的实现代码如下所示。
CVReturn status = CVOpenGLESTextureCacheCreate(
NULL,
NULL,
context,
NULL,
&textureCache);
NSDictionary *pixelBufferAttributes = @{(__bridge NSString*)kCVPixelBufferIOSurfacePropertiesKey: @{}};
status = CVPixelBufferCreate(
NULL,
renderbufferWidth,
renderbufferHeight,
kCVPixelFormatType_32BGRA,
(__bridge CFDictionaryRef)pixelBufferAttributes,
&offlinePixelBuffer);
status = CVOpenGLESTextureCacheCreateTextureFromImage(
NULL,
textureCache,
offlinePixelBuffer,
NULL, // texture attributes
GL_TEXTURE_2D,
GL_RGBA, // OpenGL format
renderbufferWidth,
renderbufferHeight,
GL_BGRA, // native iOS format
GL_UNSIGNED_BYTE,
0,
&offlineTexture);
glFramebufferTexture2D(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
CVOpenGLESTextureGetName(offlineTexture),
0);
有趣的是,format
参数设置为GL_RGBA也可以正常创建CVOpenGLESTextureRef对象。无论format
参数设置为什么值,使用时都得通过glFramebufferTexture2D将offlineTexture绑定到帧缓冲区。
1.2. 创建GL_RENDERBUFFER及glFramebufferRenderbuffer
这里尝试了使用CVOpenGLESTextureCacheCreateTextureFromImage创建成GL_RENDERBUFFER,配合glFramebufferRenderbuffer可正常工作。创建成GL_RENDERBUFFER必须将format
参数设置为GL_BGRA,否则创建失败并报错Failed to create IOSurface image (texture)
。另外,官方给的internalFormat
为GL_RGBA8,这是OpenGL ES 1.0的宏定义,基于OpenGL ES 3.0开发时,换成GL_RGBA即可。参考代码如下。
status = CVOpenGLESTextureCacheCreateTextureFromImage(
NULL,
textureCache,
offlinePixelBuffer,
NULL, // texture attributes
GL_RENDERBUFFER,
GL_RGBA8, // OpenGL format
renderbufferWidth,
renderbufferHeight,
GL_BGRA, // native iOS format
GL_UNSIGNED_BYTE,
0,
&offlineTexture);
glFramebufferRenderbuffer(
GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,
CVOpenGLESTextureGetName(offlineTexture));
1.3. 关于GL_TEXTURE_2D与GL_RENDERBUFFER
关于应该使用GL_TEXTURE_2D还是GL_RENDERBUFFER,@红猪和我讨论了一段时间。建议直接参考官方文档Drawing to Other Rendering Destinations。
2. Metal渲染到CVPixelBuffer实现代码
参考OpenGL ES渲染到CVPixelBuffer的实现,很容易模仿出Metal版本,如下代码已通过验证。
CVMetalTextureCacheCreate(
kCFAllocatorDefault,
nil,
device!,
nil,
&textureCache)
let pixelBufferAttri = [kCVPixelBufferIOSurfacePropertiesKey as NSObject: [:] as CFDictionary] as CFDictionary
let s = CVPixelBufferCreate(
kCFAllocatorDefault,
1080,
1920,
kCVPixelFormatType_32BGRA,
pixelBufferAttri,
&offlinePixelBuffer)
let result = CVMetalTextureCacheCreateTextureFromImage(
kCFAllocatorDefault,
textureCache!,
offlinePixelBuffer!,
nil,
self.colorPixelFormat,
1080,
1920,
0,
&offlineTexture)
3. 参考资料
- Rendering to a texture with iOS 5 texture cache api. GPUImage在源码中标明了它参考此链接,目前来看,所有的实现都源自于此链接,似乎是因为WWDC Session 419主讲人没分享代码给大家,而此博主写了一份可行的示例代码,所以大家都用他的代码的。
- Capturing from the Camera using AV Foundation on iOS 5 苹果官方讲座描述了OpenGL ES渲染到CVPixelBuffer的细节。
4. 致谢
感谢@红猪和我一起讨论OpenGL ES渲染到CVPixelBuffer细节。