Publish @Wraith: Ужасы социального 3D

Иногда случается эпическое, нельзя не запаблишить. @Max Volkov(Wraith) рассказывает про свой опыт хераченья 3d на флеше в Zynga East – они сделали на нем Cityville 2 и он полон впечатлений.

В качестве бонуса внизу @Ed Ryzhov про то, как он писал на флеше вот такой софтверный рендер – http://www.gamedev.ru/projects/forum/?id=154409.

Enjoy!

People in this conversation:
Simon Kozlov Add
Engineer at ROBLOX (www.roblox.com), previously at Microsoft. And ClosedCircles was my idea.
Boris Batkin Add
i press buttons for living. u can call me god
Arseny Kapoulkine (Zeux) Add
CREAT Studios, Saber3D, Sperasoft (EA Sports – FIFA). Currently making kids happy at ROBLOX. http://zeuxcg.org/
Sergei Miloikov (Z) Add
Senior programmer at Crytek Black Sea
Tropico 3-4
Graphics + engine + tools
Max Volkov (Wraith) Add
Старший хуятор @ Zynga East (Baltimore, MD). Несу возмездие во имя тридэ в широкие массы пользователей сосалок.
Kirill Kyalundzyuga (_ShaMan_) Add
R&D Programmer at Wizart Animation
Pavel Nakaznenko (Crio) Add
Programmer (Xbox 360, PS3, PC)
at Darkside Games Studio
http://dtf.ru/person/cv.php?id=835
Михаил Окунев (mehas) Add
Microsoftie (bing, ML)
ex-gamedev (physical engine for alternativaplatform.com), ex-intern in Google and MS, ex-ACMer.
Баженов Кирилл Add
Программист в Ubisoft Kiev
Roman Galashov (RomBinDaHouse) Add
Il-2 Sturmovik: Birds of Prey (Art Director);
Skyforge (TA)
Yury Degtyarev Add
Graphics programmer at Transas New Technologies
Alexey Patsiorkovskiy (tool) Add
Ed Ryzhov Add

Simon Kozlov: @Wraith О, Макс!
Расскажи как там на east coast
В вашем загнивающем фермерском капитализме

Wraith: Ну чо, зошыпили ситивиль 2
экспириенс, конечно, необычный
со флышом и 3д-графикой

RomBinDaHouse: сделали свой 3д-фреймворк внутри флеша?

Wraith: Да тут пока без меня чуваки лицензировали flare3d
а мне его пришлось перехуярить почти полностью
целиком, начиная с нового ассет-пайплайна
я щас риалтайм-тени доделываю

RomBinDaHouse: священный грааль,теперь банановый – на флеше

Wraith: на флыше 3д делать – это тот еще пиздец
все тормозит, пару матриц не умножишь лишний раз

RomBinDaHouse: да мы как-то ковырялись с Imp5, жесть конечно)

Wraith: у меня до сих пор такое ощущение, что я целый год играл в игру по написанию игры
все какое-то игрушечное

Wraith: я за год составлял списог компаний, которые проекту жить мешают
туда попали почти все
включая гуголь

Z: расскажи!
Что там такого?

Wraith: где?
во флыше?

Z: ну да
какой движок юзал, я пропустил
смотрел недавно какие-то сравнения stage3D vs WebGL, stage3D везде въигръвал, где-то – сериозно
чисто по перформансу

Wraith: Ну короче…
Stage3D – это просто компонент рантайма флыша
который выглядит как сильно упрощенный IDirect3DDevice9
http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/flash/display3D/Context3D.html – как-то так
вершинки/текстурки/шойдыры/треугольнички
шойдыры выглядят так: http://help.adobe.com/en_US/as3/dev/WSd6a006f2eb1dc31e-310b95831324724ec56-8000.html
ну так вот
на этом деле завелось (от сырости) много всяких движков
от посторонних людей

Crio: есть еще glsl2agal чтоб не париться

Wraith: да во flare3d свой компилятор шейдеров
ну и мне пришлось написать свой собственный
ибо хотелось uber-shaders

Z: да я ето знаю
сам писал
мне про впечетления от… я писал только демки

Wraith: ну оно примерно и годится чтобы писать демки
я всю разработку выбирал самые тупые решения в духе начала 2000х годов
флеш сам по себе сука тормозное говно
оверхед виртуальной машины чудовищный
на каждый чих аллокации

Wraith: каждый раз, когда ты умножаешь вектор на матрицу, оно аллоцирует новый вектор в хипе

Zeux: vector.assignTransformedBy(matrix) не придумали?

Wraith: не придумали!

Zeux: тяжело вам там!
у нас в луа конечно не сильно отличается, но мы на ней хоть рендер не пишем.

Wraith: ну чо, я когда выкатил код в транк, у кодревьювера чуть сердечный приступ не случился
ибо хули делать, я сделал везде все данные паблик
акцессоры нахуй
а они (коллеги) ж с джавы пришли…
с аллокациями бороться я научился очень просто
делаю все временные переменные метода статиками класса
ведь есть много других вещей, которые можно реюзать

Zeux: Типа мутируем существующий объект вместо создания нового?

Wraith: матрицы, ббоксы, ага!

Zeux: Я считаю нужен класс RegisterPool, и там значит Matrix m0, итп

Zeux: Но т.е. assignTransformedBy все-таки есть? :) Ну т.е. можно in-place заменять на помноженное.

Wraith: ну не везде
для вектора нет
для матрицы есть

Zeux: Флоаты-то хоть не боксятся?

Wraith: флоаты боксятся, как это
ну т.е. они value type

Zeux: omg, и тоже статики типа?

Wraith: а не reference type

Zeux: А, не, это ок.
Просто в некоторых языках типа Ocaml
флоат на стеке это аллокация

Wraith: ага, это было бы слишком жыстоко!
значит теперь, чо внутри stage3d
там есть какой-то внутренний хендлинг стейтов
судя по пиксу он вроде бы работает
но некоторые моменты меня просто убивают
например, адобе почему-то не пошли путем создания vertex declaration object

Wraith: а пошли путем glAttribPointer()
в итоге есть две проблемы

Wraith: после рендера мешиков с одним и тем же форматом вершины надо чистить текущий vertex buffer binding
во-вторых, внутри флеш тупо пересоздает vertex declaration object
прямо посреди кадра

Z: что такое “на стеке” во флеше? по моему скромному опъту там такого нет

Wraith: Зеукс имел в виду “локальные переменные типа Number”

Wraith: вообще меня жутко бесит анальная рентентивность stage3d по поводу текущего стейта
если текстура не используется в шейдере, но назначена в семплер – все блять, ошибка
если элемент вершины прибинден, но не используется в шейдере – все блять, ошибка
flare3d внутри ужасен
в нем сценеграф === трансформ граф
объекты рендерятся как-то по обходу трансформ-графа
и сортируются только в user-defined слои
по стейтам не сортируются…
пришлось ото всего этого отказаться
написать свою систему материалов
где каждый материал сам рулит всей отрисовкой батчей, которые ему дают
в результате вышла такая “матрица рендера”: для каждого user-defined слоя, для каждого материала, нарисовать все отданные ему в этот слой батчи
реально использовалось 3-4 слоя из 12, остальные очень специфичные типа “тут террейн” и “тут облака”
каждый материал – это фактически руками написанный “прешейдер” + drawTriangles(). Он сам сортирует свои батчи, и минимизирует переключения стейтов

Z: а по геометрии там как? и по DIP-ам?
DIP подозреваю тормозит, нет?

Wraith: да не шибко
т.е. в пределах 1000 дипов жить как-то можно
у нас 1000 бывает, да
30к полигонов в сцене
но 30к сделано умышленно
и вот почему
сука адобе хитрые
они заблеклистили говноинтелы
на говноинтелах Stage3D переключается в software rasterizer
в качестве растерайзера там угадайте кто!
свифтшейдер
тот самый, который давным-давно был swShader
в опенсорсе
и у ентого растерайзера есть проблема
очень дорогой triangle setup
причем он дорогой даже в том случае когда его быть не должно!
например, когда треугольник полностью за пределами экрана
ну так вот, по моим тестам на старых хуевых компах
с говноинтелами
выходило, что 20к трианглов с тривиальными шейдерами на 20 фпс – это предел растерайзера
короче, чо, сделали в движке “облегченный режим”
это глобальный флаг “пиздец”
по которому используются совсем уже простые шейдера
из сети выкачивается особый текстурный пак для модельки
с лайтмапами, запеченными прямо в дифуз
хуле, хоть так
пиксельные шейдера по 5 инструкций
хуевато, но как-то живет на стартовых картах
большой город все равно превращается в большой тормоз

Yury Degtyarev: а как в alternativaplatform с этим борятся?

Wraith: а, с софтваре
я думаю, они забили

