使用自定义CIFilter结合Metal时的高内存占用问题排查
内存占用过高问题排查与修复方案
1. 像素格式选择导致内存翻倍
你的缩放纹理使用了MTLPixelFormat.rgba16Float格式,每个像素占用8字节(RGBA各16位浮点数),而原始输入是kCVPixelFormatType_32BGRA(4字节/像素)。如果特效不需要高精度浮点运算,换成MTLPixelFormat.rgba8Unorm可直接将中间纹理的内存占用减半。
修改示例:
desc.pixelFormat = MTLPixelFormat.rgba8Unorm; // 替换rgba16Float
2. 未使用CVPixelBufferPool复用像素缓冲区
每次渲染都通过CVPixelBufferCreate创建新的像素缓冲区,无复用机制会导致内存碎片和重复分配的额外开销。初始化时创建CVPixelBufferPool,每次从池中获取缓冲区、用完后回收,能大幅减少内存波动和占用。
修改步骤:
- 添加实例变量:
pixelBufferPool: CVPixelBufferPool? - 在
init中初始化池:
override init() { // ... 原有代码 let poolAttrs = [ kCVPixelBufferPoolMinimumBufferCountKey: 2, // 保留2个缓冲区复用 kCVPixelBufferPoolMaximumBufferCountKey: 3 ] as CFDictionary pixelBufferPool = CVPixelBufferPoolCreate(kCFAllocatorDefault, poolAttrs, attrs) // ... 原有代码 }
- 在
outputImage中替换创建逻辑:
// 替换原CVPixelBufferCreate调用 CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferPool!, &pixelBuffer) guard let pixelBuffer = pixelBuffer else { return nil }
3. CIContext未绑定Metal设备,引发额外内存拷贝
初始化CIContext时未指定Metal设备,默认会创建独立GPU上下文,导致纹理在不同设备间拷贝、额外占用内存。修改为绑定当前Metal设备:
ciContext = CIContext(mtlDevice: device!); // 替换原CIContext()初始化
4. 重复创建MPSImageLanczosScale实例
每次outputImage调用都创建MPSImageLanczosScale,可改为实例变量仅初始化一次,避免不必要的对象分配:
- 添加实例变量:
let mpsScale: MPSImageLanczosScale - 在
init中初始化:
override init() { // ... 原有代码 mpsScale = MPSImageLanczosScale(device: device!); // ... 原有代码 }
- 在
outputImage中直接使用已初始化的mpsScale
5. 移除不必要的CPU阻塞
commandBuffer!.waitUntilCompleted()会阻塞CPU等待GPU完成,虽不直接导致内存问题,但会影响性能。若后续CIKernel无需等待前序GPU操作,可移除该行,让GPU命令异步执行(需确保纹理生命周期正确)。
内容的提问来源于stack exchange,提问作者Andy3000
相关产品推荐
相关产品推荐

