我有一个MTLBuffer实例变量,只能从与NSManagedObjectContext关联的串行队列中访问(读取)。
我使用MTLParallelRenderCommandEncoder将我的绘图工作分成两部分,一部分发生在主线程上,另一部分发生在NSManagedObjectContext的串行队列上。
当我访问这个MTLBuffer时,仍然会遇到线程消毒剂问题(或者金属内部崩溃)。为什么会这样?
我的代码基本上是这样的:

final class Renderer: NSObject {
    private var moc: NSManagedObjectContext
    private var geometryBuffer:MTLBuffer!

    init() {
        geometryBuffer=device.makeBuffer(length: 1024*1024, options: .storageModeShared)
    }

    func draw(with renderEncoder: MTLRenderCommandEncoder) {
        moc.perform {
            renderEncoder.setVertexBuffer(self.geometryBuffer, offset: 0, index: 0) //CRASH HERE (1)
            renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: self.geometryBuffer.length/(MemoryLayout<Float>.size*3)) //OR CRASH HERE (2)
        }
    }
}

class ViewController: MTKViewDelegate {
    func draw(in view: MTKView) {
        guard let parallelRenderCommandEncoder = commandBuffer.makeParallelRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }
        guard let layersRenderEncoder=parallelRenderCommandEncoder.makeRenderCommandEncoder() else { return }
        guard let renderEncoder = parallelRenderCommandEncoder.makeRenderCommandEncoder() else { return }

        renderer.draw(layersRenderEncoder) //This will dispatch to serial queue

        //Continue with the other renderEncoder
    }
}

启用线程清理程序后,问题将在(1)上捕获,堆栈跟踪如下:
#0  0x00007fff664f42de in __cxa_throw ()
#1  0x00007fff664e62e5 in std::__1::__throw_system_error(int, char const*) ()
#2  0x00007fff664a0acd in std::__1::mutex::lock() ()
#3  0x00007fff578cfbc2 in -[MTLToolsCommandBuffer addRetainedObject:] ()
#4  0x00007fff57930431 in -[MTLDebugRenderCommandEncoder setVertexBuffer:offset:atIndex:] ()

如果没有线程消毒剂,我会打开(2)。
除了“我的”串行队列之外,还有其他访问EXC_BAD_ACCESS (code=EXC_I386_GPFLT)的权限吗?

最佳答案

MTLParallelRenderCommandEncoder要求其endEncoding调用在所有子编码器完成后进行。
这可以通过使用DispatchGroup来实现,如下面更新的代码所示(还添加了endEncoding命令)

final class Renderer: NSObject {
    private var moc: NSManagedObjectContext
    private var geometryBuffer:MTLBuffer!

    init() {
        geometryBuffer=device.makeBuffer(length: 1024*1024, options: .storageModeShared)
    }

    func draw(with renderEncoder: MTLRenderCommandEncoder, group: DispatchGroup) {
        group.enter()

        moc.perform {
            renderEncoder.setVertexBuffer(self.geometryBuffer, offset: 0, index: 0) //CRASH HERE (1)
            renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: self.geometryBuffer.length/(MemoryLayout<Float>.size*3)) //OR CRASH HERE (2)

        renderEncoder.endEncoding
        group.leave()
        }
    }
}

class ViewController: MTKViewDelegate {
    var encodingGroup=DispatchGroup()

    func draw(in view: MTKView) {
        guard let parallelRenderCommandEncoder = commandBuffer.makeParallelRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }
        guard let layersRenderEncoder=parallelRenderCommandEncoder.makeRenderCommandEncoder() else { return }
        guard let renderEncoder = parallelRenderCommandEncoder.makeRenderCommandEncoder() else { return }

        renderer.draw(layersRenderEncoder, group: encodingGroup) //This will dispatch to serial queue

        //Continue with the other renderEncoder
        /* Do some encoding work here */

        //Finish this encoder
        renderEncoder.endEncoding()

        //Wait for all encoding threads to finish
        encodingGroup.wait()

        //End final encoder
        parallelRenderCommandEncoder.endEncoding()
    }
}

10-04 10:36