我正在尝试使用iOS Metal框架和Swift 4 / iOS 11 / XCode 9绘制(并经常更新)Polyline。对于最终项目,我希望能够“绘制”一条线与我的手指,捕捉触摸事件。我基本上正在调整本教程中的代码Metal Tutorial Swift 3 Part 1,只是改变我将在这里描述的部分。特别是片段和顶点着色器保持不变:
vertex float4 basic_vertex(
const device packed_float3* vertex_array [[ buffer(0) ]],
unsigned int vid [[ vertex_id ]]) {
return float4(vertex_array[vid],1.0);
}
fragment half4 basic_fragment() {
return half4(1.0);
}
基本上我只是在每个传入的触摸事件上扩展vertexData
数组(我将其重命名为stroke
):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let t = touches.first {
let pos = t.location(in: view).applying(transformMatrix)
stroke = []
addStrokePoint(point: pos)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let t = touches.first {
let pos = t.location(in: view).applying(transformMatrix)
addStrokePoint(point: pos)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let t = touches.first {
let pos = t.location(in: view).applying(transformMatrix)
addStrokePoint(point: pos)
}
}
func addStrokePoint(point: CGPoint) {
stroke += [Float(point.x), Float(point.y), 0.0]
}
我知道这不是定义“笔画”的特别有效或好的方法,我只想快速启动并运行。对于每个额外的触摸点,我将坐标(px,py)
转换为带有变换矩阵的规范化设备坐标(x,y)
:
transformMatrix = CGAffineTransform(a: 2.0/view.layer.frame.width, b: 0.0, c: 0.0, d: -2.0/view.layer.frame.height, tx: -1.0, ty: 1.0)
对于渲染,我将顶点绘制为.lineStrip
类型的基元,它应该“栅格化每对相邻顶点之间的直线,从而产生一系列连接线(也称为折线)。”
func render() {
if stroke.count > 2 {
let dataSize = stroke.count * MemoryLayout.size(ofValue: stroke[0])
vertexBuffer = device.makeBuffer(bytes: stroke, length: dataSize, options: [])
guard let drawable = metalLayer?.nextDrawable() else { return }
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 104.0/255.0, blue: 0.0, alpha: 1.0)
let commandBuffer = commandQueue.makeCommandBuffer()
let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
renderEncoder?.setRenderPipelineState(pipelineState)
renderEncoder?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
renderEncoder?.drawPrimitives(type: .lineStrip, vertexStart: 0, vertexCount: stroke.count)
renderEncoder?.endEncoding()
commandBuffer?.present(drawable)
commandBuffer?.commit()
}
}
代码在我的iPhone 6s设备上编译并运行,线条非常顺利地跟着我的手指。但似乎缓冲区中仍然存在一些无效或旧数据(?),因为渲染闪烁并且看似随机的附加线被绘制到屏幕上。
此外,在运行时的某个时刻,我收到以下错误:
由于执行期间的错误,命令缓冲区的执行被中止。导致GPU挂起错误(IOAF代码3)
所以,显然我在这里做错了。我的第一个猜测是我没有正确更新顶点数据,但无法弄清楚出了什么问题。我的第二个猜测是,在扩展stroke
和render()
函数调用时可能存在一些竞争条件,这可能同时发生。
感谢任何帮助!
编辑:嗯,那太愚蠢了。顶点计数错误,应该是:
renderEncoder?.drawPrimitives(type:.lineStrip,vertexStart:0,vertexCount:stroke.count / 3)
因为每个顶点都包含3个Floats。