如何在iOS Metal中高效绘制基元?

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

1个回答

0

错误:

由于执行期间出现错误,命令缓冲区的执行被中止。引起GPU挂起错误(IOAF代码3)

是由于访问超出界限的数据导致的。

这就解释了为什么修正顶点计数可以解决问题。

当出现此错误时,打开着色器验证是个好主意,因为它经常会指向错误的原因。

如果这不能指向问题,请仔细查看任何可能使用无效内存的内容,例如使用超过实际长度的缓冲区、对于数据类型使用不正确的跨度或长度,或者尝试绘制比实际存在的更多的顶点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接