drawRect表现

时间:2011-05-20 08:04:30

标签: performance ios ipad drawrect

我需要在iPad上绘制50万到100万的多边形。经过实验,我只能获得1 fps。这只是一个例子,我的真实代码有一些大小合适的多边形。

以下是一些问题:

  1. 为什么我不必将Quartz Framework添加到我的项目中?
  2. 如果许多多边形重复,我可以将其用于视图,还是太重等?
  3. 任何替代方案,QTPaint都可以处理这个问题但是会进入gpu。有没有像QT或ios这样的东西?
  4. Opengl可以提高此类型的2d性能吗?
  5. 示例drawrect:

    //X Y Array of boxes
    
    - (void)drawRect:(CGRect)rect
    {
        int reset = [self pan].x;
        int markX = reset;
        int markY = [self pan].y;
        CGContextRef context = UIGraphicsGetCurrentContext();
        for(int i = 0; i < 1000; i++)//1,000,000
        {
            for(int j = 0; j < 1000; j++)
            {
                CGContextMoveToPoint(context, markX,  markY);
                CGContextAddLineToPoint(context, markX, markY + 10);
                CGContextAddLineToPoint(context, markX + 10, markY + 10);
                CGContextAddLineToPoint(context, markX + 10, markY);
                CGContextAddLineToPoint(context, markX, markY);
                CGContextStrokePath(context);
                markX+=12;
            }
            markY += 12;
            markX = reset;
        }
    
    }
    

    平移只是用平移手势在屏幕上移动框数组。任何帮助或提示都会非常感激。

5 个答案:

答案 0 :(得分:18)

您的示例的关键问题是它未经过优化。每当调用drawRect:时,设备都会渲染所有1,000,000个方格。更糟糕的是,它正在循环中对这些API进行6,000,000次调用。如果你想以一个适度的30fps刷新这个视图,那就是180,000,000次/秒。

使用“简单”示例,绘制区域的大小为12,000px×12,000px;您可以在iPad显示屏上显示的最大区域为768×1024(假设是全屏肖像)。因此,代码在可见区域之外浪费了大量CPU资源。 UIKit可以相对轻松地处理这种情况。

管理明显大于可见区域的内容时,应将绘图限制为仅显示的内容。 UIKit有两种处理方式; UIScrollView与CATiledLayer支持的视图相结合是您最好的选择。

步骤:

免责声明:这是上述示例代码的优化

  • 创建新的基于视图的应用程序 iPad项目
  • 添加对QuartzCore.framework的引用
  • 创建一个新类,比如说MyLargeView,从UIView创建子类并添加以下代码:

#import <QuartzCore/QuartzCore.h>

@implementation MyLargeView
- (void)awakeFromNib {
    CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
    tiledLayer.tileSize = CGSizeMake(512.0f, 512.0f);
}

// Set the layer's class to be CATiledLayer.
+ (Class)layerClass {
    return [CATiledLayer class];
}

- (void)drawRect:(CGRect)rect {
    // Drawing code
    // only draws what is specified by the rect parameter

    CGContextRef context = UIGraphicsGetCurrentContext();

    // set up some constants for the objects being drawn        
    const CGFloat width  = 10.0f;           // width of rect
    const CGFloat height = 10.0f;           // height of rect
    const CGFloat xSpace = 4.0f;            // space between cells (horizontal)
    const CGFloat ySpace = 4.0f;            // space between cells (vertical)
    const CGFloat tWidth = width + xSpace;  // total width of cell
    const CGFloat tHeight = height + ySpace;// total height of cell

    CGFloat xStart = floorf(rect.origin.x / tWidth);     // first visible cell (column)
    CGFloat yStart = floorf(rect.origin.y / tHeight);    // first visible cell (row)
    CGFloat xCells = rect.size.width / tWidth + 1;       // number of horizontal visible cells
    CGFloat yCells = rect.size.height / tHeight + 1;     // number of vertical visible cells

    for(int x = xStart; x < (xStart + xCells); x++) {
        for(int y = yStart; y < (yStart + yCells); y++) {
            CGFloat xpos = x*tWidth;
            CGFloat ypos = y*tHeight;

            CGContextMoveToPoint(context, xpos,  ypos);
            CGContextAddLineToPoint(context, xpos, ypos + height);
            CGContextAddLineToPoint(context, xpos + width, ypos + height);
            CGContextAddLineToPoint(context, xpos + width, ypos);
            CGContextAddLineToPoint(context, xpos, ypos);
            CGContextStrokePath(context);
        }
    }
}


