Один из друзей спросил о возможности разработки набора диаграмм ActionScript, которые выглядят хорошо и будут работать на мобильных устройствах, особенно на телефонах Android и iPhone. Видимо, клиент спрашивал, и он хотел знать целесообразность повторного использования. Мы обсудили проблемы с существующими диаграммами Flex, в частности отсутствие у них возможностей для создания обложек Flex 4, но, что более важно, их недостаточную масштабируемость производительности.
Как и многие проекты в 2010 году, это никуда не делось, но мне было любопытно. В прошлом я занимался графикой, которая растягивала Flash Player до предела, и мне было интересно, смогу ли я через 2 года добиться большего успеха в среде с ограниченной производительностью, такой как мобильные устройства. К сожалению, вещи в Flash Player не улучшились, чтобы упростить и улучшить производительность. Очевидно, что это хуже на некоторых из этих медленных мобильных устройств. Тем не менее, я по-прежнему находил некоторые интересные вещи, играя с 4 потенциальными стратегиями рисования, которые, как я думал, я бы использовал, с теми же базовыми показателями и кодом, которые вы можете использовать самостоятельно.
Зеленая нить
Следует отметить, что диаграммы Flex не используют зеленую резьбу. Таким образом, как только вы начнете перекачивать значительные объемы данных, скажем, более 500 точек данных, вы начнете замечать замедление. Иногда вы превышаете тайм-аут сценария по умолчанию на 15 секунд, заставляя вас компилировать с большим числом, чтобы обеспечить поддержку различных устройств. Проблема здесь в том, что ваше приложение «зависает» даже на 5 секунд не приемлемо.
Введите зеленый поток. Это не идеально, но дает Flash Player несколько циклов ЦП, чтобы реагировать на другие события перерисовки и взаимодействия с пользователем, не связанные с рисованием диаграммы. Помните также, что если ваш Flash Player не отвечает, вы также можете потерять сетевые подключения, даже если они вернулись успешно.
Суть в том, что использование зеленых потоков в процедурах рисования позволяет пользователям по-прежнему взаимодействовать с вашим приложением, и оно не отображается как заблокированное. Вы можете больше узнать о рендеринге больших объемов данных в Flash Player из моей прошлой записи в блоге, а также о больших наборах данных Эндрю Триса и графических данных и реальных данных на основе растровых изображений . Суть заключается в том, чтобы создать таймер, если цикл занимает более 100 миллисекунд, приостановить кадр и продолжить на следующем кадре, где вы остановились. Потрясающее гетто
Точка / Линия Сокращение
Хотя некоторые из этих устройств имеют невероятно высокое разрешение и плотность пикселей, реальность такова, что вы все еще не видите все точки данных в диаграмме с интенсивным использованием данных. Очевидно, существует множество алгоритмов, которые помогают уменьшить плотность данных, которые вы должны рисовать, в зависимости от текущего уровня масштабирования. Хотя в теории эти алгоритмыдолжно оказать существенное влияние на возможности рендеринга Flash Player, печальная правда в том, что сами алгоритмы отнимают больше процессорного времени, чем они того стоят, по крайней мере, два, которые я видел. Требование, чтобы я проходил через огромное количество точек, не в состоянии понять, что «зацикливание» и «массивное» просто не объединяются во Flash Player. Flash Player действительно хорош в рисовании и рендеринге, поэтому, возможно, я ошибаюсь, но без истинных возможностей многопоточности (нет, асинхронный шейдер не в счет), я не вижу ценности.
3 процедуры рисования
В сочетании с зелеными потоками у меня есть 3 стандартные процедуры рисования, здесь ничего особенного (например, нет многопоточного шейдера через PixelBender ). Они используются для рисования красного квадрата 4 на 4 пикселя на белом фоне. Это просто спот-график. Цели: 1. Я должен иметь возможность рисовать столько точек, сколько захочу, и 2. эти точки должны быть точно представлены, и 3. они должны работать на мобильном устройстве. Использование растрового изображения с setPixel / copyPixels не сработало, потому что вы можете рисовать только на целых пикселях, тем самым снижая визуальную точность; чем больше точек данных, тем неточнее ваш рисунок.
Ниже приведены 3 стратегии рисования, которые я выбрал:
- Скопируйте несколько пикселей вместе в 1 растровое изображение, переместив векторную фигуру, сфотографируйте его текущее местоположение и скопируйте это изображение поверх первого.
- Рисуем связку векторных фигур и помещаем их туда, куда они должны идти.
- Рисование с использованием нового API векторных графических данных.
Давайте поговорим о плюсах и минусах каждого.
1. BitmapData.draw
Pro’s : самый быстрый.
Con’s : придется перерисовать, если вы хотите увеличить.
Вы создаете растровое изображение размером с вашу диаграмму, рисуете все, что представляет точку на вашей диаграмме в векторной фигуре, а затем на каждой итерации рисования вы позиционируете фигуру и выполняете bitmap.draw с альфа-каналом. Хорошая особенность этого алгоритма в том, что у вас есть только 2 объекта DisplayObject; точка формы и растровое изображение. Кроме того, нет никакого штрафа в постоянном добавлении пикселей к растровому изображению; это одно и то же растровое изображение с разноцветными пикселями. Никаких дополнительных ОЗУ или ЦП не выделено.
Плохая часть в том, что BitmapData.draw медленнее, чем BitmapData.copyPixels, и всякий раз, когда вы хотите изменить размер диаграммы, вы должны перерисовать ее, чтобы она хорошо выглядела при текущем разрешении. Единственное, что вы можете использовать для пула объектов — это фактические данные точек, используемые для рисования. Кроме того, становится практически невозможно перерисовать 1 конкретную точку, если 1 из ваших точек данных изменяется, таким образом, вы в основном вынуждены перерисовывать все это.
2. Несколько векторных фигур
Pro : быстро, визуально масштабируется, быстро перерисовывается.
Con : хотя иногда и так быстро, как Bitmap, огромные затраты на выделение объектов иногда запускают сборку мусора, замедляя его.
Вы создаете спрайт, затем создаете новую форму для каждой точки данных и помещаете эту форму в спрайт. Если у вас есть 1000 точек данных, у вас будет 1000 фигур. Фигуры являются одними из самых низких накладных расходов ОЗУ во Flash Player, и они очень быстрые и недорогие в создании. Очевидно, что происходит некоторая оптимизация, потому что она часто быстра или быстрее, чем метод BitmapData.draw, даже с таким количеством объектов DisplayObject.
Еще одна приятная вещь — у вас есть много вариантов при увеличении или уменьшении. Вы можете либо объединить векторные фигуры в Object, и просто переместить их, когда кто-нибудь уменьшит или уменьшит масштаб, или просто отрегулировать scaleX / scaleY графика, и Flash Player нарисует его для вас. Для изменений данных вы можете перерисовать только 1 (x) отдельных точек при изменении их соответствующих точек данных, доступ к которым осуществляется через сверхбыстрый двусвязный список. Это также дает гораздо больший контроль над пользовательскими точками проектирования, но самое главное, интерактивность с ними (детализация, всплывающие подсказки и т. Д.). Если вы возьмете пул фигур, вы также сможете быстро перерисовать все это.
Недостатком является то, что вы быстро выделяете много ресурсов, что часто приводит к непреднамеренному запуску сборки мусора. Кроме того, иногда он имеет больший объем ОЗУ, чем подпрограмма BitmapData.draw, потому что у вас так много выделенных объектов.
3. Использование IGraphicsData
Плюсы : форма с одним вектором, низкий уровень и более простое управление рисованием (в частности, правила намотки), визуально масштабируемая, самая быстрая процедура рисования из сотен.
Con’s : не масштабируется тысячами
Я был взволнован, услышав, что мы получаем более низкий уровень контроля над процедурами рисования. Если вы делаете сложные диаграммы или перерисовываете векторный объект несколько раз, это замечательно. «Вещи» в данном случае — это классы команд рисования, которые реализуют IGraphicsData. Вы также получаете более низкий уровень контроля, чем при использовании графических методов. Например, правила намотки позволяют рисовать несколько векторных рисунков поверх друг друга, и они будут просто перекрывать или вырезать отверстия, как они делают по умолчанию. Классная вещь.
По сути, вы просто создаете кучу команд рисования IGraphicsData , накачиваете их полными номерами мест для рисования и переходите к методу Graphics.drawGraphicsData .
IGraphicsData, кажется, имеет проблему масштабируемости в тысячи на настольных компьютерах и сотни на устройствах. Это не линейная проблема, а резкий звонок; как только вы наберете 4000+ точек данных, она задыхается, но 500 или ниже, она очень быстро и злая, чтобы перерисовать. Мне показалось сложным, что в пуле объектов фактические номера точек используются некоторыми объектами IGraphicsData.
Benchmark Times
На рабочем столе (в миллисекундах):
3000 точек данных
- Создание dataPoints: 9
- PlotChartBitmap :: redraw: 253
- PlotChartVector :: redraw: 198
- PlotChartGraphicsDataVector :: redraw: 190
10000 точек данных
- Создание dataPoints: 29
- PlotChartBitmap :: redraw: 739
- PlotChartVector :: redraw: 774
- PlotChartGraphicsDataVector :: redraw: 3791
On Nexus One
3000 data points
- dataPoints creation: 23
- PlotChartBitmap::redraw: 2 seconds
- PlotChartVector::redraw: 3 seconds
- PlotChartGraphicsDataVector::redraw: 13 seconds
10,000 data points
- dataPoints creation: 57 milliseconds
- PlotChartBitmap::redraw: 6 secs
- PlotChartVector::redraw: 16 secs
- PlotChartGraphicsDataVector::redraw 4 minutes
Additionally, the Bitmap chart takes the same amount of time to redraw. The Vector chart is about the same as well, even with object pooling the points (my example doesnt’ do a very good job). The IGraphicsData chart actually redraws the fastest (little good it does on a device), but this is only because no actually IGraphicsData object changed; if something did, I bet it’d continue to be slow as all get out.
Conclusions
IGraphicsData is a lot more flexible to draw with, but it just doesn’t scale, especially on slower processors like mobiles… or does it? You’ll notice that, at least on my 4 year old Macbook Pro, IGraphicsData is actually the fastest algorithm until you get above 4000 data points on the desktop, not sure on various devices. Additionally, you can create 1 shape vs. many using the winding rules. At smaller data point values, IGraphicsData uses the least RAM as well since you’re only creating 1 Shape, and a bunch of built-in ValueObjects. Knowing this, the above can be mis-leading for charts below 1000 data points that require no interactivity.
For example, for 500 data points results in this on my phone:
- dataPoints creation: 10
- PlotChartBitmap::redraw: 432
- PlotChartVector::redraw: 398
- PlotChartGraphicsDataVector::redraw: 165
…but watch the redraw times:
- PlotChartBitmap::redraw: 311
- PlotChartVector::redraw: 351
- PlotChartGraphicsDataVector::redraw: 16
I modified the above example to randomize the 10th data point so 1 of the IGraphicsData points wouldn’t be the same. This ensures it does in fact redraw. Furthermore, my object pooling isn’t very good; I’m sure if I pooled the object points I’m pushing, maybe it could be even faster.
I bring this up because for a lot of financial charts which are often real-time, this is a huge deal.
From an interactivity perspective, which is what Flash Player excels at in charting, swapping out out the Vector chart’s Shapes with Sprites so you can add Mouse/Touch listeners/signals to them, the redraw is about the same. It’s still slower than using straight Shapes, and a little more memory.
The only really nice thing about the Bitmap option is that the RAM really doesn’t grow much, regardless of the size of your data points, and how many times you redraw. Where it breaks down is zooming in and out. Both Vector and IGraphicsData allow you to pool the Shapes/Sprites/data, with IGraphicsData being really quick about it.
Sadly, even with green threading you tend to lose a lot of frame rate while it draws. Lowering to 10 vs. 100 milliseconds seems to help a little bit.
Adobe, for the love of cheese, can we please get threading in Flash Player, kthx?!
SampleChartTypes (Flash Builder Library Project) – ZIP | FXPL
NOTE: I’ve only included the charts & VO with above code; my Flex code uses unstable SDK (heh). The following code should get you started; just take the points and shove ‘em at a chart instance’s dataProvider property as an ArrayCollection:
service = new GetChartVOsService(); service.addEventListener("completed", onComplete); service.getChartVOs(500); function onComplete(event:Event):void { yourChart.dataProvider = new ArrayCollection(service.chartVOs); }