[英]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.