@end
  • 编辑视图控制器nib并将UIScrollView添加到视图
  • 将UIView添加到UIScrollView并确保它填充UIScrollView
    View hierarchy
  • 将班级更改为MyLargeView
    Change class to MyLargeView
  • 将MyLargeView的帧大小设置为12,000×12,000
    Enter frame size
  • 最后,打开视图控制器.m文件并添加以下覆盖:

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
    [super viewDidLoad];
    UIScrollView *scrollView = [self.view.subviews objectAtIndex:0];
    scrollView.contentSize = CGSizeMake(12000, 12000);
}

如果查看drawRect:调用,它只会进入rect参数指定的区域,这对应于我们配置的CATiledLayer的区块大小(512×512) awakeFromNib方法。这将扩展到1,000,000×1,000,000像素画布。

要查看的替代方法是ScrollViewSuite example,特别是3_Tiling

答案 1 :(得分:3)

OpenGL是iOS设备上的GPU硬件加速。核心图形绘制不是,并且在处理大量小图形基元(线)时可能要慢很多倍。

对于许多小方块,只需将它们写入C代码中的位图比Core Graphics线条绘制更快。然后只需在完成后将位图绘制到视图中。但Open GL会更快。

答案 2 :(得分:0)

要点4. OpenGL应该可以。检查您是否可以重用这些对象,以及是否可以将某些逻辑移动到GLSL代码。

OpenGL性能优化(在WebGL的上下文中,但大多数应该适用):http://www.youtube.com/watch?v=rfQ8rKGTVlg

答案 3 :(得分:0)

我不知道iOS历史记录的详细信息,因此在首次发布问题时这可能不是一个选项。但是,在处理路径性能问题时,我想将CAShapeLayer称为一个简单的选项。 &#34; iOS核心动画:先进技术&#34; (在Google Books上找到它)说CAShapeLayer&#34;使用硬件加速绘图&#34;我认为这意味着它是基于GPU的实现。同一本书在第6章中有一个很好的用法例子,可以归结为:

  1. 创建CAShapeLayer
  2. 配置其lineWidth,fillColor,strokeColor等
  3. 将图层添加为视图的containerView.layer
  4. 的子图层
  5. 要绘制路径,只需将其设置为图层&#34;路径&#34;属性
  6. 通过Instruments测量,这在我的应用程序中产生了巨大的性能差异。如果您的性能问题是基于路径的,那么在您尝试使用CAShapeLayer之前,请不要进入OpenGL。

答案 4 :(得分:0)

我遇到了同样的问题。经过无休止的谷歌搜索,CAShapeLayer终于救了我!以下是您需要执行的详细步骤:

  1. 通过覆盖UIView&#39; s +(Class)layerClass方法
  2. 创建一个包含CAShapeLayer视图的图层类型的视图
  3. 配置图层的lineWidth,fillColor,strokeColor等
  4. 创建UIBezierPath实例
  5. 要绘制路径,请在完成绘制后使用UIBezierPath实例添加线条,曲线或acr等,只需将bezierPath.CGPath设置为 图层&#34;路径&#34;属性
  6. 这是一个简单的演示,当您触摸演示视图时绘制一条简单的曲线:

    //Simple ShapelayerView.m
    
    -(instancetype)init {
        self = [super init];
        if (self) {
        _bezierPath = [UIBezierPath bezierPath];
        CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
    
        shapeLayer.lineWidth = 5;
        shapeLayer.lineJoin = kCALineJoinRound;
        shapeLayer.lineCap = kCALineCapRound;
        shapeLayer.strokeColor = [UIColor yellowColor].CGColor;
        shapeLayer.fillColor = [UIColor blueColor].CGColor;
        }
    
        return self;
    }
    
    + (Class)layerClass {
        return [CAShapeLayer class];
    }
    
    - (void) customDrawShape {
        CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
        [_bezierPath removeAllPoints];
    
        [_bezierPath moveToPoint:CGPointMake(10, 10)];
        [_bezierPath addQuadCurveToPoint:CGPointMake(2, 2) controlPoint:CGPointMake(50, 50)];
    
        shapeLayer.path = _bezierPath.CGPath;
    }
    
    - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
        [super touchesBegan:touches withEvent:event];
        [self customDrawShape];
    }