在重绘之前调整MTKView的大小可以缩放旧内容

时间:2017-07-28 14:05:49

标签: macos core-animation metal

我使用MTKView绘制金属内容。它的配置如下:

    mtkView = MTKView(frame: self.view.frame, device: device)
    mtkView.colorPixelFormat = .bgra8Unorm
    mtkView.delegate=self
    mtkView.sampleCount=4
    mtkView.isPaused=true
    mtkView.enableSetNeedsDisplay=true

setFrameSize被覆盖以触发重新显示。

每当视图调整大小时,它会在重绘所有内容之前缩放其旧内容。这给人一种抖动感。

我尝试将MTKView图层的contentGravity属性设置为非调整大小的值,但这完全会弄乱内容的比例和位置。看来MTKView并不想让我摆弄那个参数。

如何确保在调整大小期间始终正确地重新绘制内容?

3 个答案:

答案 0 :(得分:1)

我在调整视图大小时遇到​​了同样的问题。您甚至可以从Apple开发人员站点的HelloTriangle示例中重现它。但是,由于三角形是在屏幕中间附近绘制的,因此效果最小化,因为三角形是最靠近窗口边缘,与拖动的角相对的内容,效果最差。开发人员关于presentsWithTransactionwaitUntilScheduled的使用说明对我也不起作用。

我的解决方案是在window.contentView.layer下添加一个Metal层,并使该层足够大以至于几乎不需要调整其大小。之所以起作用,是因为与window.contentView.layer不同,enabled会自动根据视图调整大小(从而保持窗口大小),而您可以明确控制子层的大小。这样可以消除闪烁。

答案 1 :(得分:1)

在使用Metal和MTKView时,我尝试了presentsWithTransactionwaitUntilScheduled的各种组合,但均未成功。在实时调整大小时,我仍然偶尔会在适当渲染的帧之间遇到拉伸内容的帧。

最后,我完全放弃了MTKView,并使自己的使用CAMetalLayer并调整大小的NSView子类现在看起来不错(不使用presentsWithTransactionwaitUntilScheduled)。关键一点是,我需要设置图层的autoresizingMask才能使displayLayer方法在窗口调整大小期间每一帧都被调用。

这是头文件:

#import <Cocoa/Cocoa.h>

@interface MyMTLView : NSView<CALayerDelegate>    
@end

这是实现:

#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

作为参考,我使用MTKView的drawRect方法绘制了所有金属图。

答案 2 :(得分:0)

这对我有帮助-https://github.com/trishume/MetalTest

他使用MetalLayer并仔细设置了各种属性。即使在45百万像素图像的同步滚动视图中并排两个视图,一切也都非常平滑。

指向我原来的问题How do I position an image correctly in MTKView?的链接