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

Metal程序执行崩溃(EXC_BAD_ACCESS)问题排查求助

Metal程序EXC_BAD_ACCESS崩溃排查(drawIndexedPrimitives调用处)

编写的Metal程序执行时触发EXC_BAD_ACCESS错误(code=1, address=0x220),崩溃位置在调用renderEncoder.drawIndexedPrimitives方法处,重新编写代码仍无法定位问题,请求协助排查。

相关代码

import PlaygroundSupport
import MetalKit

guard let device = MTLCreateSystemDefaultDevice() else {
    fatalError("GPU is not supported")
}

let frame = CGRect(x: 0, y: 0, width: 600, height: 600)
let view = MTKView(frame: frame, device:device)
view.clearColor = MTLClearColor(red: 1, green: 1, blue: 0.8, alpha: 1)

//1
let allocator = MTKMeshBufferAllocator(device: device)
//2
let mdlMesh = MDLMesh(sphereWithExtent: [0.75, 0.75, 0.75], segments: [100, 100], inwardNormals: false, geometryType: .triangles, allocator: allocator)
//3
let mesh = try MTKMesh(mesh: mdlMesh, device: device)

guard let commandQueue = device.makeCommandQueue() else {
    fatalError("Could not create a command queue")
}

//shader
let shader = """
#include <metal_stdlib>

using namespace metal;

struct VertexIn {
    float4 position [[attribute(0)]];
};

vertex float4 vertex_main(const VertexIn vertex_in [[stage_in]])
{
    return vertex_in.position;
}

fragment float4 fragment_main() {
    return float4(1, 0, 0, 1);
}
"""

let library = try device.makeLibrary(source: shader, options: nil)
let vertexFunction = library.makeFunction(name: "vertex_main")
let fragmentFunction = library.makeFunction(name: "fragment_main")

//Pipeline

let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction

pipelineDescriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mesh.vertexDescriptor)

let pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)

// 1
guard let commandBuffer = commandQueue.makeCommandBuffer(),
      //2
      let renderPassDescriptor = view.currentRenderPassDescriptor,
      //3
      let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
else { fatalError() }

guard let submesh = mesh.submeshes.first else {
    fatalError()
}

//Here is where crashed.
renderEncoder.drawIndexedPrimitives(type: .triangle,
                  indexCount: submesh.indexCount,
                  indexType: submesh.indexType,
                  indexBuffer: submesh.indexBuffer.buffer,
                  indexBufferOffset: 0)

//1.u tell the render encoder that there are no more draw calls and end the render pass.
renderEncoder.endEncoding()

//2 u get the drawable from the MTKView.The MTKView is backed by a Core Animation CAMetalLayer and the layer owns a drawable texture which Metal can read and write to.
guard let drawable = view.currentDrawable else {
    fatalError()
}
//3.Ask the command Buffer to present the MTKVIew's drawable and commit to the GPU
commandBuffer.present(drawable)
commandBuffer.commit()

PlaygroundPage.current.liveView = view

错误提示

error: Execution was interrupted, reason: EXC_BAD_ACCESS (code=1, address=0x220).
The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.


问题原因及修复方案

1. 未绑定渲染管线状态

渲染编码器在执行绘制命令前,必须绑定已创建的渲染管线状态,否则GPU无法找到对应的着色器程序和渲染配置,导致内存访问错误。

修复代码:在调用drawIndexedPrimitives前添加:

renderEncoder.setRenderPipelineState(pipelineState)

2. 未绑定顶点缓冲区

顶点着色器需要读取顶点数据,但代码中没有将顶点缓冲区绑定到渲染编码器,GPU尝试访问不存在的顶点数据时触发崩溃。

修复代码:在绑定管线状态后添加:

renderEncoder.setVertexBuffer(mesh.vertexBuffers[0].buffer, offset: 0, index: 0)

3. Playground渲染逻辑时机错误

手动直接创建并提交命令缓冲区的方式,和Playground中MTKView的生命周期不兼容,应该通过MTKView的代理方法来处理渲染循环。

修正后的完整代码:

import PlaygroundSupport
import MetalKit

class Renderer: NSObject, MTKViewDelegate {
    let device: MTLDevice
    let commandQueue: MTLCommandQueue
    let mesh: MTKMesh
    let pipelineState: MTLRenderPipelineState
    
    init(device: MTLDevice) throws {
        self.device = device
        self.commandQueue = device.makeCommandQueue()!
        
        // 创建网格
        let allocator = MTKMeshBufferAllocator(device: device)
        let mdlMesh = MDLMesh(sphereWithExtent: [0.75, 0.75, 0.75], segments: [100, 100], inwardNormals: false, geometryType: .triangles, allocator: allocator)
        self.mesh = try MTKMesh(mesh: mdlMesh, device: device)
        
        // 创建着色器库
        let shader = """
        #include <metal_stdlib>
        
        using namespace metal;
        
        struct VertexIn {
            float4 position [[attribute(0)]];
        };
        
        vertex float4 vertex_main(const VertexIn vertex_in [[stage_in]])
        {
            return vertex_in.position;
        }
        
        fragment float4 fragment_main() {
            return float4(1, 0, 0, 1);
        }
        """
        let library = try device.makeLibrary(source: shader, options: nil)
        let vertexFunction = library.makeFunction(name: "vertex_main")!
        let fragmentFunction = library.makeFunction(name: "fragment_main")!
        
        // 创建管线状态
        let pipelineDescriptor = MTLRenderPipelineDescriptor()
        pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
        pipelineDescriptor.vertexFunction = vertexFunction
        pipelineDescriptor.fragmentFunction = fragmentFunction
        pipelineDescriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mesh.vertexDescriptor)
        self.pipelineState = try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
        
        super.init()
    }
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}
    
    func draw(in view: MTKView) {
        guard let commandBuffer = commandQueue.makeCommandBuffer(),
              let renderPassDescriptor = view.currentRenderPassDescriptor,
              let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor),
              let submesh = mesh.submeshes.first,
              let drawable = view.currentDrawable
        else { return }
        
        // 绑定管线状态和缓冲区
        renderEncoder.setRenderPipelineState(pipelineState)
        renderEncoder.setVertexBuffer(mesh.vertexBuffers[0].buffer, offset: 0, index: 0)
        
        // 执行绘制
        renderEncoder.drawIndexedPrimitives(type: .triangle,
                                            indexCount: submesh.indexCount,
                                            indexType: submesh.indexType,
                                            indexBuffer: submesh.indexBuffer.buffer,
                                            indexBufferOffset: 0)
        
        renderEncoder.endEncoding()
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
}

// 设置Playground视图
guard let device = MTLCreateSystemDefaultDevice() else {
    fatalError("GPU is not supported")
}

let frame = CGRect(x: 0, y: 0, width: 600, height: 600)
let view = MTKView(frame: frame, device: device)
view.clearColor = MTLClearColor(red: 1, green: 1, blue: 0.8, alpha: 1)
view.delegate = try Renderer(device: device)

PlaygroundPage.current.liveView = view

内容的提问来源于stack exchange,提问作者2_n1e

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 04:25:29