«

»

Апр
16

Canvas vs OpenGL/libGDX. Тестируем производительность на Андроид.

Переписав свои обои на libGDX, грех было не оценить : “А насколько все это теперь быстрее работает?” Понятно, что openGL уделает канву… но насколько? Перейдем сразу к делу. У нас в распоряжении 2 версии обоев, функция “Method Profiling” и Kindle Fire с CM7 на борту…

Что меряем?

Во-первых, я расскажу из каких элементов состоит каждый кадр обоев. Смотрим на рисунок ниже.

Графика состоит из:

  1. Фон. Заполняется бесшовной текстурой.
  2. Надпись “МИРЭА”. Хранится в виде спрайта. Просто выводится поверх фона.
  3. Кольца. До 5 колец одновременно. Будем их назвать Кольцо0-4. 0 – самое маленькое, 4 – самое большое.

Во-вторых, нужно понять, что мы меряем. А мерить мы будем время выполнения нашего кода анимации на ЦП (центральный процессор). Измерять мы его будем с помощью функции “Method Profiling”, которую нам предлагает SDK. Она показывает сколько времени код определенного потока занимал ЦП. Следуем помнить, что в случае OpenGL отрисовкой занимается графический чип (aka видеокарта), а не ЦП, ресурсы которого расходуются на растеризацию, наложение текстур и др. в случае использовании канвы. К сожалению, я не нашел способа как сравнить потребление ресурсов графического чипа канвой и openGL… но в рамках данной статьи не это является целью исследования, поэтому переживем.

Ну и в-третьих, запускать все будем на Kindle Fire с прошивкой CM7. Экран 1024 x 600, двухъядерный 1ГГц процессор OMAP4 и простенькое графическое ядро PowerVR SGX540 (он же графический чип и видеокарта).

Канва.

Начнем анализ производительности с версии написанной на канве. В Eclipse выбираем вкладочку DDMS, в списке процессов ищем наш процесс рисующий графику, нажимаем кнопочку “Start Method Profiling”, ждем некоторое время пока соберется статистика, жмем “Stop Method Profiling” и смотрим красивую картинку, интересующий наш кусочек которой приведен ниже.

По картинке мы видим, что наша программа выполняется в рамках одного потока. Визуализация хода работы потока состоит из повторяющегося шаблона, так как каждый кадр рисуется практически одинаково. Сразу можно выделить несколько больших зон в шаблоне, которые я обозначил Z0-Z6. Наведя мышкой на зоны можно посмотреть какой функции программы соответствует зона и  сколько времени выполнялась функция (чем дольше, тем больше зона). Получаем следующую статистику:

  • Z0. функция Canvas.native_drawRect. Это фон с бесшовной текстурой. Время выполнения: 23.6ms
  • Z1-Z4. функция Canvas.native_drawRect. Это кольца. Z1 – самое большое кольцо (рисуется дольше других колец), Z4 – самое маленькое кольцо. Время выполнения: 6.6, 5.3, 2.7, 1.0ms соответственно.
  • Z5. функция Canvas.native_drawBitmap. Это надпись “МИРЭА”. Время выполнения 2.8ms.
  • Z6. функция Surface.UnlockCanvasAndPost. Отправляет наше нарисованное на канве изображение на вывод на экран. Время выполнения: 8.9ms.

Кроме крупных зон есть еще разная мелочевка, но она погоды не делает. Общее время, которое тратиться на отрисовку одного кадра: 54ms… миллисекунды? Это же очень много? Такая простая графика занимает 54ms? Это максимум 1000/54 = 18.5 кадров в секунду… а если на рабочем столе есть еще пара тяжелых виджетов, то плавность анимации будет ни к черту… печаль. Но это ожидаемый результат, все действия с изображением, включая масштабирование, наложение текстур, растеризацию, создание линий и др. выполняются на ЦП, который для этого мягко говоря не создавался. Не маловажно еще и то, что под все графические ресурсы и под буфер для нового кадра, приходится отводить драгоценную память приложения, что не есть хорошо.

OpenGL/libGDX.

