簡體   English   中英

正確的方法來不斷重繪Metal NSView

[英]The right way to make a continuously redrawn Metal NSView

我正在學習Metal和Cocoa,並嘗試將樣板應用程序作為未來實驗的平台。 作為這個過程的一部分,我正在實現一個視圖,它將以60fps重繪自身(或更准確地說,它的CAMetalLayer內容)。 同樣出於教育目的,我避免使用MTKView (用於“學習可可部分”)。 這是我如何處理問題的縮寫代碼片段:

@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;
}

這是做我想要的正確方法嗎? 或者我選擇了一個長期不推薦的方法?

強烈建議使用CVDisplayLink而不是通用的NSTimer來驅動需要匹配顯示器刷新率的動畫。

您需要創建一個ivar或屬性來保存CVDisplayLinkRef

CVDisplayLinkRef displayLink;

然后,當您的視圖進入屏幕並且您想要開始制作動畫時,您將創建,配置和啟動顯示鏈接:

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

顯示鏈接回調應該是靜態函數。 它將在顯示器的v-blank周期開始時調用(在沒有物理空白的現代顯示器上,這仍然以常規的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);

在@warrenm解決方案之后添加dispatch_sync以刷新和其他次要:

#import "imageDrawer.h"
#import "image/ImageBuffer.h"
#import "common.hpp"

@implementation imageDrawer {
    CVDisplayLinkRef displayLink;
}

CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
    dispatch_sync(dispatch_get_main_queue(), ^{
        [(__bridge imageDrawer*)displayLinkContext setNeedsDisplay:YES];
    });
    return kCVReturnSuccess;
}

-(void)setContDisplay {
    CVDisplayLinkCreateWithActiveCGDisplays(&displayLink);
    CVDisplayLinkSetOutputCallback(displayLink, &MyDisplayLinkCallback, (__bridge void*)self);
    CVDisplayLinkStart(displayLink);
}

-(void)awakeFromNib {

    [self setContDisplay];
}

- (void)drawRect:(NSRect)rect {
    [super drawRect:rect];

    int w=rect.size.width, h=rect.size.height;
    // do the drawing...
}

@end

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM