Flash никогда не будет таким, как прежде

Многострадальный 2020-й подошёл к концу, и, помимо прочих событий, он запомнится нам как конец эры Flash. 1 января старый-добрый Flash выключили окончательно, в связи с чем произошел бум желтых заголовков о том, что «найден заменитель флеша лучше и безопаснее оригинала». Тема стала крайне популярной в октябре-ноябре, хотя логичнее было бы начинать дискуссии в 2012, когда компания Adobe официально высказала намерение о прекращении поддержки Flash.

Главный вопрос, который волнует всех, кто заинтересовался этой темой: возможно ли создать полный клон Flash (имеется в виду Flash-player, а не целую экосистему)?

Нет, в текущий реалиях невозможно. И на это есть несколько классов причин: экономическая целесообразность, частичная проприетарность платформы и большая разница в технологиях.

Оглавление:

Про экономику

С одной стороны, закрытие платформы вынуждает идти и искать аналоги, так как есть еще миллионы приложений и игр, которые никто и не думал переписывать с Flash-а вплоть до полного закрытия технологии. Следует уточнить, что до конца 20-го года можно было включить плеер принудительно, или использовать специальную сборку браузера, и с начала 2021 это фактически будет невозможно, как заверяет нас Adobe. В Windows уже существует граната в виде обновления которое убивает системный плеер. Если кто не знал, то в Windows плеер обновляется посредством Windows Update со времен Windows 8.

С другой стороны, многие компании просто закрыли свои проекты или переписали на чистый HTML5. Zynga, к примеру, закрывает бабушку всех ферм (ссылка на официальное подтверждение) ввиду нецелесообразности ее переноса, и это будет только плюсом, так как не нужна будет поддержка рудимента, а пользователи перейдут на ее потомков. Также давно существует тенденция ручного конвертирования Flash-проектов, но проблема у них в том, что это пригодно только для небольших роликов и игр, где есть возможность использовать текстурные атласы, собранные из шейпов, ведь вектор в таких проектах никто не переносит в его настоящем «векторном» виде. Это лишает основной фишки флеша. И другая проблема ручного переноса — необходимость иметь исходники проекта, которые(упс!) далеко не всегда доступны, иначе чеки за конвертацию с лихвой перекроют стоимость изначальной разработки проекта.

И самое дорогое и амбициозное решение, которое могут позволить себе только отчаянные, – частично или полностью реализовать Flash-player в браузере. Это не просто дорого, но и долго!

Про проприетарность

Почему до сих пор нет полноценных реализаций флеша? Потому что сама платформа проприетарная на 90%, и все по сути реализуют API по спецификации SWF и AVM2, Flash8, которая включает в себя описание структуры файла и функционала, но не описывают конкретную реализацию и математику. Также спецификация в некоторых вопросах сильно скудна и имеет ошибки. Например, поведение для Flash 5 и Flash 8 разное в математических операциях с нечисловыми операндами, и выясняется это методом научного тыка, а Flash начиная с 9 вообще может иметь как AS2 в качестве языка, так и AS3, а это совершенно разные языки с точки зрения виртуальной машины. Помимо этого, есть так называемые “VIP-инструкции”, которые были доступны только для избранных, и конечно они не входят в публичную спецификацию.

Это порождает средства обфускации, которые усложняют реверс инжиниринг в несколько раз, а также правильность выполнения инструкций. Это связано с тем, что обфускаторы формируют байткод валидный для плеера, но неверный с точки зрения языка — вы не сможете получить правильный AS3 обратно.

Конечно, есть исходники AVMPlus (ActionScript Virtual Machine), которые используются в AIR и Flash. Но это только виртуальная машина, которая еще не может быть использована в полной мере ввиду невозможности (на данный момент) JIT-компиляции, если мы говорим про «в чем сложность загнать ее в WASM?» (как делает WAFlash).

А разве возможность крутить байтики делает флеш флешем? Нет, сила флеша в Graphics API.

