如何使用iOS Metal有效地绘制基元?

时间:2017-11-17 20:43:03

标签: ios swift polyline metal gpu-programming

我正在尝试使用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)

所以,显然我在这里做错了。我的第一个猜测是我没有正确更新顶点数据,但无法弄清楚出了什么问题。我的第二个猜测是,在扩展strokerender()函数调用时可能存在一些竞争条件,这可能同时发生。

感谢任何帮助!

编辑:嗯,那太愚蠢了。顶点计数错误,应该是:

  

renderEncoder?.drawPrimitives(type:.lineStrip,vertexStart:0,vertexCount:stroke.count / 3)

因为每个顶点都包含3个Floats。

0 个答案:

没有答案