производительность drawRect - PullRequest
5 голосов
/ 20 мая 2011

Мне нужно нарисовать много полигонов от 500К до миллиона на iPad.После экспериментов я могу получить только 1 кадр в секунду, если это так.Это просто пример, в моем реальном коде есть многоугольники хорошего размера.

Вот несколько вопросов:

  1. Почему мне не нужно добавлять Quartz Framework в мой проект?
  2. Если многие из многоугольников повторяются, могу ли я использовать это с представлениями или они слишком тяжелые и т. Д.?
  3. Любые альтернативы, QTPaint может справиться с этим, но погружается в графический процессор.Есть ли что-то вроде QT или ios?
  4. Может ли Opengl увеличить двухмерную производительность этого типа?

Пример 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 ]

18 голосов
/ 15 августа 2011

Ключевая проблема вашего примера в том, что он не оптимизирован. Когда вызывается drawRect:, устройство рендерит все 1 000 000 квадратов. Хуже того, он делает 6 000 000 вызовов этих API в цикле. Если вы хотите обновить это представление даже со скромными 30 кадрами в секунду, то это 180 000 000 вызовов в секунду.

В вашем «простом» примере размер области рисования составляет 12 000 × 12 000 пикселей; максимальная область, которую вы можете отобразить на дисплее iPad, составляет 768 × 1024 (при условии полноэкранного портрета). Поэтому код тратит много ресурсов ЦП за пределы видимой области . У 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
  • Отредактируйте перо контроллера представления и добавьте 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, что будет соответствовать размеру плитки (512 × 512) для CATiledLayer, который мы настроили в awakeFromNib метод. Это масштабируется до холста 1 000 000 × 1 000 000 пикселей.

Альтернативные варианты: ScrollViewSuite, пример , в частности 3_Tiling.

3 голосов
/ 20 мая 2011

OpenGL - аппаратное ускорение графического процессора на устройствах iOS.Core Graphics не рисует и может быть во много раз медленнее при работе с большим количеством маленьких графических примитивов (линий).

Для множества маленьких квадратов просто записать их в растровое изображение в коде Cчем Core Graphics рисование линий.Затем просто сделайте растровое изображение в представлении один раз, когда закончите.Но Open GL будет еще быстрее.

0 голосов
/ 10 декабря 2015

Я столкнулся с той же проблемой. После бесконечных поисков в Google CAShapeLayer наконец-то спас меня! Вот подробные шаги, которые вам нужно сделать:

  1. Создайте представление с CAShapeLayer в качестве его типа слоя, переопределив метод UIView + (Class) layerClass
  2. Сконфигурируйте слой lineWidth, fillColor, strokeColor и т. Д.
  3. Создание экземпляра UIBezierPath
  4. Чтобы нарисовать путь, используйте экземпляр UIBezierPath, чтобы добавить линии, кривые или acr и т. Д., После того как вы закончили рисовать, просто установите bezierPath.CGPath в свойство "path" слоя

Вот простая демонстрация для рисования простой кривой при прикосновении к демонстрационному виду:

//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];
}
0 голосов
/ 12 августа 2014

Я не знаю подробностей истории iOS, так что, возможно, это был вариант, когда вопрос был впервые опубликован. Тем не менее, я хотел назвать CAShapeLayer как простой вариант, когда имеешь дело с проблемами производительности пути. «Core Core Animation: Advanced Techniques» (см. его в Google Книгах) говорит, что CAShapeLayer «использует аппаратно-ускоренное рисование», что я имею в виду, что это реализация на основе GPU. В той же книге есть хороший пример использования в главе 6, который сводится к следующему:

  1. Создание CAShapeLayer
  2. Настройте его lineWidth, fillColor, strokeColor и т. Д.
  3. Добавить слой в качестве подслоя containerView.layer вашего представления
  4. Чтобы нарисовать путь, просто установите для него свойство "path" слоя

Это сделало гигантскую разницу в производительности в моем приложении, измеренную прибором. Если ваша проблема с производительностью связана с путями, не заходите в OpenGL, пока не попробуете CAShapeLayer.

0 голосов
/ 20 мая 2011

точка 4. OpenGL должен делать это нормально. Проверьте, можете ли вы повторно использовать эти объекты и можете ли вы переместить часть логики в код GLSL.

Оптимизация производительности OpenGL (в контексте WebGL, но большая часть должна применяться): http://www.youtube.com/watch?v=rfQ8rKGTVlg

...