Эффективность холста HTML при рисовании множества строк
В настоящее время я пишу приложение, которое отображает много, и я имею в виду много 2D-путей (из сотен, тысяч крошечных сегментов) на холсте HTML5. Как правило, несколько миллионов баллов. Эти точки загружаются с сервера в двоичный ArrayBuffer
.
Я, вероятно, не буду использовать столько очков в реальном мире, но я заинтересован в том, как улучшить производительность. Вы можете назвать это любопытством, если хотите;)
В любом случае, я протестировал следующие решения:
-
Использование gl.LINES
или gl.LINE_STRIP
с WebGL и вычисление всего в шейдерах на графическом процессоре. В настоящее время самый быстрый, может отображать до 10M сегментов, не дрогнув на моем Macbook Air. Но есть очень строгие ограничения для двоичного формата, если вы хотите избежать обработки вещей в JavaScript, что происходит медленно.
-
Используя Canvas2D, нарисуйте огромный путь со всеми сегментами в одном вызове stroke()
. Когда я прохожу 100 тыс. Точек, страница замерзает на несколько секунд до обновления холста. Итак, не работаем здесь.
-
Использование Canvas2D, но нарисуйте каждый путь со своим собственным вызовом stroke()
. Несмотря на то, что другие говорили в Интернете, это намного быстрее, чем рисовать все за один звонок, но все же намного медленнее, чем WebGL. Все начинает ухудшаться, когда я достигаю сегментов 500 тыс..
Два решения Canvas2D требуют прокрутки всех точек всех путей в JavaScript, так что это довольно медленно. Знаете ли вы какой-либо метод (ы), который может улучшить скорость итерации JavaScript в ArrayBuffer или скорость обработки вообще?
Но, что странно, экран не обновляется сразу после завершения всех обращений рисования на холсте. Когда я перехожу к пределу производительности, между окончанием вызовов рисования и обновлением холста существует заметная задержка. У вас есть идея, откуда это происходит, и есть ли способ уменьшить его?
Ответы
Ответ 1
Во-первых, WebGL был хорошей идеей, но объем обработки, требуемый для декодирования и отображения двоичных данных, просто не работает в шейдерах, поэтому я исключил его.
Вот основные узкие места, с которыми я столкнулся. Некоторые из них довольно распространены в общем программировании, но это хорошая идея запомнить их:
- Лучше всего использовать несколько маленьких
for
циклов
- Создать переменные и блокировки на самом высоком уровне, не создавать их внутри циклов for
- Извлеките свои данные в куски и используйте
setTimeout
, чтобы запланировать следующий фрагмент через несколько миллисекунд: таким образом, пользователь все равно сможет использовать пользовательский интерфейс
- Объекты и массивы JavaScript бывают быстрыми и дешевыми, используйте их. Лучше всего читать/записывать их в последовательном порядке, от начала до конца.
- Если вы не записываете данные последовательно в массив, используйте объекты (поскольку несекретные чтения-записи дешевы для объектов) и нажмите индексы в массив индексов. Я использовал реализацию SortedList, чтобы отсортировать индексы, которые я нашел здесь. Накладные расходы были минимальными (около 10-20% от времени рендеринга), и в итоге это было хорошо.
Что обо всем, что я помню. Если я найду что-то еще, я обновлю этот ответ!