基于Metal Camera开发2:手势划动切换滤镜传递多个纹理到Compute Shader的实现思路,本文档描述Metal Compute Shader实现GPUImage多级滤镜叠加效果(多通滤波)的基本编程步骤。
文档结构:
- Metal Compute Shader多级滤镜叠加效果(多通滤波)的基本编程步骤介绍
- 创建中间纹理
- 讨论:Xcode调试Metal的缺点
1. Metal Compute Shader多级滤镜叠加效果(多通滤波)的基本编程步骤介绍
根据自己的实践,简要总结Metal Compute Shader多级滤镜叠加效果(多通滤波)的基本编程步骤如下:
- 创建存储滤镜处理中间结果的纹理
- 一次滤镜(滤波)对应一个管线状态(ComputePipelineState)、缓冲区(CommandBuffer)与编码器(ComputeCommandEncoder)。
- 前一级滤镜的输出 = 后一级滤镜的输入,最后输出到屏幕或编码写到MP4等文件
参考代码:
guard let commandBuffer_gamma = commandQueue?.makeCommandBuffer() else {
return
}
commandBuffer_gamma.label = "gamma correction"
let encoder_gamma = commandBuffer_gamma.makeComputeCommandEncoder()
encoder_gamma.label = "gamma correction"
encoder_gamma.setComputePipelineState(gammaFilter!)
encoder_gamma.setTexture(outputTexture, at: 0)
encoder_gamma.setTexture(outputTexture2, at: 1)
encoder_gamma.dispatchThreadgroups(threadgroups, threadsPerThreadgroup: threads)
encoder_gamma.endEncoding()
commandBuffer_gamma.commit()
commandBuffer_gamma.waitUntilCompleted()
guard let commandBuffer_passThrough = commandQueue?.makeCommandBuffer() else {
return
}
commandBuffer_passThrough.label = "pass through"
let encoder_passThrough = commandBuffer_passThrough.makeComputeCommandEncoder()
encoder_passThrough.label = "pass through"
encoder_passThrough.setComputePipelineState(passThroughFilter!)
encoder_passThrough.setTexture(outputTexture2, at: 0)
encoder_passThrough.setTexture(drawable.texture, at: 1)
encoder_passThrough.dispatchThreadgroups(threadgroups, threadsPerThreadgroup: threads)
encoder_passThrough.endEncoding()
commandBuffer_passThrough.present(drawable)
commandBuffer_passThrough.commit()
commandBuffer_passThrough.waitUntilCompleted()
处理1080p图像的消耗如下图所示。
上述为基本实现。由于创建CommandBuffer也会消耗较多CPU时间,为了提高性能,可以创建一个CommandBuffer、多个CommandEncoder,即每次滤波对应一个CommandEncoder及其ComputePipelineState。
单CommandBuffer多CommendEncoder的GPU执行栈也非常直观。
2. 创建中间纹理
MTLTextureDescriptor类描述了创建纹理的相关信息,设置纹理颜色格式、宽度等信息后,调用MTLDevice. makeTexture即可创建一个空白内容的纹理。参考代码如下。
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: self.colorPixelFormat, width: 1080, height: 1920, mipmapped: false)
outputTexture = device?.makeTexture(descriptor: textureDescriptor)
3. 讨论:Xcode 8.3.2调试Metal的缺点
在调试Compute Shader及Vertex与Fragment Shader配合使用的两种场景,我发现摄像头上传的BGRA纹理始终无法预览,而Xcode调试OpenGL ES却是可以正常看到摄像头上传的纹理数据,相比之下,不得不说这是个缺点。不设置CommandBuffer和CommandEncoder的label时,Metal的命令队列阅读略为困难,如图1所示。
设置CommandBuffer和CommandEncoder的label后,比OpenGL ES的命令队列更清晰,如图2所示。
同时,CommandBuffer和CommandEncoder的label也反应在Metal System Trace,非常方便定位性能瓶颈,如图3所示。
默认情况下,没label时Metal System Trace也难以阅读,如图4所示。