CGLayer绘图随着时间的推移而减慢

时间:2011-03-10 02:04:26

标签: objective-c ios core-graphics quartz-graphics

有人可以解释为什么重绘到屏幕外的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的速度运行。为什么会这样?这不应该以恒定的帧速率运行吗?什么样的解决方案可以让我在保持恒定帧率的同时反复“标记”图像?

2 个答案:

答案 0 :(得分:5)

您的问题是您使用NULL上下文创建了图层。 UIGraphicsGetCurrentContext()仅在绘制循环期间有效。你在绘制循环之外调用它,所以它是NULL,因此该层不能缓存任何东西。令人惊讶的是,随着时间的推移,这种性能有多糟糕。我怀疑CoreGraphics可能存在错误;你会认为这会很慢;不是“永远放慢”。但是,它仍然不是以这种方式工作的。

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

我所做的只是回到60fps就是取代它:

CGContextRef context = UIGraphicsGetCurrentContext();

用这个:

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

CreateBitmapContext()是一个函数,返回您createBitmapContext设置的相同内容。

答案 1 :(得分: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++;
}