Apps Conf
GPU, гексагональные ускорители и линейная алгебра.
Все эти слова гораздо сильнее связаны с мобильной разработкой, чем кажется на первый взгляд: гексагональные ускорители уже помогают обучать нейронные сети на мобильных устройствах; алгебра и матан пригодится, чтобы устроиться работать в Apple; а GPU-программирование не только позволяет ускорять приложения, но и учит видеть суть вещей.
Во всяком случае, так говорит руководитель мобильной разработки Prisma Андрей Володин. А еще о том, как идеи перетекают в мобильную разработку из GameDev, чем отличаются парадигмы, почему в Android нет нативного размытия — да много еще чего, продуктивный вышел выпуск AppsCast. Под катом поговорим про доклад Андрея на AppsConf без спойлеров.
AppsCast — подкаст, посвященный конференции для мобильных разработчиков AppsConf. Каждый выпуск — новый гость. Каждый гость — спикер конференции, с которым мы обсуждаем его доклад и говорим на связанные с ним темы. Ведут подкаст члены программного комитета AppsConf Алексей Кудрявцев и Даниил Попов.
Алексей Кудрявцев: Всем привет! Андрей, расскажи, пожалуйста, о своем опыте.
Андрей Володин: Мы в Prisma разрабатываем продукты, которые в основном, связаны с обработкой фото и видео. Наше флагманское приложение — Prisma. Сейчас мы делаем еще одно приложение Lensa для Facetune-подобного функционала.
Я руковожу мобильной разработкой, но я играющий тренер. На мне вся core-часть, пишу GPU пайплайны для всех этих приложений. Разрабатываю core-фреймворки, чтобы алгоритмы и нейронки, которые, разработала R&D-команда, запускались на мобильных устройствах, работали в realtime. Короче, чтобы убивать серверные вычисления и всё такое.
Алексей Кудрявцев: Звучит не как обычная iOS-разработка.
Андрей Володин: Да, у меня такая специфика — я каждый день пишу на Swift, но при этом очень далек от того, что принято считать iOS-разработкой.
Даниил Попов: Ты упомянул GPU пайплайны, это вообще о чем?
Андрей Володин: Когда делаешь фоторедакторы, тоже нужно настроить архитектуру и декомпозировать логику, потому что в приложении есть разные инструменты. Например, в Lensa есть инструмент боке, который размывает фон с помощью нейронки, есть инструмент для ретуши, который делает человека красивее. Нужно, чтобы все это работало эффективнее на GPU. Причем желательно не передавать данные между процессором и видеокартой каждый раз, а заранее выстраивать набор операций, выполнять их за один прогон, и показывать пользователю итоговый результат.
GPU пайплайны — это «маленькие комочки», из которых собираются инструкции для видеокарты. Потом она все это делает очень быстро и эффективно, а ты забираешь результат за один раз, а не после каждого инструмента. Я занимаюсь тем, чтобы наши GPU пайплайны были максимально быстрыми, эффективными и вообще в принципе существовали.
Алексей Кудрявцев: Расскажи, как ты к этому пришел? Обычный iOS-разработчик начинает с клепания и формочек, потом ходит куда-то по API и счастлив. Как так получилось, что занимаешься совсем другим?
Андрей Володин: По большей части это стечение обстоятельств. До того, как у меня появилась работа, я делал игры под iOS. Мне это всегда было интересно, но я понимал, что в России особо негде развиваться в этом направлении. Так получилось, что мы нашли друг друга с компанией Prisma. Им нужен был iOS-разработчик, который умеет писать на Swift и при этом знает GPU, в частности, Metal, который тогда только вышел, а я точно подходил под это описание.
Откликнулся на вакансию, у нас случилась синергия, и вот уже третий год я все дальше и дальше углубляюсь в эту штуку. Если сейчас что-то пойдет не так, то мне уже во всех этих Viper и MVVM — я даже не знаю, как это расшифровывается — придется разбираться с самого начала.
Чем занимается GPU Engineer
Даниил Попов: В твоем профиле на AppsConf написано GPU Engineer. Чем GPU Engineer занимается большую часть рабочего дня, кроме распития кофе?
Андрей Володин: Тут надо упомянуть, чем принципиально отличается процессор от GPU. Процессор выполняет операции как бы последовательно. Даже многопоточность, которая у нас есть, часто фейковая: процессор останавливается и переключается, чтобы сделать маленькие кусочки разных задач, и выполняет их по чуть-чуть слайсами. GPU работает ровно противоположным образом. Есть n процессоров, которые по-настоящему работают параллельно, причем есть параллельность и между процессами, и параллельность внутри GPU.
Моя основная работа, кроме банальных вещей типа оптимизации работы с памятью и организации переиспользования кода, в том, что я портирую алгоритмы, которые написаны для CPU на видеокарты так, чтобы они параллелились. Это не всегда тривиальная задача, потому что бывают очень эффективные алгоритмы, полностью завязанные на последовательное выполнение инструкций. Моя работа — придумать, например, аппроксимацию для такого алгоритма, которая делает, может быть, не то же самое в точности, но визуально результат не отличишь. Так мы можем получить ускорение в 100 раз, чуть-чуть пожертвовав качеством.
Еще я занимаюсь портированием нейронок. Кстати, мы скоро сделаем крупный Open Source релиз. Еще до того, как появился Core ML, у нас был свой аналог, и мы наконец созрели, чтобы выложить его в Open Source. Его парадигма немного отличается от Core ML. Я, в том числе, разрабатываю его core-часть.
В общем, я делаю все вокруг Computer Vision алгоритмов и вычислений.
Алексей Кудрявцев: Интересный анонс.
Андрей Володин: Это не секрет, мы не будем анонсировать его с какой-то помпой, просто можно будет посмотреть пример фреймворков, которые используются внутри Prisma.
Зачем оптимизировать под GPU
Алексей Кудрявцев: Расскажи, пожалуйста, зачем вообще алгоритмы оптимизировать под GPU. Может показаться, что достаточно добавить ядер в процессор или алгоритм оптимизировать. Почему именно GPU?
Андрей Володин: Работа на GPU может колоссально ускорить алгоритмы. Например, у нас есть нейронки, которые на центральном процессоре Samsung S10 будут выполняться 30 с, а на GPU будет 1 кадр, то есть 1/60 с. Это невероятно меняет пользовательский опыт. Нет вечного экрана загрузки, можно видеть результат работы алгоритма на видеопотоке, или крутить слайдер и тут же видеть эффекты.
Дело вовсе не в том, что мы слишком клевые, чтобы писать на CPU, поэтому перепишем все на GPU. У использования GPU есть прозрачная цель — ускорить работу.
Алексей Кудрявцев: GPU хорошо параллельно обрабатывает операции подобные друг другу. У вас как раз такие операции и поэтому удается добиться таких успехов?
Андрей Володин: Да, главная сложность не в том, чтобы закодить, а в том, чтобы создать такие алгоритмы, которые хорошо перекладываются на GPU. Это не всегда тривиально. Бывает, ты придумал, как все клево сделать, но для этого нужно слишком много точек синхронизации. Например, пишешь все в одно property, а это явный признак, что это будет плохо параллелитmся. Если много писать в одно место, то всем потокам нужно будет для этого синхронизироваться. Наша задача — аппроксимировать алгоритмы так, чтобы они хорошо параллелились.
Алексей Кудрявцев: Для меня, как для мобильного разработчика, это звучит, как rocket science.
Андрей Володин: На самом деле, это не так сложно. Для меня rocket science — это VIPER.
Третий чип
Даниил Попов: Кажется, на прошлой конференции Google I/O анонсировали железяку под TensorFlow и прочие штуки. Когда уже наконец в мобилках появится третий чип, TPU или как его назовут, который тоже будет делать всю ML-магию на устройстве?
Андрей Володин: У нас есть эта самая штука, она подключается по USB, и на ней можно гонять нейронки от Google. Это уже есть у Huawei, мы даже писали софт для их гексагональных ускорителей, чтобы на P20 быстро гонялись сегментационные нейронки.
Надо сказать, что в iPhone они на самом деле уже есть. Например, в последнем iPhone XS есть сопроцессор, которые называется NPU (Neural Processing Unit), но пока что к нему есть доступ только у Apple. Этот сопроцессор уже сейчас разделывает GPU в iPhone. Некоторые модели Core ML используют NPU и за счет этого работают быстрее, чем голый Metal.
Это существенно, учитывая, что помимо самой low inference нейронки, Core ML требует много дополнительных действий. Сначала нужно сконвертировать входные данные в формат Core ML, он их обработает, потом вернет в своем формате — нужно конвертировать обратно, и только потом показывать пользователю. Это все занимает довольно много времени. Мы пишем overhead free пайплайны, которые от начала и до конца работают на GPU, при этом Core ML модели бывают быстрее именно за счет этого хардварного процесса.
Скорее всего, на WWDC в июне покажут фреймворк для работы с NPU.
То есть, как ты сказал, устройства уже есть, просто разработчики пока не могут их использовать по полной. Моя гипотеза, что компании пока сами не понимают, как сделать это аккуратно в виде фреймворка. Или просто пока не хотят отдавать, чтобы иметь рыночное преимущество.
Алексей Кудрявцев: Со сканером отпечатков пальцев такая же штука была в IPhone, насколько я помню.
Андрей Володин: Он и сейчас не то, чтобы супер-доступный. Можно использовать его верхнеуровнево, но нельзя получить сам отпечаток. Можно просто попросить Apple разрешить пользователю им воспользоваться. Это все равно не то, что полный доступ к самому сканеру.
Гексагональные ускорители
Даниил Попов: Ты упомянул термин гексагональные ускорители. Думаю, не все знают, что это.
Андрей Володин: Это просто особенность архитектуры железяк, которые использует Huawei. Надо сказать, она довольно мудреная. Мало кто знает, но в некоторых Huawei эти процессоры есть, но не используются, потому что в них есть хардварный баг. Huawei их выпустили, а потом нашли проблему, теперь в некоторых телефонах мертвым грузом лежат специальные чипы. В свежих версиях уже все работает.
В программировании есть парадигма SIMD (Single Instruction, Multiple Data), когда одни и те же инструкции выполняются параллельно на разных данных. Чип устроен так, что умеет обрабатывать какую-то операцию параллельно сразу на нескольких потоках данных. В частности, гексагональный значит, что на 6 элементах параллельно.
Алексей Кудрявцев: Я думал, что GPU как раз так и работает: векторизует задачу и на разных данных выполняет одну и ту же операцию. В чем отличие?
Андрей Володин : GPU более general purpose. Несмотря на то, что программирование для GPU довольно низкоуровневое, относительно работы с сопроцессорами оно довольно высокоуровневое. Для программирования на GPU используется С-подобный язык. На iOS код все равно потом компилируется с помощью LLVM в машинные инструкции. А эти штуки для сопроцессоров чаще всего пишутся прямо хардкорно — на ассемблере, на машинных инструкциях. Поэтому там увеличение производительности гораздо заметнее, потому что они заточены для конкретных операций. На них нельзя посчитать вообще все что угодно, а можно посчитать только то, для чего они изначально предназначены.
Алексей Кудрявцев: А для чего они обычно разработаны?
Андрей Володин: Сейчас в основном для самых распространенных операций в нейронных сетях: convolution — свертка или какие-то промежуточные активации. В них есть заранее зашитая функциональность, которая супер-быстро работает. Так они гораздо быстрее на некоторых задачах, чем GPU, но во всех остальных просто не применимы.
Алексей Кудрявцев: Похоже на DSP-процессоры, которые в свое время использовались для аудио, и на них все плагины и эффекты очень быстро работали. Продавались специальные дорогие железяки, но потом процессоры подросли, и теперь мы записываем и обрабатываем подкасты прямо на ноутбуках.
Андрей Володин: Да, примерно то же самое.
GPU не только для графики
Даниил Попов: Я правильно понимаю, что сейчас на GPU можно обрабатывать данные, которые не связаны с графикой напрямую? Получается, что GPU теряет свое изначальное предназначение.
Андрей Володин: Именно так. Я об этом довольно часто рассказываю на конференциях. Первыми стали NVidia, которые представили CUDA. Это технология, которая делает проще GPGPU (General-purpose computing on graphics processing units). На ней можно писать на надмножестве С++ алгоритмы, которые параллелизуются на GPU.
Но люди делали это и раньше. Например, умельцы на OpenGL или на еще более старых DirectX просто записывали в текстуру данные — каждый пиксель интерпретировали как данные: в первый пиксель первые 4 байта, во второй — вторые 4 байта. Обрабатывали текстуры, потом обратно данные из текстуры извлекали и интерпретировали. Это было очень костыльно и сложно. Сейчас видеокарты поддерживают general purpose логику. Можно скормить в GPU любой буфер, описать свои структуры, даже иерархию структур, в которой они будут ссылаться друг на друга, что-то посчитать и вернуть на процессор.
Даниил Попов: То есть можно сказать, что GPU теперь Data PU.
Андрей Володин: Да графики на GPU порой обрабатывается меньше, чем общих вычислений.
Алексей Кудрявцев: Архитектура CPU и GPU разная по сути, а считать можно и там, и там.
Андрей Володин : Действительно, в чем-то CPU бывает быстрее, в чем-то GPU. Нельзя сказать, что GPU всегда быстрее.
Даниил Попов: Насколько я помню, если задача посчитать что-то очень разное, то на CPU это может быть гораздо быстрее.
Андрей Володин: Зависит еще и от объема данных. Всегда есть накладные расходы на передачу данных с CPU на GPU и обратно. Если считаешь, например, миллион элементов, то использовать GPU обычно оправдано. Но посчитать тысячу элементов на CPU может быть быстрее, чем просто скопировать их на видеокарту. Поэтому всегда надо выбирать под задачу.
Кстати, Core ML делает это. Core ML умеет в runtime, по заверениям Apple, выбирать, где быстрее посчитать: на процессоре или на видеокарте. Не знаю, работает ли это в реальности, но они заявляют, что да.
Хардкорные знания GPU Engineer для мобильного разработчика
Алексей Кудрявцев: Давайте вернемся к мобильной разработке. Ты GPU Engineer, у тебя куча хардкорных знаний. Как эти знания можно применить мобильному разработчику? Например, что ты видишь в UIKit, чего не видят другие?
Андрей Володин: Подробно об этом я буду рассказывать на AppsConf. Применить можно много где. Когда я вижу, например, как устроен API UIKit, я могу сразу понять, зачем и почему это сделано. Наблюдая падение производительности при рендеринге каких-то вьюшек я могу понять причину, потому что знаю, как внутри написан рендеринг. Я понимаю: чтобы отобразить эффекты, которые на самом деле делает Гауссово размытие поверх фрейм-буфера, нужно сначала закэшировать всю текстуру, применить к ней тяжелую операцию размытия, вернуть результат, закончить рендеринг остальных вьюх, и только потом показать на экран. Все это надо уместить в 1/60 секунды, иначе будет тормозить.
Для меня абсолютно очевидно, почему это долго, но для моих коллег это бывает непонятно. Именно поэтому я хочу поделиться дизайн-приемами, которые мы часто используем в GameDev, и своими инсайтами о том, как я смотрю на проблемы и пытаюсь их решить. Это будет эксперимент, но думаю, что должно быть интересно.
Почему в Android нет нативного размытия
Даниил Попов: Ты упомянул размытие, и у меня возник вопрос, который волнует, думаю, всех Android-разработчиков: почему в iOS есть нативный блюр, а в Android его нет.
Андрей Володин: Думаю, что это из-за архитектуры. На платформах Apple используется Tiled Shading архитектура рендеринга. При этом подходе рендерится не весь кадр, а маленькие тайлы — квадратики, части экрана. Это позволяет оптимизировать работу алгоритма, потому что основной прирост производительности при использовании GPU дает эффективное использование кэша. На iOS фрейм часто рендерится так, что вообще не занимает память. Например, на iPhone 7 Plus разрешение 1920*1080, это примерно 2 млн пикселей. Умножаем на 4 байта на канал, получается в районе 20 Мегабайт на кадр. 20 Мб на то, чтобы просто хранить фрейм-буфер системы.
Подход Tiled Shading позволяет разбивать этот буфер на маленькие кусочки и рендерить его по чуть-чуть. Так очень сильно возрастает число обращений к кэшу, потому что, чтобы сделать размытие, нужно прочитать уже нарисованные пиксели и на них посчитать Гауссовское распределение. Если читать по всему кадру, то кэш-рейт будет очень низкий, потому что каждый поток будет читать разные места. Но если читать маленькие кусочки, то кэш-рейт будет очень высокий, и производительность тоже будет высокая.
Мне кажется, отсутствие нативного размытия в Android связано именно с особенностями архитектуры. Хотя, может быть, это и продуктовое решение.
Даниил Попов: В Android для этого есть RenderScript, но там нужно руками смешивать, отрисовывать, подкладывать. Это сильно сложнее, чем в iOS один флажок поставить.
Андрей Володин: Скорее всего, еще и производительность ниже.
Даниил Попов: Да, чтобы удовлетворить дизайнерские хотелки, нам приходится даунскейлить картинку, блюрить ее, а потом апскейлить обратно, чтобы хоть как-то сэкономить.
Андрей Володин: Кстати, с помощью этого можно делать разные трюки. Гауссовское распределение — это размыленный кружок. Сигма Гаусса зависит от количества пикселей, которое ты хочешь им собрать. Часто в качестве оптимизации можно даунскейлить картинку и немножко сузить сигму, и когда ты вернешь изначальный масштаб, разницы не будет, потому что сигма напрямую зависит от размера картинки. Этот трюк мы часто используем внутри, чтобы ускорять размытие.
Даниил Попов: Тем не менее, RenderScript в Android не позволяет сделать радиус больше 30.
Андрей Володин: На самом деле, радиус 30 — это очень много. Опять же я понимаю, что 30 пикселей собрать с помощью GPU на каждом потоке очень дорого.
Чем похожи мобильная разработка и GameDev
Алексей Кудрявцев: В тезисах к своему докладу ты говоришь, что у мобильной разработки и у GameDev много общего. Расскажи немного, чего именно?
Андрей Володин: Архитектура UIKit очень напоминает игровые движки, причем старые. Современные пошли в сторону Entity Component System, об этом тоже будет в докладе. В UIKit это тоже приходит, появляются статьи, в которых пишут, как можно проектировать вьюхи на компонентах. Но это придумали в GameDev, впервые Component System использовался в игре Thief в 98 году.
Фундаментально, например, Cocos2d, над которым я долгое время работал, и идеи, которые использовали в первой реализации, очень похожи. И там, и там используется Scene graph — дерево сцены, когда у каждой ноды есть под-ноды, их рендер происходит с помощью аккумулирования аффинных преобразований, которые конкретно на iOS называются CGAffineTransform. Это просто матрицы 4*4, которые перемножаются, чтобы изменить систему координат. Анимация везде сделана примерно одинаково.
И в игровых движках, и в UIKit все построено на интерполяции по времени. Просто мы интерполируем какие-то значения — будь то цвета или позиции между кадрами. Оптимизации все такие же: в GameDev принято не делать лишней работы, так и UIKit используется setNeedsLayout, layoutIfNeeded.
Эти параллели я провожу для себя постоянно — между тем, что я когда-то делал, и между тем, что я вижу во фреймворке Apple. Об этом и расскажу на AppsConf.
Даниил Попов: Действительно, API Cocos2d похож на iOS (для UI). Как думаешь, вдохновлялись разработчики работой друг друга или просто архитектурно получилось?
Андрей Володин: Думаю, что чем-то вдохновлялись. Cocos2d появился в 2008–2009 годах, тогда UIKit не был тем UIKit, который мы знаем сейчас. Мне так кажется, что какие-то приемы там специально повторялись, чтобы людям было комфортнее работать, чтобы они могли проводить параллели.
Забавно, что качельки качнулись: изначально core-команда Cocos2d немного позаимствовала идеи Apple, а потом Apple полностью скопировал Cocos2d, вплоть до всех архитектурных решений. SpriteKit по сути полная копия всех идей, которые появились в Cocos2d. В этом смысле Apple забрал свой должок.
Алексей Кудрявцев: Мне кажется, те же приемы, что в UIKit в 2009, были еще на MacOS, который существует с древних времен. Там те же setNeedsLayout, layoutIfNeeded есть, аффинные преобразования.
Андрей Володин: Безусловно, но GameDev существует еще дольше, чем MacOS.
Алексей Кудрявцев: Не поспоришь!
Андрей Володин: Поэтому я не сравниваю Cocos2d с фреймворками Apple, а скорее рассматриваю в принципе парадигмы, которые зародились в GameDev. Именно в GameDev люди впервые поняли, что наследование — это плохо. Когда весь мир восторгался ООП, в GameDev уже начали задумываться, что наследование приносит проблемы, и придумали компоненты. Мобильная разработка, как индустрия, дошла до этого только сейчас.
Алексей Кудрявцев: Кажется, еще Алан Кэй давным-давно понял, что наследование — это плохо.
Андрей Володин : Да, но в целом, согласитесь, что еще несколько лет назад все говорили, что ООП — это круто. А сейчас есть Protocol-Oriented Programming в Swift, функциональщина, и всё придумывают что-то новое. В GameDev эти настроения появились уже довольно давно.
Алексей Кудрявцев: Сделаю ремарку: Алан Кэй тот самый человек, который придумал ООП. Он сказал, что не придумывал наследование, а только посылку сообщений, и вообще его поняли неправильно.
Отличия мобильной разработки и GameDev
Алексей Кудрявцев: Расскажи теперь про отличия: чем GameDev и мобильная разработка кардинально отличаются, и что из GameDev нам никак не применить?
Андрей Володин: Мне кажется, фундаментальное отличие в том, что продуктовая разработка максимально ленивая. Мы пытаемся писать код по принципу «пока не попросят, не встану». Пока коллбэк не сработает, мы ничего не будем делать. Даже рендеринг в продуктовой разработке ленивый: перерисовывается не весь кадр, а только те части, которые изменились.
GameDev-разработка в этом смысле беспощадна. Для каждого кадра делается всё: 30 или 60 раз в секунду перерисовывается вся сцена с нуля, каждый кадр, каждый объект обновляется, каждый кадр симулируется физика. Происходит куча всего, и это очень сильно меняет парадигму. Начинаешь жить внутри одного фрейма — у меня этому посвящена целая часть доклада. Нужно все-все-все уместить в 1/60 или 1/30 секунды. Поэтому начинаешь исхитряться, делать максимальное количество предварительных вычислений, параллелизации, пока GPU рендерит кадр, готовить на CPU следующий. Именно поэтому батарейка от игр разряжается гораздо быстрее, чем от обычных приложений.
Алексей Кудрявцев: А почему в играх нельзя все тоже сделать лениво?
Андрей Володин: Концепция игр плохо позволяет. Какие-то игры определенно могли бы выиграть от этого, например, Тетрис, в котором мало динамики и меняются только некоторые части. Но в целом игра — очень комплексная вещь. Даже когда персонаж просто стоит, он, например, покачивается — происходят какие-то анимации, есть какая-то логика, просчитывается физика. От экономии может получиться больше вреда, потому что каждый кадр настолько меняется, что переиспользовать фрагменты становится практически невозможно.
К тому же есть хардварные ограничения. Например, GPU лучше работает с типом float, а не с double, из-за этого гораздо ниже точность. Поэтому, например, если перерисовывать только часть экрана, могут возникнуть заметные артефакты. На CPU точность высокая, потому что там все рендерится в двойной точности, можно использовать красивые шрифты и аккуратные кривые, но на GPU все равно будет некоторая аппроксимация.
Совокупность этих факторов приводит к тому, что каждый кадр требует тяжелых вычислений, обновления всех объектов — фактически отрисовки с чистого листа.
Классическая разработка гораздо ближе к GameDev, чем вы думаете
Даниил Попов: Хочу обсудить провокационное утверждение из твоего будущего доклада, что «классическая разработка гораздо ближе к GameDev, чем вы думаете». Мне сразу вспомнилась серия статей о костылях в играх, которые были призваны ускорить разработку, когда поджимали сроки. По этим статьям создается впечатление, что GameDev — это костыль на костыле ради оптимизаций. В обычной разработке сейчас все помешаны на архитектуре, красивом коде. Не могу соотнести это с GameDev.
Андрей Володин: Конечно, enterprise-компании так не делают, но в инди GameDev так примерно и есть. Но конкретно этот тезис про другое. Я часто замечаю, что разработчики пользуются многими концепциями, которые используются в GameDev, но даже не понимают этого.
Например, аффинные преобразования. Немногие могут четко сказать, что это просто перемножение матриц 4*4. Чаще CGAffineTransform — это непрозрачная структура данных, в которой что-то хранится и непонятно каким образом приводит к тому, что вьюшка скейлится.
В докладе я попробую показать другую сторону того, чем мы пользуемся каждый день, но при этом, может быть, не до конца понимаем.
О пользе математики
Алексей Кудрявцев: Как мобильному разработчику прийти к этому пониманию? Как разобраться, что под капотом у рендеринга в UIKit, как устроены внутри аффинные преобразования, и не пугаться лишний раз? Я понимаю, что это матрица, но какая конкретно цифра за что отвечает, не могу сказать. Где почерпнуть информацию, чтобы не бояться и понимать?
Андрей Володин: Самый очевидный совет — начать делать pet project.
Главное, что по этому поводу стоит сказать: все концепции мобильной GPU разработки абсолютно похожи на те, что есть и на десктопе. iOS GPU программирование принципиально не отличается от того, что есть в десктопной среде. Поэтому если для iOS наблюдается недостаток материала по теме, то всегда можно почитать что-то для NVidia или AMD-решений и вдохновиться ими. Идеологически они прямо одинаковые. API немножко отличается, но обычно понятно, как переложить существующие практики с десктопного программирования на мобильное.
Алексей Кудрявцев: Когда используешь API, например, игровой движок Cocos2d или Unity, все рано ничего не понимаешь — просто дергаешь какие-то методы. Как именно начать понимать, и куда лучше посмотреть, что лучше почитать, чтобы на UIKit это можно было переложить?
Андрей Володин: Cocos2d — Open Source проект и неплохо написан. Я не очень объективен, потому что приложил к этому руку, но мне кажется, что там довольно хороший код, который можно почитать и вдохновиться. Он написан на не очень современном objective-C, но ко многим сложным местам есть подробные комментарии.
Но когда я говорю о pet project, я говорю больше не о высокоуровневых проектах типа сделать игру, а о том, чтобы написать API, который делает, например, глитч-эффект. Знаете, есть популярные API, которые делают VHS-эффект. И не на процессоре, а на GPU. Это относительно простая задача, которую можно сделать за выходные. Но это не так просто, если ни разу этого не пробовал. Когда я первый раз это делал, узнавал удивительные вещи: «Вот как работает контрастность и saturation в Instagram, или пресеты lightroom!» Оказывается, это просто шейдеры, которые перемножают 4 числа или возводят в степень — и все.
Прямо срывает башню от того, насколько это просто.
Пользуешься этим каждый день и воспринимаешь как данность — работает, но не понимаешь, как. Потом начинаешь заниматься этим сам, и становится одновременно прикольно от того, что ты делаешь что-то якобы сложное, но и забавно, что в реальности это так просто, что даже смешно.
Даниил Попов: Все равно мне кажется, что нужен какой-то математический базис. Например, в Cocos2d какие-то шейдеры — буквально 5 строчек кода, а ты сидишь и смотришь на них, как баран на ворота, и просто не понимаешь, что там написано. Наверное, так просто в язык шейдеров не погрузиться, не зная математики, базовых концепций и т.д.
Андрей Володин: Про математику согласен. Без базовых знаний линейной алгебры будет тяжело, сначала придется разобраться. Но при этом, если у вас в университете был курс линейной алгебры, и вы хотя бы примерно представляете на уровне первого курса, что такое скалярное произведение и его геометрический смысл, что такое векторное произведение и его геометрический смысл, что такое собственный вектор, нормаль, матрица, как работает матричное умножение, то будет довольно просто разобраться.
Даниил Попов: Часто студенты компьютерных специальностей ноют, что им не нужна физика и математика. Наверное, сейчас многие с трудом вспоминают, как работает перемножение матриц.
Андрей Володин: Для меня это больная тема. Я был таким же, высокомерно ныл, зачем мне функциональный анализ и тому подобное. Но у меня есть ценный жизненный опыт, когда я проходил собеседование в Apple, в команду ARKit. На собеседовании было такое огромное количество математики, что я потом благодарил себя за то, что ходил на пары. Если бы не бэкграунд, который я получил в университете, то никогда бы не ответил на эти вопросы, и никогда бы не понял, как это работает.
Сейчас, когда я сам преподаю в университете или приезжаю на день открытых дверей, всегда говорю: «Друзья, вы успеете насидеться в своих IDE, пожалуйста, ходите на линал, на матан и вообще разберитесь, что это такое. В эпоху машинного обучения вам это точно пригодится».
Даниил Попов: Самое главное — собеседование прошел?
Андрей Володин: Да, конечно, и только благодаря тому, что у меня был математический бэкграунд.
Алексей Кудрявцев: Теперь вы знаете, зачем учить матан, и куда вы можете после этого устроиться.
Андрей Володин: Например, без понимания аффинных преобразований и знания, что такое нормаль, в VR далеко не уехать. Даже когда создаешь Project Template в Xcode, там уже все перемножается, есть векторные произведения, что-то транспонируется. Если не понимать концепции, то даже простые штуки сделать не получится.
Даниил Попов: На этой моральной ноте предлагаю потихонечку заканчивать.
Напутствие
Алексей Кудрявцев: Дай какое-нибудь напутствие тем, кто хочет ближе познакомиться с GameDev и GPU.
Андрей Володин: Не всем это нужно. Это не что-то классическое, что точно пригодится на рынке труда, и просто людям, как личностям. Но мне кажется, если вы немножко забрели в тупик и не знаете, куда продолжать саморазвитие, уже прошарили все, что можно, исследовали все возможные подходы в UI: модуляризацию, ускорение запуска, runtime Objective-C — в общем, во всем уже разобрались, то это хорошее новое поле. Здесь много места для челленджей. Хотя я каждый день это делаю, мне иногда приходится открывать учебники: посидишь, вспомнишь — ага, X на Y, понял!
Если хочется бросить вызов, напрячь мозг, сделать что-то новое, то GameDev и GPU-программирование — это для вас.
Если вы тоже мечтали в детстве делать компьютерные игры, самое время начать. Приходите послушать Андрея Володина на конференции для мобильных разработчиков AppsConf 22 и 23 апреля в Москве в Инфопространстве.