Почему этот код металла не дает правильной геометрии треугольника?

Я слежу за учебником Metal от Metal By Example и конвертирую код в 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
     }

}

Я почти уверен, что шейдеры не проблема, потому что вершинный шейдер просто берет буфер вершин и возвращает вершину.

Фрагментный шейдер просто берет выходные данные вершинного шейдера и возвращает цвет.

Контроллер представления по умолчанию - не трогал его.

Я изменил класс представления контроллера на этот класс MBEMetalView.

Я наткнулся на ошибку в строке кода, которая создает буфер вершин с указателем вершин, длиной и опцией storageModeShared.

Первоначально я установил length = равным MemoryLayout.size(of: vertices), но, согласно сообщению консоли, оно создавало длину 8 только тогда, когда для буфера требовалась длина 32.

Я просто увеличил длину и ошибка исчезла ...

Но треугольник искажен и имеет неправильный цвет. Похоже, что первые две вершины имеют правильные позиции, а третья - нет.

Ни один из цветов не подходит. Любые идеи?

Когда я изменяю длину буфера - треугольник либо изменит форму, либо исчезнет с экрана.


person Evan Escobar    schedule 21.08.2019    source источник
comment
После простого угадывания удвоения (пару раз) рекомендованной длины буфера, который выводился на консоль из-за ошибки при использовании `` length: MemoryLayout.size (of: vertices) '', треугольник был отрисован правильно. .. Правильная длина кажется 128   -  person Evan Escobar    schedule 21.08.2019


Ответы (1)


Нет необходимости угадывать длину буфера. Вы можете вычислить это как количество вершин, умноженное на количество байтов, занятых каждой вершиной:

let bufferLength = MemoryLayout<MBEVertex>.stride * vertices.count
self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: bufferLength, options: .storageModeShared)

В этом случае результат на самом деле 96.

Обычно это лучший подход, чем жестко запрограммированные константы.

person warrenm    schedule 21.08.2019
comment
Аааа, спасибо @warrenm! Я не включил <MBVertex> - person Evan Escobar; 22.08.2019
comment
Есть ли шанс, что вы знаете, почему у треугольника грубые стороны вместо гладких? @warrenm - person Evan Escobar; 22.08.2019