我有一个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()
}
}