Вдогонку ко всему, позиция компании Adobe направлена скорее на ограничение развития замещающих технологий: не стоит ожидать дополнительного раскрытия спецификаций или реализации HTML5 версии плеера. Ведь если бы они хотели это сделать, то давно бы реализовали.

Про технологии

Flash опередил всех в свое время в технологическом смысле, так как он умудрился рисовать вектор очень быстро без аппаратного ускорения. Какое аппаратное ускорение в 1996 году? Да, Flash — это детище, которое развивалось 10 лет до 2006 года, а в 2012 решили его убить.

Flash не требует наличия GPU до тех пор, пока вы не будете использовать Stage3D, который резко ограничивает возможности самого флеша. Черным по белому написано, что в GPU не работает часть операций, например Overlay Blending Mode.

При создании аналога, к сожалению, простой подход «а давайте сделаем тривиальные компоненты, архитектуру, что там писать-то» не работает по нескольким причинам:

1. Есть фичи, которые не реализованы ни одним WebGL-рендерером. Ну уж если в самых популярных библиотеках для работы с 2D и 3D графикой, таких как PixiJS и ThreeJS чего-то нет, почему рандомный boilerplate даст результат? Нет уж, надо заново переоткрывать алгоритмы для векторной графики и цветовых наложений, на этот раз для GPU.

2. Часть фич, например фильтры и ColorTransform, взаимодействуют друг с другом неочевидным для кодеров, но очевидным для использующих редактор образом. В той же PixiJS такое сделать нельзя, вообще не предусмотрена эта синергия.

3. Много неопределенного поведения, и по коду, и по памяти, и по производительности. Представьте, что игра завязана на какое-то неочевидное поведение системы — такое постоянно встречается. Когда её писали, никто не думал что надо соответствовать документации Flash, оно работает — ну и хорошо. Такое поведение необходимо обрабатывать отдельно, создавать отдельные флаги в конфиге и иногда эвристики, которые помогут покрыть большинство случаев использования этого UB (unpredictable behaviour).

4. Вектор с антиалиасингом. Даже у векторного формата SVG есть такие проблемы, как просвечивающие фоны, тормозящие анимации и т.п., тем более мы их имеем при переносе вектора в WebGL. Например, при экспорте ассетов из векторного редактора, можем столкнуться с такими эффектами, как «испорченные стыки», чрезмерное потребление памяти, притормаживание анимаций. Стандартное кэширование тут не поможет, вы не сможете держать в памяти дополнительные буфера с MSAA, у которых один пиксель байт на 16. Нужны трюки, нужно использовать и 2D и WebGL, нужно кэшировать в рантайме в атласы образованные каким-то чудом, а то память провалится. А ещё не везде есть WebGL2 и нужный антиалиасинг на renderbuffer-ах, но на этот случай есть недокументированный трюк, который знают только несколько человек в мире (под IE не работает, конечно).

5. Сборщик мусора. У флеша не такое поведение сборщика мусора, как в браузере. И сделать такое же поведение в браузере — задача века. Да, в Flash на многих объектах есть метод dispose, но далеко не все им пользуются. Тяп-ляп и в продакшен! Разработчики забывают диспоузить BitmapData, и могут создавать их каждый фрейм. И да, флеш почистит (!), но не браузер, если вы делаете рендер в WebGL (а иначе дикие лаги), и грузите текстуры туда, то готовьтесь ломать мозг.

Про реализацию

Какой вообще минимум для реализации флэша?

  • Реализовать сцену, MovieClip. Остальное? Да кому оно нужно.
  • Graphics? Зачем заморачиваться — можно все атласы сгенерить на сервере, это же гораздо эффективней, да?
  • Выгрузить ресурсы через другой формат, изобрести свой экспортер на jsfl.

Вот на этом этапе, ну может чуток дальше, стоят OpenFL, Starling и другие решения, которые могут отобразить часть контента в известных движках и платформах.

