2016-06-13 54 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'只有**時間表**視圖將來有時重新繪製,這會引入額外的延遲,不是嗎? –