2011-05-24 99 views
3

我有一個在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共享內存)。

回答

3

在我的一個應用程序中,我從NSOpenGLView切換到了CAOpenGLLayer,然後由於後者的更新機制出現了一些問題而最終退出。但是,這與您在此處報告的性能問題不同。

就你而言,我相信你執行圖層內容更新的方式可能是責任。首先,使用NSTimer觸發重繪並不能保證更新事件與顯示器的刷新率保持一致。相反,我建議將CAOpenGLLayer的asynchronous屬性設置爲YES,並使用–canDrawInCGLContext:pixelFormat:forLayerTime:displayTime:來管理更新頻率。這將導致OpenGL層與顯示同步更新,並且會避免您正在執行的上下文鎖定。

這個問題的不利之處(這也是你的NSTimer方法的問題)是CAOpenGLLayer委託回調在主線程上觸發。如果你有阻塞主線程的東西,你的顯示器會凍結。同樣,如果您的OpenGL框架更新需要一段時間,它們可能會導致您的UI響應性下降。

這就是我使用CVDisplayLink在後臺線程上產生觸發的OpenGL內容更新的原因。不幸的是,當用這個更新我的CAOpenGLLayer時,我看到了一些渲染構件,所以我最終切換回到NSOpenGLView。從那以後,我遇到了一種可能避免這些工件的方法,但NSOpenGLView對我們的需求已經很好,所以我沒有再次切換回來。

+0

對三個App *函數的調用預計會從同一個線程運行,所以我不能真正利用從繪圖線程在不同線程中運行邏輯更新的可能性,反正它就是這樣爲NSOpenGLView示例。我會嘗試使用CanDrawInCGLContext:pixelFormat:forLayerTime:displayTime:和異步方法,會告訴它是否有幫助 – pqnet 2011-05-25 09:32:50

+0

@pqnet - 對於改變場景的某些交互項,我使用了一個標誌,被修改,然後在渲染回調被觸發時讀取該標誌。然後我會在重新繪製之前更新場景。最近,我已經使用GCD來更新OpenGL上下文,同時阻止在多線程上同時訪問該上下文。我在這裏回答一些關於它在我的答案:http://stackoverflow.com/questions/5944050/cadisplaylink-opengl-rendering-breaks-uiscrollview-behaviour/5956119#5956119 – 2011-05-25 15:03:13

+0

@BradLarson - 我遇到了這個答案,因爲我正在尋找一種方法來從後臺線程中獲取CAOpenGLLayer'。你提到你已經找到了一種方法來使用具有CAOpenGLLayer的'CVDisplayLink'而不會產生工件。這仍然有效嗎?你有沒有解釋如何實現這一點的參考?謝謝 – Andrea3000 2012-07-10 18:44:41

相關問題