swiftmetalmetalkitmultisampling

How to use multisampling with an MTKView?


I'm trying to get multisampling working with MTKView. I have an MTKView with a delegate. I set the view's sampleCount property to 4. I create a pipeline state descriptor with the rasterSampleCount set to 4, and use that to make a render pipeline state that I use when rendering.

In the delegate's draw(in:) method, I create a render pass descriptor by getting the view's current render pass descriptor and setting the storeAction to multisampleResolve. I've also set tried storeAndMultisampleResolve to no avail.

I have created a resolve texture for the render pass descriptor, and it is the same width and height as the view and the same pixel format.

Given the above, I get a full red frame during rendering. I have used the metal debugger to look at the textures, and both the view's texture and the resolve texture have the correct rendering in them. I'm on an AMD machine where a fully red texture often indicates an uninitialized texture.

Is there anything I need to do to get the rendering to go to the screen?

Here's how I'm setting up the view, pipeline state, and resolve texture:

    metalView = newMetalView
    metalView.sampleCount = 4
    metalView.clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
    device = newMetalView.device!

    let metalLibrary = device.makeDefaultLibrary()!
    let vertexFunction =  metalLibrary.makeFunction(name: "vertexShader")
    let fragmentFunction = metalLibrary.makeFunction(name: "fragmentShader")

    let pipelineStateDescriptor = MTLRenderPipelineDescriptor.init()
    pipelineStateDescriptor.label = "Particle Renderer"
    pipelineStateDescriptor.vertexFunction = vertexFunction
    pipelineStateDescriptor.fragmentFunction = fragmentFunction
    pipelineStateDescriptor.colorAttachments [ 0 ].pixelFormat = metalView.colorPixelFormat
    pipelineStateDescriptor.rasterSampleCount = 4

    do {
        try pipelineState = device.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
    } catch {
        NSLog("Unable to create pipeline state")
        pipelineState = nil
    }

    let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: metalView.colorPixelFormat, width: Int(metalView.bounds.width), height: Int(metalView.bounds.height), mipmapped: false)
    resolveTexture = device.makeTexture(descriptor: textureDescriptor)!

And here's how I'm drawing:

    let commandBuffer = commandQueue.makeCommandBuffer()
    commandBuffer?.label = "Partcle Command Buffer"
    let renderPassDescriptor = metalView.currentRenderPassDescriptor
    renderPassDescriptor?.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 0.0)
    renderPassDescriptor?.colorAttachments[0].loadAction = MTLLoadAction.clear
    renderPassDescriptor?.colorAttachments[0].storeAction = MTLStoreAction.multisampleResolve
    renderPassDescriptor?.colorAttachments[0].resolveTexture = resolveTexture

    let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
    renderEncoder?.label = "Particle Render Encoder"
    renderEncoder?.setViewport(MTLViewport(originX: 0.0, originY: 0.0, width: Double(viewportSize.x), height: Double(viewportSize.y), znear: -1.0, zfar: 1.0))
    renderEncoder?.setRenderPipelineState(pipelineState!);

Then I make my draw calls, and then finish up by calling:

    renderEncoder?.endEncoding()
    commandBuffer?.present(metalView.currentDrawable!)
    commandBuffer?.commit()

Here's what the debugger shows is in my textures:

enter image description here

Oddly, while doing that debugging, I accidentally hid Xcode, and for 1 frame, the view showed the correct texture.


Solution

  • What's the initial configuration of renderPassDescriptor (as returned from metalView.currentRenderPassDescriptor?

    I believe you want the color attachment's texture set to metalView.multisampleColorTexture and its resolveTexture set to metalView.currentDrawable.texture. That is, it should do the primary, multi-sampled rendering to the multi-sample texture and then that gets resolved to the drawable texture to actually draw it in the view.

    I don't know if MTKView sets up its currentRenderPassDescriptor like that automatically when there's a sampleCount > 1. Ideally, it would.