为什么 Xcode 8.1 + 金属。使我用来操作 3D 模型的计时器崩溃?
Why does Xcode 8.1 + Metal. crash the Timer I use to manipulate my 3D model?
我有一个轨迹球操纵器,它使用 Timer
来创建当前旋转角度的平滑减速衰减。它在我的 GL
应用程序中一直运行良好。在 Metal
中,它使应用程序崩溃。
下面是我如何实例化定时器:
rotationTimer = Timer.scheduledTimer(
timeInterval: TimeInterval(kRotationRate),
target: self,
selector: Selector(("rotationTimerHandler")),
userInfo: [ "radiansBegin":radians, "radians":radians, "radiansEnd":0, "counter":0 ],
repeats: true)
函数 rotationTimerHandler
减小角度 - radians
- 然后从中构建一个变换矩阵,该矩阵包含在我的渲染绘制循环的更新函数中。
我怀疑我需要与 Metal
绘制循环的时间同步,但我看不出有什么办法。如何让 Timer
和 Metal
一起玩得开心?
更新 0
根据 Warren 的问题风暴 :-) 这里有更多的背景信息。
我的应用程序的视图是具有 2 个属性的 MTKView
。
符合MTKViewDelegate
协议并实现的渲染器:
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize)
func draw(in view: MTKView)
绘图由 MTKView 的内部显示 link 计时器驱动。一切都在主线程上运行。没有异步调用。
还有一个轨迹球。将屏幕手势映射到四元数到 3D 旋转矩阵的对象。 arcball 是视图的平移手势处理程序:
addGestureRecognizer(UIPanGestureRecognizer.init(
target: arcBall,
action: #selector(EIArcball.arcBallPanHandler)))
}
当 arcball 的平移手势处理程序进入结束状态时,我实例化一个计时器 N 秒以继续生成 quaternions/matrices 基于平移结束时旋转的 angle/axis 以获得令人愉悦的滑动效果。
好的,渲染器(符合MTKViewDelegate
)。绘制循环:
public func draw(in view: MTKView) {
update(view: view as! EIView, drawableSize: view.bounds.size)
// final pass
if let passDescriptor = view.currentRenderPassDescriptor, let drawable = view.currentDrawable {
passDescriptor.colorAttachments[ 0 ].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1.0)
let commandBuffer = commandQueue.makeCommandBuffer()
let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor)
renderCommandEncoder.setFrontFacing(.counterClockwise)
renderCommandEncoder.setTriangleFillMode(.fill)
renderCommandEncoder.setCullMode(.none)
renderCommandEncoder.setRenderPipelineState(heroModelPipelineState)
renderCommandEncoder.setVertexBuffer(heroModel.vertexMetalBuffer, offset: 0, at: 0)
renderCommandEncoder.setVertexBuffer(heroModel.metallicTransform.metalBuffer, offset: 0, at: 1)
renderCommandEncoder.setFragmentTexture(heroModelTexture, at: 0)
renderCommandEncoder.drawIndexedPrimitives(
type: .triangle,
indexCount: heroModel.vertexIndexMetalBuffer.length / MemoryLayout<UInt16>.size,
indexType: MTLIndexType.uint16,
indexBuffer: heroModel.vertexIndexMetalBuffer,
indexBufferOffset: 0)
renderCommandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}
这里没有什么特别有趣的。注意调用:
update(view: view as! EIView, drawableSize: view.bounds.size)
这里是update
func update(view: EIView, drawableSize:CGSize) {
heroModel.metallicTransform.update(camera: camera, transformer: {
return view.arcBall.rotationMatrix * GLKMatrix4MakeScale(150, 150, 1)
})
}
function heroModel.metallicTransform.update(...)
构建了一个在我的顶点着色器中使用的模型-视图-投影矩阵。这是一个玩具应用程序,它绘制一个在 x-y 方向缩放 150 倍的四边形,通过向 arcball 提供平移手势来操纵(旋转)该四边形。
在 update
中,我没有尝试安排 arcball 变换矩阵的馈送进行更新。这似乎是代码的味道。
这是崩溃的屏幕转储:
我想得越多,似乎我应该明确地进行绘制调用,因为我没有真正需要不断重绘。
1) 平移时只需调用 renderer.draw()。
2) 平移结束后从我的计时器处理程序调用 render.draw()。
更新 1
这里有更多堆栈跟踪。只是一大堆汇编程序:
更新 2
bt(回溯)的输出:
我想我会尝试 运行 Metal
没有 内部计时器并显式调用 MTKView.draw()
。在如此简单的应用程序中不需要基于计时器的方法,并且我基于 NSTimer
的 Arcball 存在问题。
已解决。我决定放弃使用 MTKView
内部计时器,并在 NSTimer 回调中平移 和 期间在 Arcball
中显式调用 MTKView.draw()
。十分简单。工作方式相同。
我有一个轨迹球操纵器,它使用 Timer
来创建当前旋转角度的平滑减速衰减。它在我的 GL
应用程序中一直运行良好。在 Metal
中,它使应用程序崩溃。
下面是我如何实例化定时器:
rotationTimer = Timer.scheduledTimer(
timeInterval: TimeInterval(kRotationRate),
target: self,
selector: Selector(("rotationTimerHandler")),
userInfo: [ "radiansBegin":radians, "radians":radians, "radiansEnd":0, "counter":0 ],
repeats: true)
函数 rotationTimerHandler
减小角度 - radians
- 然后从中构建一个变换矩阵,该矩阵包含在我的渲染绘制循环的更新函数中。
我怀疑我需要与 Metal
绘制循环的时间同步,但我看不出有什么办法。如何让 Timer
和 Metal
一起玩得开心?
更新 0
根据 Warren 的问题风暴 :-) 这里有更多的背景信息。
我的应用程序的视图是具有 2 个属性的 MTKView
。
符合MTKViewDelegate
协议并实现的渲染器:
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize)
func draw(in view: MTKView)
绘图由 MTKView 的内部显示 link 计时器驱动。一切都在主线程上运行。没有异步调用。
还有一个轨迹球。将屏幕手势映射到四元数到 3D 旋转矩阵的对象。 arcball 是视图的平移手势处理程序:
addGestureRecognizer(UIPanGestureRecognizer.init(
target: arcBall,
action: #selector(EIArcball.arcBallPanHandler)))
}
当 arcball 的平移手势处理程序进入结束状态时,我实例化一个计时器 N 秒以继续生成 quaternions/matrices 基于平移结束时旋转的 angle/axis 以获得令人愉悦的滑动效果。
好的,渲染器(符合MTKViewDelegate
)。绘制循环:
public func draw(in view: MTKView) {
update(view: view as! EIView, drawableSize: view.bounds.size)
// final pass
if let passDescriptor = view.currentRenderPassDescriptor, let drawable = view.currentDrawable {
passDescriptor.colorAttachments[ 0 ].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1.0)
let commandBuffer = commandQueue.makeCommandBuffer()
let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: passDescriptor)
renderCommandEncoder.setFrontFacing(.counterClockwise)
renderCommandEncoder.setTriangleFillMode(.fill)
renderCommandEncoder.setCullMode(.none)
renderCommandEncoder.setRenderPipelineState(heroModelPipelineState)
renderCommandEncoder.setVertexBuffer(heroModel.vertexMetalBuffer, offset: 0, at: 0)
renderCommandEncoder.setVertexBuffer(heroModel.metallicTransform.metalBuffer, offset: 0, at: 1)
renderCommandEncoder.setFragmentTexture(heroModelTexture, at: 0)
renderCommandEncoder.drawIndexedPrimitives(
type: .triangle,
indexCount: heroModel.vertexIndexMetalBuffer.length / MemoryLayout<UInt16>.size,
indexType: MTLIndexType.uint16,
indexBuffer: heroModel.vertexIndexMetalBuffer,
indexBufferOffset: 0)
renderCommandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
}
这里没有什么特别有趣的。注意调用:
update(view: view as! EIView, drawableSize: view.bounds.size)
这里是update
func update(view: EIView, drawableSize:CGSize) {
heroModel.metallicTransform.update(camera: camera, transformer: {
return view.arcBall.rotationMatrix * GLKMatrix4MakeScale(150, 150, 1)
})
}
function heroModel.metallicTransform.update(...)
构建了一个在我的顶点着色器中使用的模型-视图-投影矩阵。这是一个玩具应用程序,它绘制一个在 x-y 方向缩放 150 倍的四边形,通过向 arcball 提供平移手势来操纵(旋转)该四边形。
在 update
中,我没有尝试安排 arcball 变换矩阵的馈送进行更新。这似乎是代码的味道。
这是崩溃的屏幕转储:
我想得越多,似乎我应该明确地进行绘制调用,因为我没有真正需要不断重绘。 1) 平移时只需调用 renderer.draw()。 2) 平移结束后从我的计时器处理程序调用 render.draw()。
更新 1
这里有更多堆栈跟踪。只是一大堆汇编程序:
更新 2
bt(回溯)的输出:
我想我会尝试 运行 Metal
没有 内部计时器并显式调用 MTKView.draw()
。在如此简单的应用程序中不需要基于计时器的方法,并且我基于 NSTimer
的 Arcball 存在问题。
已解决。我决定放弃使用 MTKView
内部计时器,并在 NSTimer 回调中平移 和 期间在 Arcball
中显式调用 MTKView.draw()
。十分简单。工作方式相同。