我有一个MTLBuffer
实例变量,我只能从与NSManagedObjectContext
关联的串行队列中访问(读取)。
我使用MTLParallelRenderCommandEncoder
将绘图工作分为来自主线程的部分和发生在NSManagedObjectContext
的串行队列中的部分。
访问此MTLBuffer
时,仍然遇到线程清理问题(或在Metal中崩溃)。为什么会这样?
我的代码基本上像这样:
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)
。
除了“我的”串行队列之外,还可以访问MTLBuffer
吗?
答案 0 :(得分:0)
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()
}
}