2017-05-14 22 views
1

我一直在尝试将Apple的MetalBasicTessellation项目转换为iPad Air 3上的swift 3,但迄今为止一直不成功。令人沮丧的是,这个项目带有一个iOS实现(用ObjectiveC编写,并且是一个快速的操场),但没有实现。在iOS设备上设置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的空气2是兼容的,但我有感觉的错误是由于MetalKitView配置不当造成的。我已经从该项目的Objective-C和操作系统文件中反向设计了一些东西,但我已经尽我所能地了解了我当前的专业知识。

// 
// 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() 
    } 
    } */ 


} 

整个混帐可以发现here

会某种金属大师灵魂伸出援助之手?那里的文档相当稀少。

+0

谢谢Jens。我将如何去指定一个MTLFeatureSet?我只是觉得这是由os启用的东西。我的诀窍是有限的 – Plutovman

+0

我的第一条评论可能是错误的(因此我删除了它)。你正在运行哪个iOS版本? – Jens

+0

我正在运行10.3.1(iPad Air 2) – Plutovman

回答

4

docs for MTLFeatureSet_iOS_GPUFamily3_v1说:

Introduced with the Apple A9 GPU and iOS 9.0.

(强调)

同时,iOS Device Compatibility Reference: Hardware GPU Information文章表示,iPad的空气2有A8 GPU。

我不相信你的设备是有能力的。

一般而言,MTKView的配置不会影响所支持的功能集。这在设备中是固有的(硬件和操作系统版本的组合)。您可以使用supportsFeatureSet(_:) method of MTLDevice查询设备是否支持给定功能集。由于设备可以(并且通常)独立于诸如MTKView之类的任何其他对象而获取,所以功能集支持不能依赖于这样的其他对象。

+0

谢谢。我现在正在iPhone 6s上测试相同的代码。我走得更远,但仍然无法跑步。请参阅[this](http://stackoverflow.com/questions/43976669/setting-up-metal-in-swift-3-on-an-iphone-6s)文章。 – Plutovman