我尝试使用Apple和自定义MPSKernal
提供的默认compute Shaders
过滤器,通过金属应用实时相机过滤器。
在计算着色器中,我使用MPSImageGaussianBlur进行了inplace编码 代码就在这里
func encode(to commandBuffer: MTLCommandBuffer, sourceTexture: MTLTexture, destinationTexture: MTLTexture, cropRect: MTLRegion = MTLRegion.init(), offset : CGPoint) {
let blur = MPSImageGaussianBlur(device: device, sigma: 0)
blur.clipRect = cropRect
blur.offset = MPSOffset(x: Int(offset.x), y: Int(offset.y), z: 0)
let threadsPerThreadgroup = MTLSizeMake(4, 4, 1)
let threadgroupsPerGrid = MTLSizeMake(sourceTexture.width / threadsPerThreadgroup.width, sourceTexture.height / threadsPerThreadgroup.height, 1)
let commandEncoder = commandBuffer.makeComputeCommandEncoder()
commandEncoder.setComputePipelineState(pipelineState!)
commandEncoder.setTexture(sourceTexture, at: 0)
commandEncoder.setTexture(destinationTexture, at: 1)
commandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)
commandEncoder.endEncoding()
autoreleasepool {
var inPlaceTexture = destinationTexture
blur.encode(commandBuffer: commandBuffer, inPlaceTexture: &inPlaceTexture, fallbackCopyAllocator: nil)
}
}
但有时,就地纹理往往会失败,最终会在屏幕上产生混蛋效果。
因此,如果有人可以在不使用就地纹理的情况下向我推荐解决方案,或者如何使用fallbackCopyAllocator
或以不同的方式使用compute shaders
,这将非常有用。
答案 0 :(得分:1)
我已经在这个领域做了足够的编码(将计算着色器应用于来自摄像头的视频流),你遇到的最常见的问题是像素缓冲区重用"问题。
您从样本缓冲区创建的金属纹理将备份一个像素缓冲区,该缓冲区由视频会话管理,并且可以重复用于后续视频帧,除非您保留对样本缓冲区的引用(保留参考金属质感是不够的。)
请随意查看https://github.com/snakajima/vs-metal上的代码,该代码将各种计算着色器应用于实时视频流。
VSContext:除了纹理参数之外,set()方法还接受可选的sampleBuffer参数,并保留对sampleBuffer的引用,直到计算着色器的计算完成(在VSRuntime:encode()方法中)。
答案 1 :(得分:0)
就地操作方法可能会命中或遗漏,这取决于基础过滤器正在做什么。如果它是用于某些参数的单次通过过滤器,那么在这些情况下,您最终将用尽所有精力。
自从添加该方法以来,MPS添加了一个基础MTLHeap,以便为您更加透明地管理内存。如果您的MPSImage不需要由CPU查看并且仅在GPU上存在很短的时间,建议您改用MPSTemporaryImage。当readCount达到0时,后备存储将通过MPS堆回收,并可供其他MPSTemporaryImages和下游使用的其他临时资源使用。同样,直到绝对必要(例如,写入纹理或调用.texture)时,实际上不会从堆中为其分配后备存储。为每个命令缓冲区分配一个单独的堆。
使用临时映像应有助于大大减少内存使用量。例如,在具有一百次遍历的Inception v3神经网络图中,堆能够将图自动减少为仅四个分配。