在iPhone 6s的Swift 3中设置Metal [英] Setting up Metal in Swift 3 on an iPhone 6s

查看:105
本文介绍了在iPhone 6s的Swift 3中设置Metal的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试转换Apple的 MetalBasicTessellation

I've been trying to convert Apple's MetalBasicTessellation project to work in swift 3 on an iPhone 6s running iOS 10.3.1. Everything compiles with no errors, but when running on my iPhone, I get the following error when I define renderCommandEncoder:

validateAttachmentOnDevice:347: failed assertion `MTLRenderPassDescriptor texture must be MTLTextureType2DMultisample when using a resolveTexture.'

我已经正确设置了renderPassDescriptor的texture属性以继承MKTView.currentDrawable纹理.

I have properly set the renderPassDescriptor's texture property to inherit MKTView.currentDrawable texture.

renderPassDescriptor?.colorAttachments[0].texture = view.currentDrawable?.texture

我想念什么?以下是整个AAPLTessellationPipeline类.整个项目可以在此处找到.

What am I missing? Below is the entire AAPLTessellationPipeline class. Entire project can be found here.

/*
 Copyright (C) 2016 Apple Inc. All Rights Reserved.
 See LICENSE.txt for this sample’s licensing information

 Abstract:
 Tessellation Pipeline for MetalBasicTessellation.
 The exposed properties are user-defined via the ViewController UI elements.
 The compute pipelines are built with a compute kernel (one for triangle patches; one for quad patches).
 The render pipelines are built with a post-tessellation vertex function (one for triangle patches; one for quad patches) and a fragment function. The render pipeline descriptor also configures tessellation-specific properties.
 The tessellation factors buffer is dynamically populated by the compute kernel.
 The control points buffer is populated with static position data.
 */

import Metal
import MetalKit


class AAPLTessellationPipeline: NSObject, MTKViewDelegate {

  var patchType = MTLPatchType(rawValue: 0)!
  var isWireframe: Bool = false
  var edgeFactor: [Float] = [0.0]
  var insideFactor: [Float] = [0.0]

  let device: MTLDevice
  let commandQueue: MTLCommandQueue
  let library: MTLLibrary

  /*
  //private weak var device: MTLDevice?
  //private weak var commandQueue: MTLCommandQueue!
  //private weak var library: MTLLibrary?
  private weak var computePipelineTriangle: MTLComputePipelineState?
  private weak var computePipelineQuad: MTLComputePipelineState?
  private weak var renderPipelineTriangle: MTLRenderPipelineState?
  private weak var renderPipelineQuad: MTLRenderPipelineState?
  private weak var tessellationFactorsBuffer: MTLBuffer?
  private weak var controlPointsBufferTriangle: MTLBuffer?
  private weak var controlPointsBufferQuad: MTLBuffer?
  */

  var computePipelineTriangle: MTLComputePipelineState?
  var computePipelineQuad: MTLComputePipelineState?
  var renderPipelineTriangle: MTLRenderPipelineState?
  var renderPipelineQuad: MTLRenderPipelineState?
  var tessellationFactorsBuffer: MTLBuffer?
  var controlPointsBufferTriangle: MTLBuffer?
  var controlPointsBufferQuad: MTLBuffer?

  init? (mtkView view: MTKView) {

    device = MTLCreateSystemDefaultDevice()!
    commandQueue = device.makeCommandQueue()
    library = device.newDefaultLibrary()!

    super.init()

    // Initialize properties
    isWireframe = true
    patchType = .triangle
    edgeFactor = [2.0]
    insideFactor = [2.0]
    // Setup Metal


    if !didSetupMetal() {
      return nil
    }

    // Assign device and delegate to MTKView
    view.device = device
    view.delegate = self

    // Setup compute pipelines
    if !didSetupComputePipelines() {
      return nil
    }

    // Setup render pipelines
    if !didSetupRenderPipelines(with: view) {
      return nil
    }

    // Setup Buffers
    setupBuffers()

  }

  // MARK: Setup methods
  func didSetupMetal() -> Bool {
    // Use the default device
    //device = MTLCreateSystemDefaultDevice()
    /*
    if device == nil {
      print("Metal is not supported on this device")
      return false
    }*/
    #if TARGET_OS_IOS
      if !device?.supportsFeatureSet(MTLFeatureSet_iOS_GPUFamily3_v2) {
        print("Tessellation is not supported on this device")
        return false
      }
    #elseif TARGET_OS_OSX
      if !device?.supportsFeatureSet(MTLFeatureSet_OSX_GPUFamily1_v1) {
        print("Tessellation is not supported on this device")
        return false
      }
    #endif

    // Create a new command queue
    //commandQueue = device.makeCommandQueue()

    // Load the default library
    //library = device.newDefaultLibrary()
    return true
  }

  func didSetupComputePipelines() -> Bool {
    //var computePipelineError: Error?
    // Create compute pipeline for triangle-based tessellation
    let kernelFunctionTriangle = library.makeFunction(name: "tessellation_kernel_triangle")
    //print ("...kernel triangle \(kernelFunctionTriangle)")
    //computePipelineTriangle: MTLComputePipelineState?
    do {
      computePipelineTriangle = try device.makeComputePipelineState(function: kernelFunctionTriangle!)
    } catch let error as NSError {
      print("compute pipeline error: " + error.description)
    }

    let kernelFunctionQuad = library.makeFunction(name: "tessellation_kernel_quad")
    //var computePipelineQuad: MTLComputePipelineState?
    do {
      computePipelineQuad = try device.makeComputePipelineState(function: kernelFunctionQuad!)
    } catch let error as NSError {
      print("compute pipeline error: " + error.description)
    }


    return true
  }

  func didSetupRenderPipelines(with view: MTKView) -> Bool {

    let vertexProgramTriangle = library.makeFunction(name: "tessellation_vertex_triangle")
    let vertexProgramQuad = library.makeFunction(name: "tessellation_vertex_quad")
    let fragmentProgram = library.makeFunction(name: "tessellation_fragment")
    //var renderPipelineError: Error? = nil
    // Create a reusable vertex descriptor for the control point data
    // This describes the inputs to the post-tessellation vertex function, declared with the 'stage_in' qualifier
    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
    // Create a reusable render pipeline descriptor
    let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
    // Configure common render properties
    renderPipelineDescriptor.vertexDescriptor = vertexDescriptor
    renderPipelineDescriptor.sampleCount = view.sampleCount
    renderPipelineDescriptor.colorAttachments[0].pixelFormat = view.colorPixelFormat


    //renderPipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
    renderPipelineDescriptor.fragmentFunction = fragmentProgram





    // Configure common tessellation properties
    renderPipelineDescriptor.isTessellationFactorScaleEnabled = false
    renderPipelineDescriptor.tessellationFactorFormat = .half
    renderPipelineDescriptor.tessellationControlPointIndexType = .none
    renderPipelineDescriptor.tessellationFactorStepFunction = .constant
    renderPipelineDescriptor.tessellationOutputWindingOrder = .clockwise
    renderPipelineDescriptor.tessellationPartitionMode = .fractionalEven

    /*
    #if TARGET_OS_IOS
      // In iOS, the maximum tessellation factor is 16
      renderPipelineDescriptor.maxTessellationFactor = 16
    #elseif TARGET_OS_OSX
      // In OS X, the maximum tessellation factor is 64
      renderPipelineDescriptor.maxTessellationFactor = 64
    #endif
    */

    renderPipelineDescriptor.maxTessellationFactor = 16

    // Create render pipeline for triangle-based tessellation
    //renderPipelineDescriptor.vertexFunction = library?.newFunction(withName: "tessellation_vertex_triangle")
    renderPipelineDescriptor.vertexFunction = vertexProgramTriangle

    // Compile renderPipeline for triangle-based tessellation
    do {
      renderPipelineTriangle = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
    } catch let error as NSError {
      print("render pipeline error: " + error.description)
    }


    renderPipelineDescriptor.vertexFunction = vertexProgramQuad

    // Compile renderPipeline for quad-based tessellation
    do {
      renderPipelineQuad = try device.makeRenderPipelineState(descriptor: renderPipelineDescriptor)
    } catch let error as NSError {
      print("render pipeline error: " + error.description)
    }


    return true
  }

  func setupBuffers() {
    // Allocate memory for the tessellation factors buffer
    // This is a private buffer whose contents are later populated by the GPU (compute kernel)
    tessellationFactorsBuffer = device.makeBuffer(length: 256, options: MTLResourceOptions.storageModePrivate)
    tessellationFactorsBuffer?.label = "Tessellation Factors"
    // Allocate memory for the control points buffers
    // These are shared or managed buffers whose contents are immediately populated by the CPU
    let controlPointsBufferOptions: MTLResourceOptions = .storageModeShared

    /*
    #if TARGET_OS_IOS
      // In iOS, the storage mode can only be shared
      controlPointsBufferOptions = .storageModeShared
    #elseif TARGET_OS_OSX
      // In OS X, the storage mode can be shared or managed, but managed may yield better performance
      controlPointsBufferOptions = .storageModeManaged
    #endif
    */

    let controlPointPositionsTriangle: [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]
    controlPointsBufferTriangle = device.makeBuffer(bytes: controlPointPositionsTriangle, length: MemoryLayout<Float>.size, options: controlPointsBufferOptions)
    controlPointsBufferTriangle?.label = "Control Points Triangle"
    let controlPointPositionsQuad: [Float] = [-0.8, 0.8, 0.0, 1.0,             // upper-left
      0.8, 0.8, 0.0, 1.0,             // upper-right
      0.8, -0.8, 0.0, 1.0,             // lower-right
      -0.8, -0.8, 0.0, 1.0]
    controlPointsBufferQuad = device.makeBuffer(bytes: controlPointPositionsQuad, length: MemoryLayout<Float>.size, options: controlPointsBufferOptions)
    controlPointsBufferQuad?.label = "Control Points Quad"
    // More sophisticated tessellation passes might have additional buffers for per-patch user data
  }

  // MARK: Compute/Render methods
  func computeTessellationFactors(with commandBuffer: MTLCommandBuffer) {
    // Create a compute command encoder
    let computeCommandEncoder: MTLComputeCommandEncoder = commandBuffer.makeComputeCommandEncoder()
    computeCommandEncoder.label = "Compute Command Encoder"
    // Begin encoding compute commands
    computeCommandEncoder.pushDebugGroup("Compute Tessellation Factors")
    // Set the correct compute pipeline
    if patchType == .triangle {
      computeCommandEncoder.setComputePipelineState(computePipelineTriangle!)
    }
    else if patchType == .quad {
      computeCommandEncoder.setComputePipelineState(computePipelineQuad!)
    }

    // Bind the user-selected edge and inside factor values to the compute kernel
    computeCommandEncoder.setBytes(edgeFactor, length: MemoryLayout<Float>.size, at: 0)
    computeCommandEncoder.setBytes(insideFactor, length: MemoryLayout<Float>.size, at: 1)
    // Bind the tessellation factors buffer to the compute kernel
    computeCommandEncoder.setBuffer(tessellationFactorsBuffer, offset: 0, at: 2)
    // Dispatch threadgroups
    computeCommandEncoder.dispatchThreadgroups(MTLSizeMake(1, 1, 1), threadsPerThreadgroup: MTLSizeMake(1, 1, 1))
    // All compute commands have been encoded
    computeCommandEncoder.popDebugGroup()
    computeCommandEncoder.endEncoding()
  }

  func tessellateAndRender(in view: MTKView, with commandBuffer: MTLCommandBuffer) {
    // Obtain a renderPassDescriptor generated from the view's drawable
    let renderPassDescriptor: MTLRenderPassDescriptor? = view.currentRenderPassDescriptor


    // If the renderPassDescriptor is valid, begin the commands to render into its drawable
    if renderPassDescriptor != nil {
      /*
      //renderPassDescriptor?.colorAttachments[0].texture = .texture // assign passed texture
      renderPassDescriptor?.colorAttachments[0].texture = view.currentDrawable?.texture
      renderPassDescriptor?.colorAttachments[0].loadAction = .clear // set the texture to the clear color before doing any drawing
      renderPassDescriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 104.0/255.0, blue: 5.0/255.0, alpha: 1.0) // set clear color to green
      //renderPassDescriptor?.colorAttachments[0].storeAction = .multisampleResolve
      //renderPassDescriptor?.colorAttachments[0].storeAction = .unknown
      */

      renderPassDescriptor?.colorAttachments[0].texture = view.currentDrawable?.texture
      //renderPassDescriptor?.colorAttachments[0].texture = view.multisampleColorTexture

      // Create a render command encoder
      let renderCommandEncoder: MTLRenderCommandEncoder? = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
      renderCommandEncoder?.label = "Render Command Encoder"
      // Begin encoding render commands, including commands for the tessellator
      renderCommandEncoder?.pushDebugGroup("Tessellate and Render")
      // Set the correct render pipeline and bind the correct control points buffer
      if patchType == .triangle {
        renderCommandEncoder?.setRenderPipelineState(renderPipelineTriangle!)
        renderCommandEncoder?.setVertexBuffer(controlPointsBufferTriangle, offset: 0, at: 0)
      }
      else if patchType == .quad {
        renderCommandEncoder?.setRenderPipelineState(renderPipelineQuad!)
        renderCommandEncoder?.setVertexBuffer(controlPointsBufferQuad, offset: 0, at: 0)
      }

      // Enable/Disable wireframe mode
      if isWireframe {
        renderCommandEncoder?.setTriangleFillMode(.lines)
      }
      // Encode tessellation-specific commands
      renderCommandEncoder?.setTessellationFactorBuffer(tessellationFactorsBuffer, offset: 0, instanceStride: 0)
      let patchControlPoints: Int = (patchType == .triangle) ? 3 : 4
      renderCommandEncoder?.drawPatches(numberOfPatchControlPoints: patchControlPoints, patchStart: 0, patchCount: 1, patchIndexBuffer: nil, patchIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0)



      //renderCommandEncoder.drawPatches(numberOfPatchControlPoints: 3, patchStart: 0, patchCount: 1, patchIndexBuffer: nil, patchIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0)
      // All render commands have been encoded
      renderCommandEncoder?.popDebugGroup()
      renderCommandEncoder?.endEncoding()
      // Schedule a present once the drawable has been completely rendered to
      commandBuffer.present(view.currentDrawable!)
    }
  }

  // MARK: MTKView delegate methods
  // Called whenever view changes orientation or layout is changed
  func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
  }

  // Called whenever the view needs to render
  func draw(in view: MTKView) {
    autoreleasepool {
      // Create a new command buffer for each tessellation pass
      let commandBuffer: MTLCommandBuffer? = commandQueue.makeCommandBuffer()

      commandBuffer?.label = "Tessellation Pass"

      self.computeTessellationFactors(with: commandBuffer!)

      self.tessellateAndRender(in: view, with: commandBuffer!)
      // Finalize tessellation pass and commit the command buffer to the GPU
      commandBuffer?.commit()

    }
  }
}

推荐答案

您似乎不想使用MSAA,因此需要将MTKView的样本计数设置为1(而不是4):

It looks like you're not trying to use MSAA, so you need to set the MTKView's sample count to 1 (instead of 4):

    mtkView.sampleCount = 1

另外,您的笔尖在视图上配置了深度格式,即使没有为此配置管道,它也会导致它为您生成深度纹理,因此也将深度模板像素格式设置为:

Additionally, your nib has a depth format configured on the view, which is causing it to generate a depth texture for you even though your pipeline isn't configured for that, so also set the depth-stencil pixel format to .invalid:

    mtkView.depthStencilPixelFormat = .invalid

最后,您的缓冲区长度不正确.据我所知,controlPointPositionsTriangle的长​​度应为12 * MemoryLayout<Float>.size,而不是MemoryLayout<Float>.size.对于controlPointsBufferQuad(长度应为16 * MemoryLayout<Float>.size)也是如此.

Finally, your buffer lengths are incorrect. As far as I can tell, the length of controlPointPositionsTriangle should be 12 * MemoryLayout<Float>.size, not MemoryLayout<Float>.size. Likewise for controlPointsBufferQuad (which should be 16 * MemoryLayout<Float>.size in length).

这篇关于在iPhone 6s的Swift 3中设置Metal的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