Metal Camera开发3:实现GPUImage链式叠加滤镜

基于Metal Camera开发2:手势划动切换滤镜传递多个纹理到Compute Shader的实现思路,本文档描述Metal Compute Shader实现GPUImage多级滤镜叠加效果(多通滤波)的基本编程步骤。

文档结构:

  1. Metal Compute Shader多级滤镜叠加效果(多通滤波)的基本编程步骤介绍
  2. 创建中间纹理
  3. 讨论:Xcode调试Metal的缺点
Metal Camera开发3:实现GPUImage链式叠加滤镜_第1张图片
Metal 多个滤镜叠加效果示意

1. Metal Compute Shader多级滤镜叠加效果(多通滤波)的基本编程步骤介绍

根据自己的实践,简要总结Metal Compute Shader多级滤镜叠加效果(多通滤波)的基本编程步骤如下:

  1. 创建存储滤镜处理中间结果的纹理
  2. 一次滤镜(滤波)对应一个管线状态(ComputePipelineState)、缓冲区(CommandBuffer)与编码器(ComputeCommandEncoder)。
  3. 前一级滤镜的输出 = 后一级滤镜的输入,最后输出到屏幕或编码写到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图像的消耗如下图所示。

Metal Camera开发3:实现GPUImage链式叠加滤镜_第2张图片
CommandBuffer与CommendEncoder一一对应的消耗

上述为基本实现。由于创建CommandBuffer也会消耗较多CPU时间,为了提高性能,可以创建一个CommandBuffer、多个CommandEncoder,即每次滤波对应一个CommandEncoder及其ComputePipelineState。

Metal Camera开发3:实现GPUImage链式叠加滤镜_第3张图片
单CommandBuffer多CommendEncoder的消耗

单CommandBuffer多CommendEncoder的GPU执行栈也非常直观。


Metal Camera开发3:实现GPUImage链式叠加滤镜_第4张图片
单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所示。

Metal Camera开发3:实现GPUImage链式叠加滤镜_第5张图片
图1 Metal命令队列

设置CommandBuffer和CommandEncoder的label后,比OpenGL ES的命令队列更清晰,如图2所示。

Metal Camera开发3:实现GPUImage链式叠加滤镜_第6张图片
图2 用label提高阅读性

同时,CommandBuffer和CommandEncoder的label也反应在Metal System Trace,非常方便定位性能瓶颈,如图3所示。

Metal Camera开发3:实现GPUImage链式叠加滤镜_第7张图片
图3 label方便Metal System Trace定位问题

默认情况下,没label时Metal System Trace也难以阅读,如图4所示。

Metal Camera开发3:实现GPUImage链式叠加滤镜_第8张图片
图4 无label时Metal System Trace图表难以阅读
图5 无法预览Metal的纹理

你可能感兴趣的:(Metal Camera开发3:实现GPUImage链式叠加滤镜)