2011-05-24 149 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

相关问题