2017-05-14 2 views
1

J'ai essayé de convertir le projet MetalBasicTessellation d'Apple pour travailler dans swift 3 sur un iPad Air 3, mais jusqu'à présent, j'ai échoué. Frustrant, le projet est livré avec une implémentation iOS (écrite dans objectiveC, et un terrain de jeu rapide), mais pas de mise en œuvre rapide.Configuration de Metal dans Swift 3 sur un appareil iOS

J'ai obtenu le code à compiler, mais ne fonctionne pas sur mon iPad avec l'erreur suivante:

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' 

Je suis assez sûr que l'iPad Air 2 est conforme, mais j'ai le sentiment de l'erreur est due à un MetalKitView mal configuré. J'ai inversé ce que je pouvais des fichiers d'objectifs-c et de terrains de jeu du projet, mais je suis allé aussi loin que je peux comprendre avec mon expertise actuelle.

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


} 

Le git entier peut être trouvé here

serait une sorte-soul métal-gourou donner un coup de main? La documentation est assez rare.

+0

Merci, Jens. Comment pourrais-je spécifier un MTLFeatureSet? Je me suis juste dit que c'était quelque chose qui était activé par l'OS. Mon savoir-faire est ici limité – Plutovman

+0

Mon premier commentaire était probablement faux (donc je l'ai supprimé). Quelle version iOS utilisez-vous? – Jens

+0

Je cours 10.3.1 (iPad Air 2) – Plutovman

Répondre

4

Le docs for MTLFeatureSet_iOS_GPUFamily3_v1 disent:

Introduced with the Apple A9 GPU and iOS 9.0.

(Je souligne.)

Pendant ce temps, l'article iOS Device Compatibility Reference: Hardware GPU Information dit l'iPad 2 a un air GPU A8.

Je ne crois pas que votre appareil en soit capable.

En général, la configuration du MTKView n'affecte pas le jeu de fonctions pris en charge. C'est inhérent à l'appareil (la combinaison du matériel et de la version du système d'exploitation). Vous pouvez demander si un périphérique prend en charge un ensemble de fonctionnalités donné en utilisant le supportsFeatureSet(_:) method of MTLDevice. Comme un périphérique peut être (et est généralement) acquis indépendamment de tout autre objet tel qu'un MTKView, le support de jeu de fonctions ne peut pas dépendre de tels autres objets.

+0

Merci. Je suis en train de tester le même code sur un iPhone 6s. Je m'éloigne, mais je ne peux toujours pas courir. S'il vous plaît voir [this] (http://stackoverflow.com/questions/43976669/setting-up-metal-in-swift-3-on-an-iphone-6s) post. – Plutovman