Wraith: т.е. я смотрел код альтернативы
по мне, так он еще хуже, чем flare3d
та же ущербная концепция трансформ-графа
и вроде не было там ничего про софтваре
вот прям сейчас я допилил тени
соответственно, дипы выросли в 2 раза
и шейдеры усложнились сильно, у меня там пока poisson disk на 12 точек
и уже quadro fx 1800 немножко притормаживает
ну т.е. карта довольно-таки галимая, на ней даже игра Capsized тормозит
но все же
1000 дипов 20 фпс
60к поликов

Zeux: Какой ваш самый-самый мин спек? Т.е. самый low end из того на чем вообще запустится

Wraith: ну что-то порядка селерона 2200

Wraith: с говноинтелом

Zeux: Или типа засчет swshader вы умеете запуститься везде?

Wraith: примерно так
кроме того
этим летом адобе немного отпустило
и они запилили “Constrained mode” для Stage3D
это вот что: шейдера транслируются в vs_2_0/ps_2_0
и рендер идет прямо во фреймбуфер, минуя композитинг флыша
т.е. 2д-элементы он прямо на фреймбуфер прихуяривает
64 инструкции, 1 временный регистр (из 8 доступных) они резервируют под свои нужды
поскольку у нас шейдера были простые даже в хардваре моде
то на constrained все это запустилось почти сполпинка
пришлось правда шейдера чуть подхачть

Wraith: вообще, конечно, странно
мне process explorer показывает GPU Utilization 50%
ну оно и понятно, по профайлеру игра 85% проводит в моем 3д-движке
там, чота 40% обработка объектов (визибилити, трансформы, анимации) и сабмит батчей, и 45% система материалов рендерит батчи
короче, все очень печально
и прежде чем упереться в дипы, вы упретесь в тупку флеша

Z: А что за проект, посмотреть можно где-то?
Как спекали по арту – DIPS, геометрия, размеръ текстур?

Wraith: конечно, http://apps.facebook.com/cityville-two/

Z: ааа, Адъ…
ето я наверное в такое потому что никогда не играл, но ето не для меня… :)

Wraith: Да я тоже в сосалки не играю, даже в нашу.

Z: да не, там контроль, постоянно отбирает его
камеру не подвигаеш
постоянно что-то где-то кликать надо…
и хочет спамить в fb чтото

Wraith: ну это First-Time User Experience
туториал т.е.
потом отпускает

Z: я понимаю, я просто с Тропикой сравниваю…

Wraith: но лично меня бесит другое – постоянно попапы выскакивают

Z: ну да

Z: накапало на мозг, я прибил

Wraith: купи ключи, добавь френдов

Wraith: лично меня такое жутко бесит

Wraith: спекали – “на глазок”. До 1000 дипов и до 20к полигонов в кадре
и проебали, конечно
текстуры – 128 для маленьких, 256 для средних и 512 для больших домиков
текстуры у нас всякие, там целый арт-пайплайн
про генерацию лайтмапов, эмбиент оклюжена, иконок
на домик приходится 5 текстур: диффуз, 2 лайтмапы для разных положений домика (левая/правая), AO и emissive, которые видны ночью
из них запекаются 2 текстурных пака:
первый для номального рендера, туда попадают 4 текстуры, при это AO впекается в альфу лайтмапов
второй – для софтверного, там все впекается прямо в диффуз, кроме emissive
и текстуры ограничиваются по размеру 256
т.е. большие автоматически даунсемплятся
каждая моделька имеет свой текстурный пак
вообще, забавно
все тулзы я тоже руками писал на С++
вышло что я на С++ кода написал в 2 раза больше, чем на флыше
это как в той серии ералаша про батарейки от часов
в рантайме мааленькая хуевинка на 5 килобайт, а powered by ебического размера тулза

Wraith: ах да, про constrained mode не договорил
поскольку с constrained некоторые говноинтелы вышли из блеклиста
на них игра примерно работает в хардваре
однако пиздец подкрался как всегда оттуда, откуда не ждали
гугель выпустил обновление хрома
и в этом обновлении гугель обновил PepperFlash
PepperFlash – это очень странное жывотное
например, оно рендерит оглем
даже на подвенде
и вот с этим жывотным, говноинтелы в constrained mode не проваливаются
и тормозят
больше того, даже если нарочно пофорсить constrained mode
оно точно так же тормозит
причину этого я так и не нашел
я почему-то подозреваю, что это все из-за многопроцессности хрома
и что он гонит картинку, по шине назад в процесс, передает ее в другой процесс, а потом ее гонит обратно
или типа того
впрочем, это беспочвенные подозрения, потому что с другой стороны, если в хроме переключить флешплеер на обычный, скачанный с адобе
то все нормально

