2011-03-10 77 views
0

任何人都可以解释为什么重绘到离线CGLayer会导致渲染随着时间的推移而变慢?让我给你展示一个我为了说明问题而创建的测试。CGLayer绘图随着时间的推移变慢

@implementation DrawView 


- (id)initWithFrame:(CGRect)frame { 
    self = [super initWithFrame:frame]; 
    if (self) { 
     //setup frame rate monitoring 
     fps = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 20)]; 
     fps.textColor = [UIColor whiteColor]; 
     fps.font = [UIFont boldSystemFontOfSize:15]; 
     fps.text = @"0 fps"; 
     [self addSubview:fps]; 
     frames = 0; 
     lastRecord = [NSDate timeIntervalSinceReferenceDate]; 

     //create a cglayer and draw the background graphic to it 
     CGContextRef context = UIGraphicsGetCurrentContext(); 
     cacheLayer = CGLayerCreateWithContext(context, self.bounds.size, NULL); 

     CGImageRef background = [[UIImage imageNamed:@"background.jpg"] CGImage]; 
     CGContextRef cacheContext = CGLayerGetContext(cacheLayer); 
     CGContextDrawImage(cacheContext, CGRectMake(0, 0, 768, 1024), background); 

     //initialize cgimage stamp 
     stamp = [[UIImage imageNamed:@"stamp.png"] CGImage]; 
     stampTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/60 target:self selector:@selector(stamp) userInfo:nil repeats:YES]; 
    } 
    return self; 
} 

- (void) stamp { 
    //calculate fps 
    NSTimeInterval interval = [NSDate timeIntervalSinceReferenceDate]; 
    NSTimeInterval diff = interval-lastRecord; 
    if (diff > 1.0) { 
     float rate = frames/diff; 
     frames = 0; 
     lastRecord = [NSDate timeIntervalSinceReferenceDate]; 
     fps.text = [NSString stringWithFormat:@"%0.1f fps", rate]; 
    } 
    //stamp the offscreen cglayer with the cgimage graphic 
    CGRect stampRect = CGRectMake(0, 0, 200, 200); 
    CGContextRef cacheContext = CGLayerGetContext(cacheLayer); 
    CGContextDrawImage(cacheContext, stampRect, stamp); 
    [self setNeedsDisplayInRect:stampRect]; 
} 

- (void)drawRect:(CGRect)dirtyRect { 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextDrawLayerInRect(context, self.bounds, cacheLayer); 
    frames++; 
} 

当运行该测试在ipad的模拟器或设备它开始于40 fps的,并以恒定的速率在10秒的过程中,直到它在像3 fps的运行下降。这是为什么发生?不应该以恒定的帧率运行吗?什么样的解决方案可以让我一次又一次地“印”图像,同时保持恒定的帧率?

+0

+1我通过在初始drawRect调用中创建CGLayer来解决同样的问题。 – 9dan 2011-10-12 09:31:01

+0

对于这个惊人的问题的全面和权威性调查,请查看所有这些包括所有意见: http://stackoverflow.com/questions/4739748/is-there-a-way-to-make-drawrect-工作 - 右 - 现在! ,享受这包括所有的样本项目: http://stackoverflow.com/questions/4786754/mysterious-progressive-slowing-problem-in-run-loop-drawrect 和提供的各种样品溶液项目参与者们。 (注意:很多早期的评论是完全错误的,所以请仔细阅读。) 这真是太神奇了...... Felz是一位真正解决它的家伙。他摇滚。 – Fattie 2016-04-22 19:12:04

回答

4

你的问题是你创建了一个NULL上下文的图层。 UIGraphicsGetCurrentContext()仅在绘制循环中有效。你把它叫做draw循环外部,所以它是NULL,所以图层不能缓存任何东西。它仍然令我感到惊讶,随着时间的推移,这种表现会如何糟糕。我怀疑CoreGraphics中可能存在一个错误;你会认为这会很慢;不会“放慢速度”。但是,它的设计目的不是这样。

如果您创建位图上下文并将其用于图层,则会获得60fps。你不需要在这里放弃CGLayer。

我所做的一切,回到60fps的是替换这样的:

CGContextRef context = UIGraphicsGetCurrentContext(); 

与此:

CGContextRef context = CreateBitmapContext(self.bounds.size); 

