在 iOS 设备上的 Swift 3 中设置 Metal



我一直在尝试将Apple的MetalBasicTessellation项目转换为iPad Air 3上的swift 3,但到目前为止都没有成功。 令人沮丧的是,该项目带有一个iOS实现(用objectiveC编写,以及一个swift playground(,但没有swift 3实现。

我已经获得了要编译的代码,但无法在我的iPad上运行,并出现以下错误:

2017-05-14 14:25:54.268400-0700 MetalBasicTessellation[2436:570250] -[MTLRenderPipelineDescriptorInternal validateWithDevice:], line 1728: error 'tessellation is only supported on MTLFeatureSet_iOS_GPUFamily3_v1 and later'

我很确定iPad Air 2是兼容的,但我感觉错误是由于配置不正确的MetalKitView造成的。 我已经从项目的 objective-c 和 playground 文件中对我所能做的事情进行了逆向工程,但我已经以我目前的专业知识达到了我能够理解的程度。

//
//  ViewController.swift
//  MetalBasicTessellation
//
//  Created by vladimir sierra on 5/10/17.
//  
//
import UIKit
import Metal
import MetalKit
class ViewController: UIViewController {
  @IBOutlet weak var mtkView: MTKView!
  // Seven steps required to set up metal for rendering:
  // 1. Create a MTLDevice
  // 2. Create a CAMetalLayer
  // 3. Create a Vertex Buffer
  // 4. Create a Vertex Shader
  // 5. Create a Fragment Shader
  // 6. Create a Render Pipeline
  // 7. Create a Command Queue
  var device: MTLDevice! // to be initialized in viewDidLoad
  //var metalLayer: CAMetalLayer! // to be initialized in viewDidLoad
  var vertexBuffer: MTLBuffer! // to be initialized in viewDidLoad
  var library: MTLLibrary!
  // once we create a vertex and fragment shader, we combine them in an object called render pipeline. In Metal the shaders are precompiled, and the render pipeline configuration is compiled after you first set it up. This makes everything extremely efficient
  var renderPipeline: MTLRenderPipelineState! // to be initialized in viewDidLoad
  var commandQueue: MTLCommandQueue! // to be initialized in viewDidLoad
  //var timer: CADisplayLink! // function to be called every time the device screen refreshes so we can redraw the screen

  override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    /*
    if let window = view.window {
      let scale = window.screen.nativeScale // (2 for iPhone 5s, 6 and iPads;  3 for iPhone 6 Plus)
      let layerSize = view.bounds.size
      // apply the scale to increase the drawable texture size.
      view.contentScaleFactor = scale
      //metalLayer.frame = CGRect(x: 0, y: 0, width: layerSize.width, height: layerSize.height)
      //metalLayer.drawableSize = CGSize(width: layerSize.width * scale, height: layerSize.height * scale)
    } */
  }
  override func viewDidLoad() {
    super.viewDidLoad()
    device = MTLCreateSystemDefaultDevice() // returns a reference to the default MTLDevice
    //device.supportsFeatureSet(MTLFeatureSet_iOS_GPUFamily3_v2)

    // set up layer to display metal content
    //metalLayer = CAMetalLayer()          // initialize metalLayer
    //metalLayer.device = device           // device the layer should use
    //metalLayer.pixelFormat = .bgra8Unorm // normalized 8 bit rgba
    //metalLayer.framebufferOnly = true    // set to true for performance issues
    //view.layer.addSublayer(metalLayer)   // add sublayer to main view's layer
    // precompile custom metal functions
    let defaultLibrary = device.newDefaultLibrary()! // MTLLibrary object with precompiled shaders

    let fragmentProgram = defaultLibrary.makeFunction(name: "tessellation_fragment")
    let vertexProgram = defaultLibrary.makeFunction(name: "tessellation_vertex_triangle")
    // Setup Compute Pipeline
    let kernelFunction = defaultLibrary.makeFunction(name: "tessellation_kernel_triangle")
    var computePipeline: MTLComputePipelineState?
    do {
      computePipeline = try device.makeComputePipelineState(function: kernelFunction!)
    } catch let error as NSError {
      print("compute pipeline error: " + error.description)
    }
    // Setup Vertex Descriptor
    let vertexDescriptor = MTLVertexDescriptor()
    vertexDescriptor.attributes[0].format = .float4
    vertexDescriptor.attributes[0].offset = 0
    vertexDescriptor.attributes[0].bufferIndex = 0;
    vertexDescriptor.layouts[0].stepFunction = .perPatchControlPoint
    vertexDescriptor.layouts[0].stepRate = 1
    vertexDescriptor.layouts[0].stride = 4*MemoryLayout<Float>.size
    // Setup Render Pipeline
    let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
    renderPipelineDescriptor.vertexDescriptor = vertexDescriptor
    //renderPipelineDescriptor.fragmentFunction = defaultLibrary.makeFunction(name: "tessellation_fragment")
    renderPipelineDescriptor.fragmentFunction = fragmentProgram
    //renderPipelineDescriptor.vertexFunction = defaultLibrary.makeFunction(name: "tessellation_vertex_triangle")
    renderPipelineDescriptor.vertexFunction = vertexProgram
    //renderPipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm // normalized 8 bit rgba
    renderPipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat
    renderPipelineDescriptor.isTessellationFactorScaleEnabled = false
    renderPipelineDescriptor.tessellationFactorFormat = .half
    renderPipelineDescriptor.tessellationControlPointIndexType = .none
    renderPipelineDescriptor.tessellationFactorStepFunction = .constant
    renderPipelineDescriptor.tessellationOutputWindingOrder = .clockwise
    renderPipelineDescriptor.tessellationPartitionMode = .fractionalEven
    renderPipelineDescriptor.maxTessellationFactor = 64;
    // Compile renderPipeline
    do {
      renderPipeline = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
    } catch let error as NSError {
      print("render pipeline error: " + error.description)
    }
    // Setup Buffers
    let tessellationFactorsBuffer = device.makeBuffer(length: 256, options: MTLResourceOptions.storageModePrivate)
    let controlPointPositions: [Float] = [
      -0.8, -0.8, 0.0, 1.0,   // lower-left
      0.0,  0.8, 0.0, 1.0,   // upper-middle
      0.8, -0.8, 0.0, 1.0,   // lower-right
    ]
    let controlPointsBuffer = device.makeBuffer(bytes: controlPointPositions, length:256 , options: [])
    // Tessellation Pass
    let commandBuffer = commandQueue.makeCommandBuffer()
    let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()
    computeCommandEncoder.setComputePipelineState(computePipeline!)
    let edgeFactor: [Float] = [16.0]
    let insideFactor: [Float] = [8.0]
    computeCommandEncoder.setBytes(edgeFactor, length: MemoryLayout<Float>.size, at: 0)
    computeCommandEncoder.setBytes(insideFactor, length: MemoryLayout<Float>.size, at: 1)
    computeCommandEncoder.setBuffer(tessellationFactorsBuffer, offset: 0, at: 2)
    computeCommandEncoder.dispatchThreadgroups(MTLSizeMake(1, 1, 1), threadsPerThreadgroup: MTLSizeMake(1, 1, 1))
    computeCommandEncoder.endEncoding()
    let renderPassDescriptor = mtkView.currentRenderPassDescriptor
    let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
    renderCommandEncoder.setRenderPipelineState(renderPipeline!)
    renderCommandEncoder.setVertexBuffer(controlPointsBuffer, offset: 0, at: 0)
    renderCommandEncoder.setTriangleFillMode(.lines)
    renderCommandEncoder.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0)
    renderCommandEncoder.drawPatches(numberOfPatchControlPoints: 3, patchStart: 0, patchCount: 1, patchIndexBuffer: nil, patchIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0)
    renderCommandEncoder.endEncoding()
    commandBuffer.present(mtkView.currentDrawable!)
    commandBuffer.commit()
    commandBuffer.waitUntilCompleted()
    /*
    // finally create an ordered list of commands forthe GPU to execute
    commandQueue = device.makeCommandQueue()
    timer = CADisplayLink(target: self, selector: #selector(ViewController.gameloop)) // call gameloop every time the screen refreshes
    timer.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
    */

  }
  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }
  /*
  func render() {
    guard let drawable = metalLayer?.nextDrawable() else { return } // returns the texture to draw into in order for something to appear on the screen
    //objectToDraw.render(commandQueue: commandQueue, renderPipeline: renderPipeline, drawable: drawable, clearColor: nil)
  }
  // this is the routine that gets run every time the screen refreshes
  func gameloop() {
    autoreleasepool {
      self.render()
    }
  } */

}

整个 git 可以在这里找到

会有什么金属大师灵魂伸出援手吗? 那里的文档非常稀少。

MTLFeatureSet_iOS_GPUFamily3_v1的文档说:

Apple A9 GPU和iOS 9.0一起推出。

(强调后加。

同时,iOS设备兼容性参考:硬件GPU信息文章称iPad Air 2具有A8 GPU。

我不相信您的设备有能力。

通常,MTKView的配置不会影响支持的功能集。这是设备固有的(硬件和操作系统版本的组合(。您可以使用 MTLDevicesupportsFeatureSet(_:) 方法查询设备是否支持给定的功能集。由于设备可以(并且通常(独立于任何其他对象(如MTKView(获取,因此功能集支持不能依赖于此类其他对象。

相关内容

  • 没有找到相关文章

最新更新