You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用LiDAR相机采集深度的Swift示例代码GPU超时崩溃求助

问题描述

我在苹果开发者官网下载了《使用LiDAR相机采集深度》的Swift示例项目,在iPhone 14 Pro上运行时,应用会间歇性崩溃,抛出错误:

‘Execution of the command buffer was aborted due to an error during execution. Caused GPU Timeout Error (00000002:kIOGPUCommandBufferCallbackErrorTimeout)’

崩溃时间不定,有时几秒内就崩溃,有时需十几分钟甚至更久。

我尝试了多种方法排查,也通过Feedback Assistant向苹果提交了反馈,但始终无法通过Xcode定位崩溃代码行。经过多小时排查,终于发现错误出在MetalTextureViewColor.swift文件的draw方法中,具体是encoder.endEncoding()这一行,报错信息为:

‘MTLCommandBuffer execution failed. The commands associated with an encoder caused an error’

完整的draw函数代码如下:

override func draw(in view: MTKView) {
        guard parent.capturedData.colorY != nil && parent.capturedData.colorCbCr != nil else {
            print("There's no content to display.")
            return
        }
        guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else { return }
        guard let passDescriptor = view.currentRenderPassDescriptor else { return }
        guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return }
        // Vertex and Texture coordinates data (x,y,u,v) * 4 ordered for triangle strip.
        let vertexData: [Float] = [-1, -1, 1, 1,
                                    1, -1, 1, 0,
                                   -1,  1, 0, 1,
                                    1,  1, 0, 0]
        encoder.setVertexBytes(vertexData, length: vertexData.count * MemoryLayout<Float>.stride, index: 0)
        encoder.setFragmentTexture(parent.capturedData.colorY!, index: 0)
        encoder.setFragmentTexture(parent.capturedData.colorCbCr!, index: 1)
        encoder.setDepthStencilState(depthState)
        encoder.setRenderPipelineState(pipelineState)
        encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
        
        // error happening on the line below
        encoder.endEncoding()


        commandBuffer.present(view.currentDrawable!)
        commandBuffer.commit()
        //commandBuffer.waitUntilCompleted()
    }

请问是否有办法捕获该错误并忽略,让应用继续运行?


解决方案与优化建议

1. 捕获Command Buffer执行错误

Metal的MTLCommandBuffer支持添加完成回调,可在回调中捕获执行阶段的错误,避免应用崩溃。同时对encoder.endEncoding()做容错处理:

override func draw(in view: MTKView) {
        guard parent.capturedData.colorY != nil && parent.capturedData.colorCbCr != nil else {
            print("There's no content to display.")
            return
        }
        guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else { return }
        
        // 添加命令缓冲区完成回调,捕获执行错误
        commandBuffer.addCompletedHandler { buffer in
            if let error = buffer.error {
                print("Command Buffer执行错误: \(error.localizedDescription)")
                // 可在此添加错误统计、重试逻辑,或直接忽略
                return
            }
        }
        
        guard let passDescriptor = view.currentRenderPassDescriptor else { return }
        guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return }
        
        let vertexData: [Float] = [-1, -1, 1, 1,
                                    1, -1, 1, 0,
                                   -1,  1, 0, 1,
                                    1,  1, 0, 0]
        encoder.setVertexBytes(vertexData, length: vertexData.count * MemoryLayout<Float>.stride, index: 0)
        encoder.setFragmentTexture(parent.capturedData.colorY!, index: 0)
        encoder.setFragmentTexture(parent.capturedData.colorCbCr!, index: 1)
        encoder.setDepthStencilState(depthState)
        encoder.setRenderPipelineState(pipelineState)
        encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
        
        // 尝试安全结束编码
        do {
            try encoder.endEncoding()
        } catch {
            print("Encoder结束编码错误: \(error.localizedDescription)")
            // 即使出错也尝试结束编码,避免资源泄漏
            encoder.endEncoding()
            return
        }

        commandBuffer.present(view.currentDrawable!)
        commandBuffer.commit()
    }

2. 根源优化:减少GPU超时概率

虽然捕获错误能避免崩溃,但从根源优化可彻底解决问题:

  • 确保colorY和colorCbCr纹理在渲染周期内未被释放或异步修改,可添加纹理引用计数检查
  • 将vertexData改为类级静态常量,避免每次draw都重新创建数组
  • 关闭Xcode的Metal Validation等调试选项,这些会增加GPU负载
  • 确保pipelineState在初始化时预创建,不要在draw方法内重复初始化

3. 全局容错兜底(不推荐)

如果想做最极端的容错,可将draw逻辑封装到单独方法并添加全局异常捕获,但可能导致未知渲染状态:

override func draw(in view: MTKView) {
    do {
        try performRender(in: view)
    } catch {
        print("渲染过程出错: \(error)")
    }
}

private func performRender(in view: MTKView) throws {
    // 原draw方法的完整逻辑
    guard parent.capturedData.colorY != nil && parent.capturedData.colorCbCr != nil else {
        print("There's no content to display.")
        return
    }
    guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else { return }
    guard let passDescriptor = view.currentRenderPassDescriptor else { return }
    guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor) else { return }
    
    let vertexData: [Float] = [-1, -1, 1, 1,
                                1, -1, 1, 0,
                               -1,  1, 0, 1,
                                1,  1, 0, 0]
    encoder.setVertexBytes(vertexData, length: vertexData.count * MemoryLayout<Float>.stride, index: 0)
    encoder.setFragmentTexture(parent.capturedData.colorY!, index: 0)
    encoder.setFragmentTexture(parent.capturedData.colorCbCr!, index: 1)
    encoder.setDepthStencilState(depthState)
    encoder.setRenderPipelineState(pipelineState)
    encoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
    
    encoder.endEncoding()

    commandBuffer.present(view.currentDrawable!)
    commandBuffer.commit()
}

内容的提问来源于stack exchange,提问作者N D

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 10:40:05