问题
I am using this code to render the "Hello Triangle" triangle. On my iPhone, though, the triangle has very rough edges, not smooth edges, like in the example.
import UIKit
import Metal
import MetalKit
import simd
class MBEMetalView: UIView {
// // // // // MAIN // // // // //
var metalDevice: MTLDevice! = nil
var metalLayer: CAMetalLayer! = nil
var commandQueue: MTLCommandQueue! = nil
var vertexBuffer: MTLBuffer! = nil
var pipelineState: MTLRenderPipelineState! = nil
var displayLink: CADisplayLink! = nil
override class var layerClass : AnyClass {
return CAMetalLayer.self
}
// override func didMoveToWindow() {
// self.redraw()
// }
override func didMoveToSuperview() {
super.didMoveToSuperview()
if self.superview != nil {
self.displayLink = CADisplayLink(target: self, selector: #selector(displayLinkFired))
self.displayLink.add(to: RunLoop.main, forMode: .common)
} else {
self.displayLink.invalidate()
}
}
@objc func displayLinkFired() {
self.redraw()
}
// // // // // INIT // // // // //
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.prepareDeviceLayerAndQueue()
self.makeBuffers()
self.makePipeline()
}
func prepareDeviceLayerAndQueue() {
metalLayer = (self.layer as! CAMetalLayer)
metalDevice = MTLCreateSystemDefaultDevice()
metalLayer.device = metalDevice
metalLayer.pixelFormat = .bgra8Unorm
commandQueue = metalDevice.makeCommandQueue()
}
func makeBuffers() {
var vertices: [MBEVertex] = [
MBEVertex(position: vector_float4(0, 0.5, 0, 1) , color: vector_float4(1, 0, 0, 1)),
MBEVertex(position: vector_float4(-0.5, -0.5, 0, 1) , color: vector_float4(0, 1, 0, 1)),
MBEVertex(position: vector_float4(0.5, -0.5, 0, 1) , color: vector_float4(0, 0, 1, 1))
]
self.vertexBuffer = metalDevice.makeBuffer(bytes: &vertices, length: 56, options: .storageModeShared)
}
func makePipeline() {
guard let library = metalDevice.makeDefaultLibrary() else { print("COULD NOT CREATE LIBRARY") ; return }
guard let vertexFunction = library.makeFunction(name: "vertex_main") else { print("COULD NOT CREATE A VERTEX FUNCTION") ; return }
guard let fragmentFunction = library.makeFunction(name: "fragment_main") else { print("COULD NOT CREATE LIBRARY") ; return }
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = metalLayer.pixelFormat
pipelineState = try? metalDevice.makeRenderPipelineState(descriptor: pipelineDescriptor)
if pipelineState == nil { print("COULD NOT CREATE PIPELINE STATE") ; return }
}
// // // // // FUNCTIONS // // // // //
func redraw() {
guard let drawable = metalLayer.nextDrawable() else { print("COULD NOT CREATE A DRAWABLE") ; return }
let texture = drawable.texture
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = texture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].storeAction = .store
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1)
guard let commandBuffer = commandQueue.makeCommandBuffer() else { print("COULD NOT CREATE A COMMAND BUFFER") ; return }
guard let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { print("COULD NOT CREATE AN ENCODER") ; return }
commandEncoder.setRenderPipelineState(pipelineState)
commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
commandEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
// // // // // TYPES // // // // //
struct MBEVertex {
var position: vector_float4
var color: vector_float4
}
}
I have tried to render the triangle a few different times with different methods (sometimes use a MetalKit view from interface builder, sometimes create the view manually)... each time, though, the triangle comes out with rough edges.
回答1:
The main issue here is that the drawable size of your layer is much smaller than the resolution of your screen. You can get them to match by taking the following steps:
When your Metal view moves to a new superview, update its contentsScale
property to match that of the hosting display:
layer.contentsScale = self.window?.screen.scale ?? 1.0
Add a property to your view subclass that computes the ideal drawable size based on the bounds of the view and its scale:
var preferredDrawableSize: CGSize {
return CGSize(width: bounds.size.width * layer.contentsScale,
height: bounds.size.height * layer.contentsScale)
}
Update the drawableSize
of your layer when you detect that it doesn't match the computed preferred size:
func redraw() {
if metalLayer.drawableSize != preferredDrawableSize {
metalLayer.drawableSize = preferredDrawableSize
}
...
}
By the way, these days there's really no good reason not to use MTKView
for this purpose. It abstracts all of these details for you and is much nicer to work with.
来源:https://stackoverflow.com/questions/57613416/why-is-the-triangle-being-rendered-with-rough-edges-and-not-smooth-edges-metal