我正在学习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;
}
这是做我想要的正确方法吗?或者我选择了一个长期而不推荐的方法?
答案 0 :(得分:2)
强烈建议使用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);
答案 1 :(得分:0)
在@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