我有一个在Mac OS X上呈现OpenGL内容的应用程序。最初它是渲染到NSOpenGLView,然后我将其更改为渲染到CAOpenGLLayer子类。为什么我的CAOpenGLLayer比我以前的NSOpenGLView更新慢?
当我这样做时,我看到了一个巨大的性能损失:帧率减半,鼠标响应率降低,口吃(时不时停止,最多一秒钟,在此期间探查器活动报告等待互斥量数据加载到GPU内存),并加倍CPU使用率。
我调查这个问题,有几个问题:
- 也有类似的性能损失被视为由其他人?
- 我在做我的CAOpenGLLayer设置有问题吗?
- CAOpenGLLayer和Core Animation框架是如何实现的,也就是说,我的OpenGL内容从我的glDrawElements调用到我的屏幕的路径是什么,以及我应该怎样做,以优化这种设置的性能?
这里是我的CAOpenGLLayer设置代码:
// my application's entry point (can't be easily changed):
void AppUpdateLogic(); //update application logic. Will load textures
void AppRender(); //executes drawing
void AppEventSink(NSEvent* ev); //handle mouse and keyboard events.
//Will do pick renderings
@interface MyCAOpenGLLayer: CAOpenGLLayer
{
CGLPixelFormatObj pixelFormat;
CGLContextObj glContext;
}
@end
@implementation MyCAOpenGLLayer
- (id)init {
self = [super init];
CGLPixelFormatAttribute attributes[] =
{
kCGLPFAAccelerated,
kCGLPFAColorSize, (CGLPixelFormatAttribute)24,
kCGLPFAAlphaSize, (CGLPixelFormatAttribute)8,
kCGLPFADepthSize, (CGLPixelFormatAttribute)16,
(CGLPixelFormatAttribute)0
};
GLint numPixelFormats = 0;
CGLChoosePixelFormat(attributes, &pixelFormat, &numPixelFormats);
glContext = [super copyCGLContextForPixelFormat:mPixelFormat];
return self;
}
- (void)drawInCGLContext:(CGLContextObj)inGlContext
pixelFormat:(CGLPixelFormatObj)inPixelFormat
forLayerTime:(CFTimeInterval)timeInterval
displayTime:(const CVTimeStamp *)timeStamp
{
AppRender();
[super drawInCGLContext:inGlContext
pixelFormat:inPixelFormat
forLayerTime:timeInterval
displayTime:timeStamp ]
}
- (void)releaseCGLPixelFormat:(CGLPixelFormatObj)pixelFormat {
[self release];
}
- (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask {
[self retain];
return pixelFormat;
}
- (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat {
[self retain];
return glContext;
}
- (void)releaseCGLContext:(CGLContextObj)glContext {
[self release];
}
@end
@interface MyMainViewController: NSViewController {
CGLContextObj glContext;
CALayer* myOpenGLLayer;
}
-(void)timerTriggered:(NSTimer*)timer;
@end
@implementation MyMainViewController
-(void)viewDidLoad:(NSView*)view {
myOpenGLLayer = [[MyCAOpenGLLayer alloc] init];
[view setLayer:myOpenGLLayer];
[view setWantsLayer:YES];
glContext = [myOpenGLLayer copyCGLContextForPixelFormat:nil];
[NSTimer scheduledTimerWithTimeInterval:1/30.0
target:self
selector:@selector(timerTriggered:)
userInfo:nil
repeats:YES ];
}
- (void)timerTriggered:(NSTimer*)timer {
CGLContextObj oldContext = CGLContextGetCurrent();
CGLContextSetCurrent(glContext);
CGLContextLock(glContext);
AppUpdateLogic();
[myOpenGLLayer setNeedsDisplay:YES];
CGLContextUnlock(glContext);
CGLContextSetCurrent(oldContext);
}
- (void)mouseDown:(NSEvent*)event {
CGLContextObj oldContext = CGLContextGetCurrent();
CGLContextSetCurrent(glContext);
CGLContextLock(glContext);
AppEventSink(event);
CGLContextUnlock(glContext);
CGLContextSetCurrent(oldContext);
}
@end
这可能是知道我的显卡有用的不是很厉害(英特尔GMA与64 MB共享内存)。
对三个App *函数的调用预计会从同一个线程运行,所以我不能真正利用从绘图线程在不同线程中运行逻辑更新的可能性,反正它就是这样为NSOpenGLView示例。我会尝试使用CanDrawInCGLContext:pixelFormat:forLayerTime:displayTime:和异步方法,会告诉它是否有帮助 – pqnet 2011-05-25 09:32:50
@pqnet - 对于改变场景的某些交互项,我使用了一个标志,被修改,然后在渲染回调被触发时读取该标志。然后我会在重新绘制之前更新场景。最近,我已经使用GCD来更新OpenGL上下文,同时阻止在多线程上同时访问该上下文。我在这里回答一些关于它在我的答案:http://stackoverflow.com/questions/5944050/cadisplaylink-opengl-rendering-breaks-uiscrollview-behaviour/5956119#5956119 – 2011-05-25 15:03:13
@BradLarson - 我遇到了这个答案,因为我正在寻找一种方法来从后台线程中获取CAOpenGLLayer'。你提到你已经找到了一种方法来使用具有CAOpenGLLayer的'CVDisplayLink'而不会产生工件。这仍然有效吗?你有没有解释如何实现这一点的参考?谢谢 – Andrea3000 2012-07-10 18:44:41