2016-06-13 22 views
1

我正在学习金属和可可,并试图做一个样板应用程序作为未来实验的平台。作为这个过程的一部分,我正在实现一个视图,它将以60fps的速度重绘自己(或者更准确地说,它的CAMetalLayer的内容)。也为了教育目的我避免MTKView(“学习可可部分”)。以下是我如何解决问题的缩写代码片段:正确的方法,使连续重绘金属NSView

@implementation MyMetalView // which is a subclass of NSView 

- (BOOL) isOpaque { 
    return YES; 
} 

- (NSViewLayerContentsRedrawPolicy) layerContentsRedrawPolicy { 
    return NSViewLayerContentsRedrawOnSetNeedsDisplay; 
} 

- (CALayer *) makeBackingLayer { 
    // create CAMetalLayer with default device 
} 

- (BOOL) wantsLayer { 
    return YES; 
} 

- (BOOL) wantsUpdateLayer { 
    return YES; 
} 

- (void) displayLayer:(CALayer *)layer { 
    id<MTLCommandBuffer> cmdBuffer = [_commandQueue commandBuffer]; 
    id<CAMetalDrawable> drawable = [((CAMetalLayer *) layer) nextDrawable]; 

    [cmdBuffer enqueue]; 
    [cmdBuffer presentDrawable:drawable]; 

    // rendering 

    [cmdBuffer commit]; 
} 

@end 

int main() { 
    // init app, window and MyMetalView instance 

    // invocation will call [myMetalViewInstance setNeedsDisplay:YES] 
    [NSTimer scheduledTimerWithTimeInterval:1./60. invocation:setNeedsDisplayInvokation repeats:YES]; 

    [NSApp run]; 
    return 0; 
} 

这是正确的方式来做我想做的事吗?或者我选择了一个很长而不被推荐的方法?

回答

1

强烈建议使用CVDisplayLink而不是普通的NSTimer来驱动需要匹配显示器刷新率的动画。

你想创建一个实例变量或者财产持有CVDisplayLinkRef

CVDisplayLinkRef displayLink; 

然后,当你的看法会在屏幕上,你要开始动画,你会创建,配置,并开始显示链接:

CVDisplayLinkCreateWithActiveCGDisplays(&displayLink); 
CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, self); 
CVDisplayLinkStart(displayLink); 

显示链接回调应该是一个静态函数。它会在显示器的V空白期间的开头被调用(现代显示在没有物理V空白,这还是发生在一个普通60Hz的节奏):

static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) 
{ 

    [(MyMetalView *)displayLinkContext setNeedsDisplay:YES]; 
    return kCVReturnSuccess; 
} 

当你的观点离开显示器,或者如果你想暂停,你可以释放显示链接并删除它:

CVDisplayLinkRelease(displayLink); 
+0

我可以直接在MyDisplayLinkCallback中调用我的渲染代码吗? –

+0

我个人会将我的图画留在我的'updateLayer'实现或'updateLayer'所​​调用的方法中。最好是使用回调函数作为反弹回视图实现的一种方式,而不是在整个类实现和回调(在更大程度上超过必要)的情况下分解视图的代码。 – warrenm

+0

我的意思是从回调而不是'setNeedsDisplay:YES'调用视图的'updateLayer'。如果我理解正确,'setNeedsDisplay:YES'只有**时间表**视图将来有时重新绘制,这会引入额外的延迟,不是吗? –