I am following the setup for a basic metal tutorial and am trying to render a blank screen of uniform color.
For some reason I am getting this issue where Metal only draws to a part of the screen.
I set up a single view application and attached the MainStoryboard's view to a class I am creating. I set its background color to green.
Here is the class
class MBEMetalView: UIView {
var device:MTLDevice?
var MTLLayer:CAMetalLayer?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
device = MTLCreateSystemDefaultDevice()
//The book said to cast the layer to a CAMetalLayer
//but that causes a crash in the redraw method because
//failed assertion `No rendertargets set in RenderPassDescriptor.'
//So we do this.. which might be wasteful?
MTLLayer = CAMetalLayer()
MTLLayer?.device = device
MTLLayer?.pixelFormat = MTLPixelFormat.bgra8Unorm
MTLLayer?.framebufferOnly = true
MTLLayer?.frame = frame
layer.addSublayer(MTLLayer!)
}
override func didMoveToWindow() {
redraw()
}
func redraw() {
var drawable = MTLLayer?.nextDrawable()
var texture = drawable?.texture
var passDescriptor = MTLRenderPassDescriptor()
passDescriptor.colorAttachments[0].texture = texture
passDescriptor.colorAttachments[0].loadAction = MTLLoadAction.clear
passDescriptor.colorAttachments[0].storeAction = MTLStoreAction.store
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 1, 0, 1)
var commandQueue = device?.makeCommandQueue()
var commandBuffer = commandQueue?.makeCommandBuffer()
var commandEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: passDescriptor)
commandEncoder?.endEncoding()
if let drawOn = drawable {
commandBuffer?.present(drawOn)
commandBuffer?.commit()
}
}
}
The class should have rendered the entire screen green however this is not the case it only fills up a portion as shown by this picture from an iPhone 6s plus (Red is the background of the view not what Metal is drawing).
This result brings to mind a few questions:
.addSublayer
line). However when I try this I get a crash that is
shown in the comments? Why is this?You set the CAMetalLayer
's frame in init(coder)
and never change it. But in init(coder)
the final frame size is not known yet (it's just using whatever size you set in Interface Builder at this point but not the device size yet). In didMoveToWindow()
, also set the frame of the Metal layer to its final size. (Or perhaps in layoutSubviews()
.)