我有一個在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