Can anyone explain why redrawing to an offscreen CGLayer would cause rendering to slow down over time? Let me show you a test I've created to illustrate the problem.
@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++;
}
When I run this test in the ipad simulator or device it starts at 40 fps and drops at a constant rate over the course of 10 seconds until it's running at like 3 fps. Why is this happening? Shouldn't this run at a constant framerate? What kind of solution would allow me to 'stamp' an image over and over while maintaining a constant framerate?
Your problem is that you created your layer with a NULL context. UIGraphicsGetCurrentContext()
is only valid during the draw loop. You call it outside the draw loop so it's NULL and so the layer can't cache anything. It still surprises me how badly this trashes performance over time. I suspect there might be a bug in CoreGraphics; you'd think this would just be slow; not "ever slowing." But still, it's not designed to work this way.
If you create a bitmap context and use that for your layer, you'll get your 60fps. You don't need to abandon CGLayer here.
All I did to get back to 60fps is to replace this:
CGContextRef context = UIGraphicsGetCurrentContext();
with this:
CGContextRef context = CreateBitmapContext(self.bounds.size);
Where CreateBitmapContext()
is a function that returns the same thing your createBitmapContext
sets up.