哪里CreateBitmapContext()是最多返回同样的事情你createBitmapContext集的功能。

+0

感谢您的更新。我很好奇,如果我现在拥有自己的位图上下文,使用CGLayer有什么好处吗? – seanalltogether 2011-03-10 04:36:18

+0

阅读CGLayer参考介绍,了解使用CGLayer的性能优势。请注意,通常CGLayer不适合离屏缓冲。 Quartz自己处理这个问题,如果这就是你为什么要这样做的话,那么你就是在为这个问题而高度优化Quartz。 – 2011-03-10 18:49:12

+0

@Joe Blow,我想这取决于你认为直观的东西。颠覆优化绘图系统并递归重入中间导致性能问题的事实应该不令人意外。强制“立即绘制”可能会绘制隐藏或隐藏的视图,这是浪费的,并会与动画优化作斗争。 UIKit通常不使用画布对象,而这正是您试图重新创建的对象。但是,您可以通过绘制到作为图层内容的位图上下文来轻松创建画布对象。它不需要runloop的花哨技巧。 – 2011-03-29 15:05:18

0

我发现了这个解决方案,虽然看起来与直觉相反,但创建一个缓存的位图上下文并且然后从中复制一个图像以绘制到当前上下文会更快。

- (void) createBitmapContext { 
    // Create the bitmap context 
    void *   bitmapData; 
    int    bitmapByteCount; 
    int    bitmapBytesPerRow; 
    CGSize   size = self.bounds.size; 

    // Declare the number of bytes per row. Each pixel in the bitmap in this 
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and 
    // alpha. 
    bitmapBytesPerRow = (size.width * 4); 
    bitmapByteCount  = (bitmapBytesPerRow * size.height); 

    // Allocate memory for image data. This is the destination in memory 
    // where any drawing to the bitmap context will be rendered. 
    bitmapData = malloc(bitmapByteCount); 
    if (bitmapData == NULL) 
    { 
     //return nil; 
    } 
    cacheContext = CGBitmapContextCreate (bitmapData, size.width, size.height,8,bitmapBytesPerRow, 
              CGColorSpaceCreateDeviceRGB(),kCGImageAlphaNoneSkipFirst); 
} 


- (id)initWithFrame:(CGRect)frame { 
    self = [super initWithFrame:frame]; 
    if (self) { 
     //setup frame rate monitoring 
     fps = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, 100, 20)]; 
     fps.textColor = [UIColor whiteColor]; 
     fps.font = [UIFont boldSystemFontOfSize:15]; 
     fps.text = @"0 fps"; 
     [self addSubview:fps]; 
     frames = 0; 
     lastRecord = [NSDate timeIntervalSinceReferenceDate]; 

     //create a bitmap context and draw the background graphic to it 
     [self createBitmapContext]; 

     CGImageRef background = [[UIImage imageNamed:@"background.jpg"] CGImage]; 
     CGContextDrawImage(cacheContext, CGRectMake(0, 0, 768, 1024), background); 

     //initialize cgimage stamp 
     stamp = [[UIImage imageNamed:@"stamp.png"] CGImage]; 
     stampTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/60 target:self selector:@selector(stamp) userInfo:nil repeats:YES]; 
    } 
    return self; 
} 

- (void) stamp { 
    //calculate fps 
    NSTimeInterval interval = [NSDate timeIntervalSinceReferenceDate]; 
    NSTimeInterval diff = interval-lastRecord; 
    if (diff > 1.0) { 
     float rate = frames/diff; 
     frames = 0; 
     lastRecord = [NSDate timeIntervalSinceReferenceDate]; 
     fps.text = [NSString stringWithFormat:@"%0.1f fps", rate]; 
    } 
    //stamp the offscreen bitmap context with the cgimage graphic 
    CGRect stampRect = CGRectMake(0, 0, 200, 200); 
    CGContextDrawImage(cacheContext, stampRect, stamp); 
    [self setNeedsDisplayInRect:stampRect]; 
} 

- (void)drawRect:(CGRect)dirtyRect { 
    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGImageRef cacheImage = CGBitmapContextCreateImage(cacheContext); 
    CGContextDrawImage(context, self.bounds, cacheImage); 
    CGImageRelease(cacheImage); 
    frames++; 
} 
相关问题