Как правило, уже на этом этапе заявляют «мы сохраняем флэш-игры». Каждые полгода-год новый проект это заявляет. Но это всё ещё очень далеко от полноценного флэш-плеера.

Следующий уровень — нужно виртуализировать Flash окружение. И главный проект который через это продрался — это Mozilla Shumway, наследник проекта Gordon. Проект, названный фамилией инопланетянина Альфа, был экспериментальным, его признали успешным и так же успешно закрыли. По словам Дэвида Фланнагана, который в проекте не участвовал, но следил за ним извне, совместимость была достигнута на 90%, а остальные 10% были очень-очень сложными.

Следует уточнить, что Shumway делает перенос только на canvas2D, с соответствующей производительностью в 1FPS на сложных сценах, но это исправляется заменой рендерера. Главное, что достигли в проекте Shumway - огромное число тестов на совместимость и код хорошего качества. Спасибо всем тем, кто это делал аж 3 года и вечный покой проекту.

Эту основу взяли и продвигают другие проекты. Давайте рассмотрим их:

1. Транслятор + перерождённая PixiJS от CrazyPanda (TS)

Игра Запорожье (https://vk.com/zaporozhye_igra), оно же Household (https://www.facebook.com/playthehousehold/) является далеко не первым проектом с миллионом LoC, который надо переносить. Подобные проекты пробовали портировать на Haxe.

Что делает его особенным, так это гигапиксель ассетов в векторной форме. Лет 200 человеко-часов — что с этим всем делать? Можно написать аналогичный на html5, как это сделала Zynga. Проблема состоит в том, что это стоит денег и людей, это не кодерская проблема, а проектная. Оценка рисков достаточно сложная: сможет ли компания нанять и удержать 10-15 человек на 3-4 года, чтобы они это переписали, и получится ли продукт сопоставимого качества на выходе, а вдруг не сумеют?

Можно обойтись меньшим составом, но даже гениальный кодер не может 1 часом компенсировать 10 часов, которые когда-то потратил уже уволившийся программист. И с художниками то же самое — если будут проблемы в старых ресурсах, то цена их правки будет сравнима с их созданием.

Ситуация осложняется тем, что останавливать процесс разработки нельзя даже на месяц, игра живёт на натуральном трафике, отсутствие апдейтов может привести к оттоку людей. Из-за этого простые решения типа openFL не подходят — ну допустим сохранится 50% проекта, а 50% будет изменено/переписано, а чтобы не наплодить тонну багов и не обременять этим основную команду, надо 99% против 1%.

В итоге получился какой-то экстрим-R&D: команда из 3 разработчиков и 1 тестера создала отдельный билд-таргет, так чтобы html5-версия собиралась из тех же исходников и ресурсов. Что пришлось сделать:

1. Транслятор кода в TS с промежуточными человекочитаемыми стадиями. Применяется как к чистому AS3-коду, так и к декомпилированным из swf фрейм-скриптам.

2. Flash API, основанное на Shumway. Медленно заменялись части, так чтобы всё работало.

3. Форк PixiJS, раза в 2 больше обычной библиотеки, из которого в upstream ушла где-то треть Pull Request для релиза PixiJS v5. Вы используете пятую версию? Поздравляю, у вас в игре есть хаки и оптимизации из Запорожья.

4. Поле игры со Stage3D всё-таки пришлось переписать на чистый WebGL, потому что очень хочется 60 FPS на 10000 коров на участке. А у топовых игроков действительно тысячи анимированных объектов на каждой локации. Получились все 144FPS с маленькой нагрузкой на CPU, почти всё обсчитывается на видеокарте. На старых intel-ах выдерживает почётные 30FPS.

5. Для тестирования был использован AI, тестер писал сценарии, а пока все спали — бот усердно их прогонял, искал отличия в html5 и flash билдах на разных видеокартах (Intel, AMD, Nvidia) под разными браузерами (Chrome, Firefox, IE).

Результат вышел такой, что разработчики игры и художники замечают только мелкие огрехи. Только представьте, что есть один спец который из кучи FLA-шек, подброшенных художниками, собирает SWF-файлы по какой-то особенной логике — и он вообще не в курсе извращений этого билд-таргета!

Как начиналось: https://www.youtube.com/watc? v=Qynuu95oioU

Тест в процессе: https://www.youtube.com/watc? v=adixpp9CK_A

Что получилось: https://www.youtube.com/watc? v=TAtuNrh0Y8E

Минусы проекта:

1. AVM отчуждена, а назад не подключена. Игры без исходников при таком подходе перевести не получится.

2. Нужно потратить время, чтобы вывести это в open-source так, чтобы люди смогли справиться с объемом кода, для этого нужно базироваться на текущей версии PixiJS, необходимо организовать свой playground.

3. Реализовано, конечно, не всё. Если игру разрабатывал извращенец, использующий экзотику, например из BitmapData, то для нее возможно придется изменять половину фреймворка.

Достижения:

1. Прямота. Когда хочешь — можно впрямую писать на TS с модами, например поддержкой целых чисел и «obj instanceof Interface». Также можно писать шейдера сразу на GLSL и использовать некоторые WebGL-команды напрямую.

2. Совместимость с редактором. Представьте, что используют профессиональные художники во флеше: фильтры, маски, режимы наложения и их комбинации — всё это работает и покрыто тестами.

3. Текст. Multiline TextField, html, embed-шрифты - всё что надо для чатов и i18n в игре.

4. Совместимость по «flash.display». Мышиные и stage-события выдерживают весь тот кошмар, который накодили люди за 14 лет, кучу рекурсивных или странных асинхронных последствий. Нет, сама игра младше, но в базе кода есть UI-либы от 2006 года.

5. Езда на поворотах. Flash выдерживал на порядок более сложные сцены, чем современные WebGL-движки. Один неудачный фильтр DropShadow или Blur в любом WebGL-рендерере (даже в Figma) может уронить FPS на пол или показать квадратный ужас. Сотня подгруженных SWF-ок вгонит в Out-of-memory. Решение Crazy Panda не сильно проигрывает флэшу на поворотах, и не вылетает за трассу в отличие от других.

6. Сохранение нервов основной команды. Большая часть активных пользователей перешла на WebGL, меньшая скачала электрон-приложение со старым флэш-плеером, залоченное на специальный домен, и обе версии собираются из одних исходников. Сохранилась и аудитория и пайплайн. Так как система статистики осталась старой, можно сравнивать результаты, и пока они достаточно оптимистичны. В частности, у тех, кто пережил переход, средний FPS вырос в два раза.

Чтобы посмотреть исходники и прогнать это в профайлере или SpectorJS, надо лишь зарегистрироваться в одной из популярных соцсетей и открыть «Запорожье»/«The household».

Также, пока opensource-решение не выложено, можно посмотреть демку 3-летней давности, на старом canvas2d от шамвея. Кто тут любит тушёнку из котиков? http://pixijs.io/pixi-swf/demos/ninja-cat.html

2. AwayFL (TS)

Это детище одного из разработчиков Away3D, довольно известной библиотеки для флеша, которая реализует часть его API на Stage3D и была тем самым ThreeJS того времени. С затуханием платформы проект перерос в AwayJS.

AwayFL нельзя назвать полноценным флеш-плеером, так как по сути это набор библиотек, которые используют перекроенный AwayJS в качестве рендера, и перекроенный Shumway для виртуализации окружения. Использование уже существующей наработки в качестве Away3D/JS упростило старт, но сильно усложняет продвижение, так как архитектура стала неспособна выдерживать такую нагрузку, которая требуется некоторым, с виду простым, проектам, даже на топовых железках. В отличие от вышеупомянутого, это уже вполне настоящая виртуальная машина с JIT и частично оптимизирующим компиляторм байткода в JS (иначе вы никогда не приблизитесь к скорости выполнения кода флешем) (спасибо Shumway, но его все равно переписали, для быстрого старта).Активно используется для конвертации игр на портале Poki.com, хотя до полноценной реализации флеша далеко, так как много нерешенных задач:

  • Проблемы с шрифтами, много TextAPI не поддерживается или реализовано неверно
  • Мягкие маски и сложные BlendModes не реализованы
  • Часть GraphicsAPI не реализована
  • Фильтры не реализованы (в прогрессе)
  • Проблема высокого потребления памяти и CPU на больших проектах. Это как раз связано с тем, что нельзя мониторить срабатывания GC, чтобы отпускать элементы. Да, есть WeakRef и FinalizationRegistry, но также есть Safari =((
  • 5 человек в команде: 3 инженера, стажер и тестировщик. Не так много.
  • Поздно стартовали, активная разработка началась всего лишь в феврале это года
  • Спрятанный нижний уровень. Если необходимо внести правку на уровне WebGL, фикс придется проталкивать через несколько слоев абстракции.
  • AGAL для шейдеров! Так как это Away3D, то огромная шейдерная база была построена на AGAL и невозможно перевести на GLSL не переписав половины ядра в разумные сроки.

Достижения:

1. Создатели небольших флэш-игр любили задействовать полный функционал BitmapData, иногда чрезмерно. Есть поддержка кучи извратов с ней и переключение разных режимов, если обнаружена цепочка тормозящих вызовов.

2. JIT с поддержкой наследования внешних библиотек (external lib). Можно миксовать классы игры (AS), так и нативные (JS), например для того, чтобы игры с физическим движком Nape запускались с достойным FPS. Для сравнения можете запустить игру с Nape в WAflash.

Актуальную версию можно потыкать тут: https://exponenta.games/games/AFL/

Некоторые игры, которые опубликованы на AwayFL:

3. SWF2JS (JS)

https://swf2js.com/en/, там же есть плейграунд.

Малоизвестная реализация от корейских ребят. Как они хвастаются, полноценная реализация AS3 API. Но в флеше есть сильный подвох — много не документировано, и только метод научного тыка спасает.

Сколько мы ни пытались на нем запустить что-то сложнее, чем «масяня»( и то с артефактами), не получилось. Не работают ни AS2, ни AS3 игры: либо часть не отрисовывается, либо падают с ошибкой. Скорость выполнения скриптов тоже оставляет желать лучшего.

По цифрам на сайте, активно обновляется, на момент написания последнее обновление было от 24.10.2020 (т.е. в октябре).

Усложняет жизнь проприетарность и отсутствие публичного списка проектов на нем. Не совсем очевидно, для чего и для кого разрабатывается проект. Отличительной чертой проекта является поддержка видео (не flv1), только зачем?

Не будем все перечислять, попробуйте сами.

4. Ruffle (WASM, Rust)

Самая модная и самая хайповая реализация. Rust — сила! Но язык реализации одновременно усложняет разработку и найм заинтересованных в команду. Кто писал на Rust, тот знает боль и страдания первых 2-3 месяцев работы.

Что касается функционала эмулятора, то на данный момент поддерживается только AS2, но без учета версии флеша, что приводит к неочевидным проблемам.

Ожидается, что это будет самой производительной и полноценной версией плеера, но только года через 2, и если они научатся JIT-компиляции в WASM(что пока никто не смог реализовать). Без JIT-а и жизнь не та.

5. WAFlash(WASM, С/C++)

Реализация от еще одного автора из Кореи. Как пишет сам автор — это просто оригинальный AVMPlus + crossbridge + некоторый рендер-движок, собранный в WASM.

Проект показывает самую быструю загрузку, но довольно низкую производительность. Ничего удивительно в этом нет, так как оригинальный AVMPlus имеет JIT-компилятор, который не может быть реализован на текущий момент для WASM.

Другая проблема — софтверная растеризация и неполное покрытие API, что приводит к просадкам и странным гличам, которых сложно ожидать от фактически «оригинальной» реализации. Многие игры просто падают с ошибкой памяти, многие жарят CPU.

6. CheerpX (WASM, C/C++)

Это не эмулятор флеша, это эмулятор x86 окружение, что требуется для запуска Flash-плеера в WAASM. Самый амбициозный проект, так как если можно запустить флеш, то можно запустить и что-то другое. Полностью проприетарное решение, нет даже тестовой странички. И самое медленное решение.

Демонстрация работы с саммита по WASM:

7. IceStone

Подробнее о проекте можно прочитать в статье.

Отказались от использования Shumway, т.к. он показывал очень низкий FPS, о замене рендеринга не задумывались. Как описывают авторы проекта, основным направлением работы стал паблишинг, конвертация игр может осуществляться частично в ручном режиме.

Будет ли опенсорс-версия, неизвестно.

Вывод

На текущий момент реализация полноценного конвертора/раннера в HTML5, который не потребует участия человека для 100% игр, представляется трудно осуществимой. В то же время реализация JIT-компилятора в WASM тоже под вопросом. Таким образом, нас ожидает либо увлекательное переизобретение эффективных алгоритмов рендера Flash без самого флеша, либо полное забвение вектора, на данный момент второе — наиболее вероятное развитие событий.

Если желаете проверить как работает ваша любимая «масяня» на вышеперечисленных реализация, то можете попробовать загрузить в плейграунды и посмотреть:

PS:

Спасибо Попелышеву Ивану и Борисовой Елизавете за соавторство.

104104
31 комментарий

Статья интересная, но текст будто из машинного перевода, очень сложно читать

12
Ответить

Просто очень много специальной терминологии. Если с этим работал - всё понятно. Если нет - то да, тяжело должно быть.

13
Ответить

Спасибо!.
Да, статья писалась в спешке и была уже сильно адаптированная, но нету возможности разжевывать все - куда еще больше текста?

Ну да, есть большой и жирный минус - такое сложно подавать.
Возможно она будет немного упрощаться - мне не часто приходится писать на общую публику.

4
Ответить

Хех да это весело ))) Был у меня проект в работе - 18 игр головоломок написанных на флеше. Все ресы в swf с каким то кодом. Приличная код база и много математики. В итоге в одно лицо было все портировано на openfl где то за полгода, под конец подключился еще человек. Результат был ужасен! ))) На мелких пазлах более менее было, а на больших сильно проседал фпс и память просто росла как на дрожжах. В играх юзался cacheAsBitmap - это была боль ) OpenFL не справлялся со всем набором фич. Билд игры выходил где то 1mb сжатого js кода. Еще прикол в том что на одном экране могло отображаться десятки вьюверов игры - а это анрил для webgl - потеря контекста. В итоге нами было принято волевое решение написать свою реализацию flash graphics api (flash.display) на haxe. Ну и после заменить openfl. Писал я один где то полтора месяца. Конечно я учитывал только те фичи которые нужны, а это color transform, cacheAsBitmap, mask, scrollRect, dirty zones и text. И самое главное что бы все было на canvas! В итоге получился супер легкий двиг! С поддержкой выше перечисленного. Размер билда 300кб, память вообще не растет, cacheAsBitmap реализован только для поля игры где могут располагаться десятки тысяч элементов. Color transform реализовано на уровне трансформации цвета, а не попиксельно. Ну и самое главное стабильная производительность! Одно из самых сложных это было коррекция текста - что бы он на разных браузерах был одинаково. И pixel snapping - это боль ) Поиграть можно здесь https://www.conceptispuzzles.com.

До этого я делал свой конвертер ) но как то не очень он популярен https://github.com/AntonovSergey2211/guepard

15
Ответить

спасибо за статью, топчик

5
Ответить

Комментарий недоступен

3
Ответить

Спасибо за статью. Очень круто - как бывший флешер говорю.

PS: Кстати, забавно, что де-факто все стали использовать вектор в Unity. Хотя, конечно, он совершенно другой и опирается на видеокарту.

2
Ответить