Wraith: потому что из-за бага в трансляторе agal->vs_2_0 (баг с интерпретацией свизлов), некоторые шейдера уходили за 64 инструкции

Wraith: весь код лоулевел-рендера буквально напичкан private var tmpmat : Matrix3D = new Matrix3D;

mehas: Даааа, мы как-то долго искали утечку памяти в физической демке. Потом оказалось, что память течет из какой-то матричной операции.

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

Баженов Кирилл: tex 0×28 texture sample (fragment shader only)
эт чо, в вертексном низзя текстуру семплить?

Wraith: низзя!

Баженов Кирилл: гавно! надо чтобы было можно! :)

Wraith: дык я б тоже только за, но адобе… буэ!

Wraith: у них шойдыр транслируется в vs_2_x на подвенде


Бонусная часть!

Ed Ryzhov: ну так надо свои матрицы юзать, и вектора, и вообще, все остальное тоже
встроенное вообще все, кроме битмап – нафиг
а так, можно и софтварные 3д рендеры писать с нормальным фпсом. я же пишу :)

Wraith: Ээээ…
Вообще-то оно на флыше
а не на С++

Ed Ryzhov: минутка саморекламы http://www.gamedev.ru/projects/forum/?id=154409 :)
на голом флэше

wizzard0: Вах, олдскульное триде. Не тормозит. И стилистика под pixel doubling очень удачно подобрана.

Alexey Frunze: 12500/15200 чего?

Ed Ryzhov: долларов

tool: Круто! А вторая часть когда? :)

Ed Ryzhov: когда stage3d попрет в массы, тогда будет

tool: Не пугают отчисления Адоби? Или как-то обошли их?

Ed Ryzhov: если использовать только stage3d или только алхимию – тогда платить не нужно

Z: Круто, респект!
Расскажи, почему аж все свое и что конкретно из низкоуровневого?
И пробовал ли из C++ скомпилировать?
Оно кстати запустилось аж в 3-его раза в Chrome, но ето явно потому что у меня под флеш объчно по пол-гига памяти уходит

Ed Ryzhov: ну, в общем, идея следующая. во времена 10 флэшплеера адоби выпустили пререлиз alchemy (нынешний flascc).
на уровне VM вся эта штука поддерживается в виду нескольких специальных пар опкодов в стиле getInt(adr)/setInt(adr, val) и, соответственно, парой функций, которые умеют выделять кусок памяти, с которой данные опкоды работают
смысл в том, что эти опкоды – самый быстрый способ работать с памятью во флэше. писать можно только примитивные типы – инты, флоаты, даблы. ссылку на объект записать нельзя. ну и хрен с ним
стандартный as3 про все это ничего не знает. подразумевалось, что все будут работать через с++, но толковые пацаны быстро все расковыряли и добавили обходных путей. можно постпроцессить флэшку тулами, можно писать на haxe, который поддерживает все это сразу
ну вот поскольку as3 ничего про эту память не знает, пришлось писать все свое. хранить вертексы и все прочее в этой самой быстрой памяти
почему не взял существующие движки? потому что в существующих (до сих пор) нет z-буфера, освещения и мультитекстурирования. ну короче ничего нет.
опять же народ как-то небрежно относится к памяти. и увидеть где-нибудь во внутреннем цикле выделение памяти на каждой итерации нифига не сюрприз, а суровая реальность. за счет этого GC рвет башню и он начинает тормозить всю систему. поэтому чтобы соблюдать “никаких new в главном цикле” – надо делать свое
внутренние лоулевельные оптимизации сводятся, в основном, к раскрутке циклов, массовым инлайнам и избавлению от джампов.
на с++ пробовал компилить, но как-то мне не сильно понравилось. тупо неудобно, и особого буста принципиально получить невозможно.
кстати, что интересно, большинство одскульных триков с интами работают отлично и дают хороший буст, за счет избавления от джампов.
а вот почему у тебя запустилось с третьего раза – хз, должно работать как часы.

sergey miryanov: Спасибо за объяснение

Z: Круто, значит Alchemy не дает такой уж буст, да? А то говорили вроде по идее оно должно генерить байткод, которъй оптимальнее сгенеренного из as3 – или таки не намного?
Haxe даже незнаю что ето такое есще
Я писал лиш 2D и немного 3D, в FD :)
работал с матеръми флеш кодерами, которъе про CG знали только что он где-то там есть :)
про память не знали НИЧЕГО…
Vector вообще для них бъл что-то екзотическое и новое
еще хорошо что я писал с нуля, но потом когда один ихний деец стал помогать… я с университета такой код не видел

