为什么这个 Metal 代码没有生成正确的三角形几何图形?
Why is this Metal code not producing the correct triangle geometry?
我正在关注 Metal By Example 的 Metal 教程并将代码转换为 Swift。
我是运行第二个例子,由于某种原因,这段代码不起作用。
import UIKit
import Metal
import MetalKit
import simd
class MBEMetalView: UIView {
// // // // // MAIN // // // // //
var metalDevice: MTLDevice! = nil
var metalLayer: CAMetalLayer! = nil
var commandQueue: MTLCommandQueue! = nil
var vertexBuffer: MTLBuffer! = nil
var pipelineState: MTLRenderPipelineState! = nil
var displayLink: CADisplayLink! = nil
override class var layerClass : AnyClass {
return CAMetalLayer.self
}
// override func didMoveToWindow() {
// self.redraw()
// }
override func didMoveToSuperview() {
super.didMoveToSuperview()
if self.superview != nil {
self.displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired))
self.displayLink.add(to: RunLoop.main, forMode: .common)
} else {
self.displayLink.invalidate()
}
}
@objc func displayLinkFired() {
self.redraw()
}
// // // // // INIT // // // // //
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.prepareDeviceLayerAndQueue()
self.makeBuffers()
self.makePipeline()
}
func prepareDeviceLayerAndQueue() {
metalLayer = (self.layer as! CAMetalLayer)
metalDevice = MTLCreateSystemDefaultDevice()
metalLayer.device = metalDevice
metalLayer.pixelFormat = .bgra8Unorm
commandQueue = metalDevice.makeCommandQueue()
}
func makeBuffers() {
var vertices: [MBEVertex] = [
MBEVertex(position: vector_float4(0, 0.5, 0, 1) , color: vector_float4(1, 0, 0, 1)),
MBEVertex(position: vector_float4(-0.5, -0.5, 0, 1) , color: vector_float4(0, 1, 0, 1)),
MBEVertex(position: vector_float4(0.5, -0.5, 0, 1) , color: vector_float4(0, 0, 1, 1))
]
self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: 56, options: .storageModeShared)
}
func makePipeline() {
guard let library = metalDevice.makeDefaultLibrary() else { print("COULD NOT CREATE LIBRARY") ; return }
guard let vertexFunction = library.makeFunction(name: "vertex_main") else { print("COULD NOT CREATE A VERTEX FUNCTION") ; return }
guard let fragmentFunction = library.makeFunction(name: "fragment_main") else { print("COULD NOT CREATE LIBRARY") ; return }
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
pipelineState = try? metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor)
if pipelineState == nil { print("COULD NOT CREATE PIPELINE STATE") ; return }
}
// // // // // FUNCTIONS // // // // //
func redraw() {
guard let drawable = metalLayer.nextDrawable() else { print("COULD NOT CREATE A DRAWABLE") ; return }
let texture = drawable.texture
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].storeAction = .store
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
guard let commandBuffer = commandQueue.makeCommandBuffer() else { print("COULD NOT CREATE A COMMAND BUFFER") ; return }
guard let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { print("COULD NOT CREATE AN ENCODER") ; return }
commandEncoder.setRenderPipelineState(pipelineState)
commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
commandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
// // // // // TYPES // // // // //
struct MBEVertex {
var position: vector_float4
var color: vector_float4
}
}
我很确定着色器不是问题,因为顶点着色器只是接受顶点缓冲区和 return 顶点。
片段着色器简单地获取顶点着色器的输出和 returns 颜色。
视图控制器是默认的 -- 没有碰过它。
我将控制器的视图 class 更改为这个 MBEMetalView class。
我确实偶然发现了使用顶点指针、长度和选项 "storageModeShared" 创建顶点缓冲区的代码行错误。
最初,我将长度设置为
MemoryLayout.size(of: vertices)
但是根据控制台消息,当缓冲区需要 32 的长度时,它只创建了 8 的长度。
我只是增加了长度,错误消失了...
但是,三角形变形了,颜色不正确。看起来前两个顶点的位置正确,但第三个顶点的位置不正确。
None 的颜色是正确的。有什么想法吗?
当我更改缓冲区的长度时 - 三角形会改变形状或从屏幕上消失。
无需猜测缓冲区的长度。您可以将其计算为顶点数乘以每个顶点占用的字节数:
let bufferLength = MemoryLayout<MBEVertex>.stride * vertices.count
self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: bufferLength, options: .storageModeShared)
在这种情况下,结果实际上是 96。
这通常是比硬编码常量更好的方法。
我正在关注 Metal By Example 的 Metal 教程并将代码转换为 Swift。
我是运行第二个例子,由于某种原因,这段代码不起作用。
import UIKit
import Metal
import MetalKit
import simd
class MBEMetalView: UIView {
// // // // // MAIN // // // // //
var metalDevice: MTLDevice! = nil
var metalLayer: CAMetalLayer! = nil
var commandQueue: MTLCommandQueue! = nil
var vertexBuffer: MTLBuffer! = nil
var pipelineState: MTLRenderPipelineState! = nil
var displayLink: CADisplayLink! = nil
override class var layerClass : AnyClass {
return CAMetalLayer.self
}
// override func didMoveToWindow() {
// self.redraw()
// }
override func didMoveToSuperview() {
super.didMoveToSuperview()
if self.superview != nil {
self.displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired))
self.displayLink.add(to: RunLoop.main, forMode: .common)
} else {
self.displayLink.invalidate()
}
}
@objc func displayLinkFired() {
self.redraw()
}
// // // // // INIT // // // // //
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.prepareDeviceLayerAndQueue()
self.makeBuffers()
self.makePipeline()
}
func prepareDeviceLayerAndQueue() {
metalLayer = (self.layer as! CAMetalLayer)
metalDevice = MTLCreateSystemDefaultDevice()
metalLayer.device = metalDevice
metalLayer.pixelFormat = .bgra8Unorm
commandQueue = metalDevice.makeCommandQueue()
}
func makeBuffers() {
var vertices: [MBEVertex] = [
MBEVertex(position: vector_float4(0, 0.5, 0, 1) , color: vector_float4(1, 0, 0, 1)),
MBEVertex(position: vector_float4(-0.5, -0.5, 0, 1) , color: vector_float4(0, 1, 0, 1)),
MBEVertex(position: vector_float4(0.5, -0.5, 0, 1) , color: vector_float4(0, 0, 1, 1))
]
self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: 56, options: .storageModeShared)
}
func makePipeline() {
guard let library = metalDevice.makeDefaultLibrary() else { print("COULD NOT CREATE LIBRARY") ; return }
guard let vertexFunction = library.makeFunction(name: "vertex_main") else { print("COULD NOT CREATE A VERTEX FUNCTION") ; return }
guard let fragmentFunction = library.makeFunction(name: "fragment_main") else { print("COULD NOT CREATE LIBRARY") ; return }
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
pipelineState = try? metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor)
if pipelineState == nil { print("COULD NOT CREATE PIPELINE STATE") ; return }
}
// // // // // FUNCTIONS // // // // //
func redraw() {
guard let drawable = metalLayer.nextDrawable() else { print("COULD NOT CREATE A DRAWABLE") ; return }
let texture = drawable.texture
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].storeAction = .store
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
guard let commandBuffer = commandQueue.makeCommandBuffer() else { print("COULD NOT CREATE A COMMAND BUFFER") ; return }
guard let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { print("COULD NOT CREATE AN ENCODER") ; return }
commandEncoder.setRenderPipelineState(pipelineState)
commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
commandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
// // // // // TYPES // // // // //
struct MBEVertex {
var position: vector_float4
var color: vector_float4
}
}
我很确定着色器不是问题,因为顶点着色器只是接受顶点缓冲区和 return 顶点。
片段着色器简单地获取顶点着色器的输出和 returns 颜色。
视图控制器是默认的 -- 没有碰过它。
我将控制器的视图 class 更改为这个 MBEMetalView class。
我确实偶然发现了使用顶点指针、长度和选项 "storageModeShared" 创建顶点缓冲区的代码行错误。
最初,我将长度设置为
MemoryLayout.size(of: vertices)
但是根据控制台消息,当缓冲区需要 32 的长度时,它只创建了 8 的长度。
我只是增加了长度,错误消失了...
但是,三角形变形了,颜色不正确。看起来前两个顶点的位置正确,但第三个顶点的位置不正确。
None 的颜色是正确的。有什么想法吗?
当我更改缓冲区的长度时 - 三角形会改变形状或从屏幕上消失。
无需猜测缓冲区的长度。您可以将其计算为顶点数乘以每个顶点占用的字节数:
let bufferLength = MemoryLayout<MBEVertex>.stride * vertices.count
self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: bufferLength, options: .storageModeShared)
在这种情况下,结果实际上是 96。
这通常是比硬编码常量更好的方法。