Last active
October 11, 2021 00:43
-
-
Save mike-neko/2ecd87c48e9eb82e095eb4f9620717a0 to your computer and use it in GitHub Desktop.
Tessellation in Metal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import MetalKit | |
class ViewController: UIViewController, MTKViewDelegate { | |
/// StoryBoardに配置済のMTKView | |
@IBOutlet weak var mtkView: MTKView! | |
/// edgeTessellationFactorの設定値 | |
var edgeFactor = Float(2) | |
/// insideTessellationFactorの設定値 | |
var insideFactor = Float(2) | |
var commandQueue: MTLCommandQueue! | |
var computePipeline: MTLComputePipelineState! | |
var renderPipeline: MTLRenderPipelineState! | |
var controlPointsBuffer: MTLBuffer! | |
var tessellationFactorBuffer: MTLBuffer! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
let device = MTLCreateSystemDefaultDevice()! | |
commandQueue = device.makeCommandQueue()! | |
let library = device.makeDefaultLibrary()! | |
// MTKViewの初期設定 | |
mtkView.sampleCount = 4 | |
mtkView.depthStencilPixelFormat = .depth32Float_stencil8 | |
mtkView.colorPixelFormat = .bgra8Unorm | |
mtkView.clearColor = MTLClearColorMake(0.3, 0.3, 0.3, 1) | |
mtkView.device = device | |
mtkView.delegate = self | |
// MTLVertexDescriptorの設定 | |
let vertexDescriptor = MTLVertexDescriptor() | |
vertexDescriptor.attributes[0].format = .float4 | |
vertexDescriptor.attributes[0].offset = 0 | |
vertexDescriptor.attributes[0].bufferIndex = 0 | |
vertexDescriptor.layouts[0].stride = MemoryLayout<float4>.stride | |
vertexDescriptor.layouts[0].stepFunction = .perPatchControlPoint | |
// MTLRenderPipelineDescriptorの設定 | |
let renderDescriptor = MTLRenderPipelineDescriptor() | |
renderDescriptor.vertexDescriptor = vertexDescriptor | |
renderDescriptor.sampleCount = mtkView.sampleCount | |
renderDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat | |
renderDescriptor.vertexFunction = library.makeFunction(name: "tessellationTriangleVertex") | |
renderDescriptor.fragmentFunction = library.makeFunction(name: "colorFragment") | |
renderDescriptor.depthAttachmentPixelFormat = mtkView.depthStencilPixelFormat | |
renderDescriptor.stencilAttachmentPixelFormat = mtkView.depthStencilPixelFormat | |
renderDescriptor.isTessellationFactorScaleEnabled = false | |
renderDescriptor.tessellationFactorFormat = .half | |
renderDescriptor.tessellationControlPointIndexType = .none | |
renderDescriptor.tessellationFactorStepFunction = .constant | |
renderDescriptor.tessellationOutputWindingOrder = .clockwise | |
renderDescriptor.tessellationPartitionMode = .fractionalEven | |
renderDescriptor.maxTessellationFactor = 16 // ios | |
// 描画用のRenderPipelineの生成 | |
renderPipeline = try! device.makeRenderPipelineState(descriptor: renderDescriptor) | |
// ControlPointの位置をMTLBufferへ設定 | |
let positions = [ | |
float4(arrayLiteral: -0.8, -0.8, 0.0, 1.0), | |
float4(arrayLiteral: 0.0, 0.8, 0.0, 1.0), | |
float4(arrayLiteral: 0.8, -0.8, 0.0, 1.0), | |
] | |
controlPointsBuffer = positions.withUnsafeBufferPointer { | |
device.makeBuffer(bytes: $0.baseAddress!, | |
length: positions.count * MemoryLayout<float4>.size, | |
options: .storageModeShared) // ios | |
} | |
// TessellationFactors設定用のComputePipelineの生成 | |
let kernel = library.makeFunction(name: "tessellationFactorsCompute")! | |
computePipeline = try! device.makeComputePipelineState(function: kernel) | |
// TessellationFactors用のMTLBufferを作成 | |
tessellationFactorBuffer = device.makeBuffer(length: MemoryLayout<MTLTriangleTessellationFactorsHalf>.size, | |
options: .storageModePrivate) | |
// 描画開始 | |
mtkView.draw() | |
} | |
override func didReceiveMemoryWarning() { | |
super.didReceiveMemoryWarning() | |
// Dispose of any resources that can be recreated. | |
} | |
func draw(in view: MTKView) { | |
let commandBuffer = commandQueue.makeCommandBuffer()! | |
// TessellationFactors設定用のComputeKernel | |
let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()! | |
computeCommandEncoder.setComputePipelineState(computePipeline) | |
var factor = float2(edgeFactor, insideFactor) | |
withUnsafePointer(to: &factor) { | |
computeCommandEncoder.setBytes($0, length: MemoryLayout<float2>.size, index: 0) | |
} | |
computeCommandEncoder.setBuffer(tessellationFactorBuffer, offset: 0, index: 1) | |
computeCommandEncoder.dispatchThreadgroups(MTLSize(width: 1, height: 1, depth: 1), | |
threadsPerThreadgroup: MTLSize(width: 1, height: 1, depth: 1)) | |
computeCommandEncoder.endEncoding() | |
// 描画 | |
let renderDescriptor = mtkView.currentRenderPassDescriptor! | |
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderDescriptor)! | |
renderEncoder.setRenderPipelineState(renderPipeline) | |
renderEncoder.setTriangleFillMode(.lines) // ワイヤーフレームで描画 | |
renderEncoder.setVertexBuffer(controlPointsBuffer, offset: 0, index: 0) | |
renderEncoder.setTessellationFactorBuffer(tessellationFactorBuffer, offset: 0, instanceStride: 0) | |
renderEncoder.drawPatches(numberOfPatchControlPoints: 3, patchStart: 0, patchCount: 1, patchIndexBuffer: nil, patchIndexBufferOffset: 0, instanceCount: 1, baseInstance: 0) | |
renderEncoder.endEncoding() | |
commandBuffer.present(mtkView.currentDrawable!) | |
commandBuffer.commit() | |
} | |
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
LGTM