I'm using a MTKView to draw Metal content. It's configured as follows:
mtkView = MTKView(frame: self.view.frame, device: device)
mtkView.colorPixelFormat = .bgra8Unorm
mtkView.delegate = self
mtkView.sampleCount = 4
mtkView.isPaused = true
mtkView.enableSetNeedsDisplay = true
setFrameSize
is overriden to trigger a redisplay.
Whenever the view resizes it scales its old content before it redraws everything. This gives a jittering feeling.
I tried setting the contentGravity
property of the MTKView's layer to a non-resizing value, but that totally messes up the scale and position of the content. It seems MTKView doesn't want me to fiddle with that parameter.
How can I make sure that during a resize the content is always properly redrawn?
In my usage of Metal and MTKView
, I tried various combinations of presentsWithTransaction
and waitUntilScheduled
without success. I still experienced occasional frames of stretched content in between frames of properly rendered content during live resize.
Finally, I dropped MTKView
altogether and made my own NSView subclass that uses CAMetalLayer
and resize looks good now (without any use of presentsWithTransaction
or waitUntilScheduled
). One key bit is that I needed to set the layer's autoresizingMask
to get the displayLayer
method to be called every frame during window resize.
Here's the header file:
#import <Cocoa/Cocoa.h>
@interface MyMTLView : NSView<CALayerDelegate>
@end
Here's the implementation:
#import <QuartzCore/CAMetalLayer.h>
#import <Metal/Metal.h>
@implementation MyMTLView
- (id)initWithFrame:(NSRect)frame
{
if (!(self = [super initWithFrame:frame])) {
return self;
}
// We want to be backed by a CAMetalLayer.
self.wantsLayer = YES;
// We want to redraw the layer during live window resize.
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize;
// Not strictly necessary, but in case something goes wrong with live window
// resize, this layer placement makes it more obvious what's going wrong.
self.layerContentsPlacement = NSViewLayerContentsPlacementTopLeft;
return self;
}
- (CALayer*)makeBackingLayer
{
CAMetalLayer* metalLayer = [CAMetalLayer layer];
metalLayer.device = MTLCreateSystemDefaultDevice();
metalLayer.delegate = self;
// *Both* of these properties are crucial to getting displayLayer to be
// called during live window resize.
metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable;
metalLayer.needsDisplayOnBoundsChange = YES;
return metalLayer;
}
- (CAMetalLayer*)metalLayer
{
return (CAMetalLayer*)self.layer;
}
- (void)setFrameSize:(NSSize)newSize
{
[super setFrameSize:newSize];
self.metalLayer.drawableSize = newSize;
}
- (void)displayLayer:(CALayer*)layer
{
// Do drawing with Metal.
}
@end
For reference, I do all my Metal drawing in MTKView's drawRect
method.