Ed Ryzhov: флэш тормозной не только из-за хренового компилятора. он просто сам по себе тормозной.
критические места в любом случае придется проходить дизассемблером, а на некритические можно забить. нет смысла генерить оптимальный байткод для метода, который вызывается пару раз в секунду
ну на данный момент мое мнение такое. если увижу какой-то проект, который прям в разы ускорился, тогда буду разбираться. но алхимии уже больше года, а пока ничего такого нет

Simon Kozlov: Я так понимаю, смысл Alchemy еще и в том, чтобы портировать большие C++ кодобазы
См Unreal Engine

Ed Ryzhov: да, тут согласен

Ed Ryzhov: haxe это язык со своими плюшками, который умеет компилироваться во флэш, среди прочего

Crio: Генерит оптимальнее за счет оптимизации на уровне llvm

Z: Я ето слъшал, интересно на практике посмотреть
Bullet вон на флеше видел, шустро так
в Away3D прикрутили

Crio: дак скачай да глянь, он же в паблике уже. Или что то конкретное интересует?
там портированого всякого идет в примерах
Box2d, bullet, lua, Quake1, libqrencode
на этапе компиляции причем можно указать чтобы интермедиэт файлы сохранял и прям глянуть разницу на low level

Wraith: ичо?

Ed Ryzhov: ну я к тому, что флэш не обязательно тормозное говно. надо просто знать особенности

Wraith: В моей стране эльфов другой флеш
он тормозной
и говно

Crio: Земляк!!!!

Boris Batkin: интересно, флеш с LLVM бэкендом взлетел-бы?

Crio: всмысле? Вот сейчас flascc это все дело так и собирает во флеш. Или ты наоборот имеешь ввиду?

Boris Batkin: наоборот. флеш плеер чтобы делал jit на llvm
почему-то думаю будет в разы быстрее, чем

Crio: такое наверное есть. Надо глянуть как деплой на мобилки идет

Boris Batkin: http://sourceforge.net/apps/trac/lightspark – кажись уже есть

Crio: http://llvm.org/devmtg/2009-10/Petersen_OptimizingActionScriptBytecode.pdf
вот еще
и вот пацаны вроде запилили уже http://news.ycombinator.com/item?id=862485
У всяких скейлформов всяко есть

Boris Batkin: значит вообще в морг
покамест нормально variant отрабатывать умеет только V8 похоже

Boris Batkin: http://blip.tv/jsconf/jsconf2012-vyacheslav-egorov-6141593 – очень нравится

Simon Kozlov: Но сам флеш это не делает, ага?

Wraith: сам не делает
Больше того, я лично не совсем убежден, что JIT-компиляция сильно поможет языку с динамической типизацией
ну т.е. когда любое обращение к полю класса – это лукап по хештаблице и какое-то подобие dynamic_cast – тут вообще сложно что-то соптимизировать
хотя вот говорят, что luajit сильно помогает уменьшить оверхед виртуальной машины

Boris Batkin: повторюсь. http://blip.tv/jsconf/jsconf2012-vyacheslav-egorov-6141593 – погляди

Zeux: Это решается в Chrome и прочих JS JITs через фиксирование формы типа
И inline caching, в итоге код получается “если тип объекта это структура из трех интов то поставить там второй инт, иначе запустить медленный и печальный код”
Очень много мороки, т.к. надо уметь polymorphic/monomorphic call sites (один тип объекта в одной инструкции или бывают разные), инвалидировать всякое при изменениях прототипов итп
Но нечеловеческими усилиями получается всего лишь на порядок медленнее C++ на хорошем JS коде.

Simon Kozlov: А проверка раз на функцию?

Zeux: Проверка раз на использование, плюс если повезет то проверки мержатся.
Это очевидно не всегда возможно, i.e.
self.foo = bar()
self.foo = 4;
bar() мог сделать все что угодно.

Simon Kozlov: Да, понятно что после модификации надо тоже проверить
Вопрос был, скорее, можно ли реже, чем в каждой функции

Zeux: Там есть альтернативный development про type tracing
Я не помню в каком из JS JITs оно, но в таком виде можно реже.
Условно ты компилируешь версию функции
для конкретных типов аргументов
И при вызове проверяешь, можно ли вызвать правильную специализацию.