Теперь посмотрим, сколько будет потреблять ресурсов также самая анимация, но написанная на openGL с использованием библиотеки libGDX. Должно работать побыстрее. Смотрим на рисунок.

А картинка стала совсем другой. По-прежнему на графике видим повторяющийся шаблон (каждый кадр делаем одно и тоже), но зоны стали другими. Посмотрим, что внутри:

  • A0. Всякая мелочевка libgdx. Включая код вывода фона и надписи “МИРЭА”. Время выполнения: 4ms
  • A1-A5. Розовые зоны. Расчет координат окружностей. Cамые продолжительные зоны. Оно и понятно, тут вычисляются координаты треугольников для триангуляции колец. Каждое кольцо состоит из 192х треугольников, координаты которых каждый кадр вычисляются с помощью медленных функций Math.sin и Math.cos. Я специально не стал пока оптимизировать этот кусок кода, чтобы картинка была нагляднее. Время выполнения зон A1-A5: 9ms.

Итого имеем: время отрисовки одного кадра 12ms, из которых 6.5ms уходит на то, чтобы обновить координаты колец. Т.е. на управление выводим графики мы тратим всего 5-6ms, чисто теоретически можно успевать рисовать 1000/6 = 166 кадров к секунду. Неплохо. Стоит заметить, что в это время не входят затраты графического чипа, который занимается рендерингом и выводом графики на экран. ЦП посредством команд openGL лишь дает команды видеокарте, типа “выведи вот эту текстуру вот в эти координаты и увеличь ее в 2 раза”. Благодаря этому драгоценные ресурсы ЦП можно пустить на расчет логики вашего приложения, а графикой будет заниматься специально созданный для этого чип, который делает это на порядок быстрее и менее энергозатратно чем ЦП. Стоит также заметить, что текстуры и многие другие данные, хранятся в памяти видеокарты и не расходуют лимитированную память приложения.

Заключение.

Я не пытался получить точные цифры и не делал 1000 экспериментов и не смотрел гистограмму распределения результатов и не считал ее “среднее”. Однако полученные цифры позволяют качественно понять разницу между применением openGL и canvas. Ну а теперь выводы:

  • openGL на порядок меньше расходует ресурсов ЦП. Следовательно дольше живет батарейка, можно делать плавную и сложную анимацию. На один кадр рассматриваемой анимации libGDX тратит 6ms. Еще 6ms тратим мы сами для расчета координат колец. Canvas на ту же анимацию тратит 54ms, то есть в 4-5 раз больше.
  • если в вашем приложении нет ресурсоемкой логики, то его производительность упирается в графический чип устройства. ЦП практически не заметит разницу между выводом на экран 10 или 1000 текстур, а вот видеокарты может с какого-то момента не хватить.
  • canvas довольно шустрый, но только на маленьких площадях. Проблема канвы в том, что ее скорость работы пропорциональна площади рисунка. Поэтому залить фон или вывести большой по площади объект довольно затратно. А вот нарисовать, что-то мелкое и простое, получается довольно шустро.
  • рисовать полноэкранную плавную анимацию с помощью канвы не получится. В лучшем случае удастся выжать 15-20 кадров, при условии, что параллельно нам не работает другое ресурсоемкое приложение. А вот нарисовать анимацию на небольшой кнопке или “песочные часы” – всегда пожалуйста.
Удачных разработок.

5 комментариев

  1. Лев Сообщает:

    Можете пожалуйста сделать туториал, как с этой библиотекой создать простые обои?

    1. PandaСoder Сообщает:

      Хороший туториал делать слишком долго, исходники лежат на гитхабе.

      1. Лев Сообщает:

        О, спасибо, не заметил!

      2. Лев Сообщает:

        Только там не все классы есть. Например, нету MireaWallpaperEngine, LibdgxWallpaperListener

  2. Лев Сообщает:

    И где теперь искать LibdgxWallpaperService? Кажется, в новой версии libgdx, gdx-backend-android-livewallpaper был объединен с основной библиотекой.

Добавить комментарий

Мы сохраним Ваш e-mail в тайне.